aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig15
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/alienware-wmi.c286
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c9
-rw-r--r--drivers/platform/x86/dell-laptop.c370
-rw-r--r--drivers/platform/x86/dell-rbtn.c15
-rw-r--r--drivers/platform/x86/dell-smbios.c193
-rw-r--r--drivers/platform/x86/dell-smbios.h46
-rw-r--r--drivers/platform/x86/dell-wmi.c238
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c8
-rw-r--r--drivers/platform/x86/hp-wmi.c46
-rw-r--r--drivers/platform/x86/ideapad-laptop.c14
-rw-r--r--drivers/platform/x86/intel-hid.c3
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c8
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c35
-rw-r--r--drivers/platform/x86/intel_telemetry_pltdrv.c13
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c4
-rw-r--r--drivers/platform/x86/toshiba_acpi.c115
18 files changed, 995 insertions, 424 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 69f93a576e45..ed2004be13cf 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -91,10 +91,21 @@ config ASUS_LAPTOP
If you have an ACPI-compatible ASUS laptop, say Y or M here.
+config DELL_SMBIOS
+ tristate "Dell SMBIOS Support"
+ depends on DCDBAS
+ default n
+ ---help---
+ This module provides common functions for kernel modules using
+ Dell SMBIOS.
+
+ If you have a Dell laptop, say Y or M here.
+
config DELL_LAPTOP
tristate "Dell Laptop Extras"
depends on X86
- depends on DCDBAS
+ depends on DELL_SMBIOS
+ depends on DMI
depends on BACKLIGHT_CLASS_DEVICE
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
@@ -110,8 +121,10 @@ config DELL_LAPTOP
config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
+ depends on DMI
depends on INPUT
depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on DELL_SMBIOS
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 40574e7390f3..448443c3baba 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
+obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
index 1e1e59423889..005629447b0c 100644
--- a/drivers/platform/x86/alienware-wmi.c
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -33,6 +33,9 @@
#define WMAX_METHOD_BRIGHTNESS 0x3
#define WMAX_METHOD_ZONE_CONTROL 0x4
#define WMAX_METHOD_HDMI_CABLE 0x5
+#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
+#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
+#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -60,6 +63,8 @@ enum WMAX_CONTROL_STATES {
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
+ u8 amplifier;
+ u8 deepslp;
};
static struct quirk_entry *quirks;
@@ -67,16 +72,43 @@ static struct quirk_entry *quirks;
static struct quirk_entry quirk_unknown = {
.num_zones = 2,
.hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
};
-static struct quirk_entry quirk_x51_family = {
+static struct quirk_entry quirk_x51_r1_r2 = {
.num_zones = 3,
- .hdmi_mux = 0.
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+};
+
+static struct quirk_entry quirk_x51_r3 = {
+ .num_zones = 4,
+ .hdmi_mux = 0,
+ .amplifier = 1,
+ .deepslp = 0,
};
static struct quirk_entry quirk_asm100 = {
.num_zones = 2,
.hdmi_mux = 1,
+ .amplifier = 0,
+ .deepslp = 0,
+};
+
+static struct quirk_entry quirk_asm200 = {
+ .num_zones = 2,
+ .hdmi_mux = 1,
+ .amplifier = 0,
+ .deepslp = 1,
+};
+
+static struct quirk_entry quirk_asm201 = {
+ .num_zones = 2,
+ .hdmi_mux = 1,
+ .amplifier = 1,
+ .deepslp = 1,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -88,12 +120,12 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
static const struct dmi_system_id alienware_quirks[] __initconst = {
{
.callback = dmi_matched,
- .ident = "Alienware X51 R1",
+ .ident = "Alienware X51 R3",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
},
- .driver_data = &quirk_x51_family,
+ .driver_data = &quirk_x51_r3,
},
{
.callback = dmi_matched,
@@ -102,17 +134,44 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
},
- .driver_data = &quirk_x51_family,
+ .driver_data = &quirk_x51_r1_r2,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware ASM100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+ },
+ .driver_data = &quirk_asm100,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware ASM200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+ },
+ .driver_data = &quirk_asm200,
},
{
- .callback = dmi_matched,
- .ident = "Alienware ASM100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
- },
- .driver_data = &quirk_asm100,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM201",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+ },
+ .driver_data = &quirk_asm201,
+ },
{}
};
@@ -133,7 +192,7 @@ struct wmax_brightness_args {
u32 percentage;
};
-struct hdmi_args {
+struct wmax_basic_args {
u8 arg;
};
@@ -170,7 +229,7 @@ static u8 global_brightness;
/*
* Helpers used for zone control
-*/
+ */
static int parse_rgb(const char *buf, struct platform_zone *zone)
{
long unsigned int rgb;
@@ -210,7 +269,7 @@ static struct platform_zone *match_zone(struct device_attribute *attr)
/*
* Individual RGB zone control
-*/
+ */
static int alienware_update_led(struct platform_zone *zone)
{
int method_id;
@@ -218,16 +277,16 @@ static int alienware_update_led(struct platform_zone *zone)
char *guid;
struct acpi_buffer input;
struct legacy_led_args legacy_args;
- struct wmax_led_args wmax_args;
+ struct wmax_led_args wmax_basic_args;
if (interface == WMAX) {
- wmax_args.led_mask = 1 << zone->location;
- wmax_args.colors = zone->colors;
- wmax_args.state = lighting_control_state;
+ wmax_basic_args.led_mask = 1 << zone->location;
+ wmax_basic_args.colors = zone->colors;
+ wmax_basic_args.state = lighting_control_state;
guid = WMAX_CONTROL_GUID;
method_id = WMAX_METHOD_ZONE_CONTROL;
- input.length = (acpi_size) sizeof(wmax_args);
- input.pointer = &wmax_args;
+ input.length = (acpi_size) sizeof(wmax_basic_args);
+ input.pointer = &wmax_basic_args;
} else {
legacy_args.colors = zone->colors;
legacy_args.brightness = global_brightness;
@@ -283,7 +342,7 @@ static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
/*
* LED Brightness (Global)
-*/
+ */
static int wmax_brightness(int brightness)
{
acpi_status status;
@@ -327,7 +386,7 @@ static struct led_classdev global_led = {
/*
* Lighting control state device attribute (Global)
-*/
+ */
static ssize_t show_control_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -435,11 +494,7 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-/*
- The HDMI mux sysfs node indicates the status of the HDMI input mux.
- It can toggle between standard system GPU output and HDMI input.
-*/
-static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
+static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
u32 command, int *out_data)
{
acpi_status status;
@@ -467,16 +522,20 @@ static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
}
+/*
+ * The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ * It can toggle between standard system GPU output and HDMI input.
+ */
static ssize_t show_hdmi_cable(struct device *dev,
struct device_attribute *attr, char *buf)
{
acpi_status status;
u32 out_data;
- struct hdmi_args in_args = {
+ struct wmax_basic_args in_args = {
.arg = 0,
};
status =
- alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE,
+ alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
(u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
@@ -495,11 +554,11 @@ static ssize_t show_hdmi_source(struct device *dev,
{
acpi_status status;
u32 out_data;
- struct hdmi_args in_args = {
+ struct wmax_basic_args in_args = {
.arg = 0,
};
status =
- alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS,
+ alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
(u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
@@ -519,7 +578,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
const char *buf, size_t count)
{
acpi_status status;
- struct hdmi_args args;
+ struct wmax_basic_args args;
if (strcmp(buf, "gpu\n") == 0)
args.arg = 1;
else if (strcmp(buf, "input\n") == 0)
@@ -528,7 +587,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -563,11 +622,144 @@ static int create_hdmi(struct platform_device *dev)
ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
if (ret)
- goto error_create_hdmi;
- return 0;
+ remove_hdmi(dev);
+ return ret;
+}
-error_create_hdmi:
- remove_hdmi(dev);
+/*
+ * Alienware GFX amplifier support
+ * - Currently supports reading cable status
+ * - Leaving expansion room to possibly support dock/undock events later
+ */
+static ssize_t show_amplifier_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ status =
+ alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
+ (u32 *) &out_data);
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return scnprintf(buf, PAGE_SIZE,
+ "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "unconnected [connected] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
+ return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
+
+static struct attribute *amplifier_attrs[] = {
+ &dev_attr_status.attr,
+ NULL,
+};
+
+static struct attribute_group amplifier_attribute_group = {
+ .name = "amplifier",
+ .attrs = amplifier_attrs,
+};
+
+static void remove_amplifier(struct platform_device *dev)
+{
+ if (quirks->amplifier > 0)
+ sysfs_remove_group(&dev->dev.kobj, &amplifier_attribute_group);
+}
+
+static int create_amplifier(struct platform_device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_group(&dev->dev.kobj, &amplifier_attribute_group);
+ if (ret)
+ remove_amplifier(dev);
+ return ret;
+}
+
+/*
+ * Deep Sleep Control support
+ * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
+ */
+static ssize_t show_deepsleep_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
+ (u32 *) &out_data);
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return scnprintf(buf, PAGE_SIZE,
+ "[disabled] s5 s5_s4\n");
+ else if (out_data == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "disabled [s5] s5_s4\n");
+ else if (out_data == 2)
+ return scnprintf(buf, PAGE_SIZE,
+ "disabled s5 [s5_s4]\n");
+ }
+ pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
+ return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t toggle_deepsleep(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ acpi_status status;
+ struct wmax_basic_args args;
+
+ if (strcmp(buf, "disabled\n") == 0)
+ args.arg = 0;
+ else if (strcmp(buf, "s5\n") == 0)
+ args.arg = 1;
+ else
+ args.arg = 2;
+ pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
+
+ status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+ NULL);
+
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
+
+static struct attribute *deepsleep_attrs[] = {
+ &dev_attr_deepsleep.attr,
+ NULL,
+};
+
+static struct attribute_group deepsleep_attribute_group = {
+ .name = "deepsleep",
+ .attrs = deepsleep_attrs,
+};
+
+static void remove_deepsleep(struct platform_device *dev)
+{
+ if (quirks->deepslp > 0)
+ sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
+}
+
+static int create_deepsleep(struct platform_device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
+ if (ret)
+ remove_deepsleep(dev);
return ret;
}
@@ -606,6 +798,18 @@ static int __init alienware_wmi_init(void)
goto fail_prep_hdmi;
}
+ if (quirks->amplifier > 0) {
+ ret = create_amplifier(platform_device);
+ if (ret)
+ goto fail_prep_amplifier;
+ }
+
+ if (quirks->deepslp > 0) {
+ ret = create_deepsleep(platform_device);
+ if (ret)
+ goto fail_prep_deepsleep;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -614,6 +818,8 @@ static int __init alienware_wmi_init(void)
fail_prep_zones:
alienware_zone_exit(platform_device);
+fail_prep_deepsleep:
+fail_prep_amplifier:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 131fee2b093e..091ca7ada8fc 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -272,6 +272,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X75VD",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X75VD"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. 1015E",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index aaeeae81e3a9..2c2f02b2e08a 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -28,12 +28,11 @@
#include <linux/acpi.h>
#include <linux/mm.h>
#include <linux/i8042.h>
-#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <acpi/video.h>
-#include "../../firmware/dcdbas.h"
#include "dell-rbtn.h"
+#include "dell-smbios.h"
#define BRIGHTNESS_TOKEN 0x7d
#define KBD_LED_OFF_TOKEN 0x01E1
@@ -44,33 +43,6 @@
#define KBD_LED_AUTO_75_TOKEN 0x02EC
#define KBD_LED_AUTO_100_TOKEN 0x02F6
-/* This structure will be modified by the firmware when we enter
- * system management mode, hence the volatiles */
-
-struct calling_interface_buffer {
- u16 class;
- u16 select;
- volatile u32 input[4];
- volatile u32 output[4];
-} __packed;
-
-struct calling_interface_token {
- u16 tokenID;
- u16 location;
- union {
- u16 value;
- u16 stringlength;
- };
-};
-
-struct calling_interface_structure {
- struct dmi_header header;
- u16 cmdIOAddress;
- u8 cmdIOCode;
- u32 supportedCmds;
- struct calling_interface_token tokens[];
-} __packed;
-
struct quirk_entry {
u8 touchpad_led;
@@ -103,11 +75,6 @@ static struct quirk_entry quirk_dell_xps13_9333 = {
.kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
};
-static int da_command_address;
-static int da_command_code;
-static int da_num_tokens;
-static struct calling_interface_token *da_tokens;
-
static struct platform_driver platform_driver = {
.driver = {
.name = "dell-laptop",
@@ -306,126 +273,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
{ }
};
-static struct calling_interface_buffer *buffer;
-static DEFINE_MUTEX(buffer_mutex);
-
-static void clear_buffer(void)
-{
- memset(buffer, 0, sizeof(struct calling_interface_buffer));
-}
-
-static void get_buffer(void)
-{
- mutex_lock(&buffer_mutex);
- clear_buffer();
-}
-
-static void release_buffer(void)
-{
- mutex_unlock(&buffer_mutex);
-}
-
-static void __init parse_da_table(const struct dmi_header *dm)
-{
- /* Final token is a terminator, so we don't want to copy it */
- int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
- struct calling_interface_token *new_da_tokens;
- struct calling_interface_structure *table =
- container_of(dm, struct calling_interface_structure, header);
-
- /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
- 6 bytes of entry */
-
- if (dm->length < 17)
- return;
-
- da_command_address = table->cmdIOAddress;
- da_command_code = table->cmdIOCode;
-
- new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
- sizeof(struct calling_interface_token),
- GFP_KERNEL);
-
- if (!new_da_tokens)
- return;
- da_tokens = new_da_tokens;
-
- memcpy(da_tokens+da_num_tokens, table->tokens,
- sizeof(struct calling_interface_token) * tokens);
-
- da_num_tokens += tokens;
-}
-
-static void __init find_tokens(const struct dmi_header *dm, void *dummy)
-{
- switch (dm->type) {
- case 0xd4: /* Indexed IO */
- case 0xd5: /* Protected Area Type 1 */
- case 0xd6: /* Protected Area Type 2 */
- break;
- case 0xda: /* Calling interface */
- parse_da_table(dm);
- break;
- }
-}
-
-static int find_token_id(int tokenid)
-{
- int i;
-
- for (i = 0; i < da_num_tokens; i++) {
- if (da_tokens[i].tokenID == tokenid)
- return i;
- }
-
- return -1;
-}
-
-static int find_token_location(int tokenid)
-{
- int id;
-
- id = find_token_id(tokenid);
- if (id == -1)
- return -1;
-
- return da_tokens[id].location;
-}
-
-static struct calling_interface_buffer *
-dell_send_request(struct calling_interface_buffer *buffer, int class,
- int select)
-{
- struct smi_cmd command;
-
- command.magic = SMI_CMD_MAGIC;
- command.command_address = da_command_address;
- command.command_code = da_command_code;
- command.ebx = virt_to_phys(buffer);
- command.ecx = 0x42534931;
-
- buffer->class = class;
- buffer->select = select;
-
- dcdbas_smi_request(&command);
-
- return buffer;
-}
-
-static inline int dell_smi_error(int value)
-{
- switch (value) {
- case 0: /* Completed successfully */
- return 0;
- case -1: /* Completed with error */
- return -EIO;
- case -2: /* Function not supported */
- return -ENXIO;
- default: /* Unknown error */
- return -EINVAL;
- }
-}
-
/*
* Derived from information in smbios-wireless-ctl:
*
@@ -548,6 +395,7 @@ static inline int dell_smi_error(int value)
static int dell_rfkill_set(void *data, bool blocked)
{
+ struct calling_interface_buffer *buffer;
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
int hwswitch_bit = (unsigned long)data - 1;
@@ -555,19 +403,19 @@ static int dell_rfkill_set(void *data, bool blocked)
int status;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0)
goto out;
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = 0x2;
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
hwswitch = buffer->output[1];
@@ -577,27 +425,28 @@ static int dell_rfkill_set(void *data, bool blocked)
(status & BIT(0)) && !(status & BIT(16)))
disable = 1;
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
out:
- release_buffer();
- return dell_smi_error(ret);
+ dell_smbios_release_buffer();
+ return dell_smbios_error(ret);
}
/* Must be called with the buffer held */
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
- int status)
+ int status,
+ struct calling_interface_buffer *buffer)
{
if (status & BIT(0)) {
/* Has hw-switch, sync sw_state to BIOS */
int block = rfkill_blocked(rfkill);
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = (1 | (radio << 8) | (block << 16));
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
} else {
/* No hw-switch, sync BIOS state to sw_state */
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -613,30 +462,31 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
+ struct calling_interface_buffer *buffer;
int radio = ((unsigned long)data & 0xF);
int hwswitch;
int status;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0 || !(status & BIT(0))) {
- release_buffer();
+ dell_smbios_release_buffer();
return;
}
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = 0x2;
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
hwswitch = buffer->output[1];
- release_buffer();
+ dell_smbios_release_buffer();
if (ret != 0)
return;
@@ -653,25 +503,26 @@ static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
{
+ struct calling_interface_buffer *buffer;
int hwswitch_state;
int hwswitch_ret;
int status;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
status = buffer->output[1];
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = 0x2;
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
hwswitch_ret = buffer->output[0];
hwswitch_state = buffer->output[1];
- release_buffer();
+ dell_smbios_release_buffer();
seq_printf(s, "return:\t%d\n", ret);
seq_printf(s, "status:\t0x%X\n", status);
@@ -752,23 +603,24 @@ static const struct file_operations dell_debugfs_fops = {
static void dell_update_rfkill(struct work_struct *ignored)
{
+ struct calling_interface_buffer *buffer;
int hwswitch = 0;
int status;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0)
goto out;
- clear_buffer();
+ dell_smbios_clear_buffer();
buffer->input[0] = 0x2;
- dell_send_request(buffer, 17, 11);
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
if (ret == 0 && (status & BIT(0)))
@@ -776,20 +628,21 @@ static void dell_update_rfkill(struct work_struct *ignored)
if (wifi_rfkill) {
dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
- dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
+ dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
}
if (bluetooth_rfkill) {
dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
hwswitch);
- dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
+ dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
+ buffer);
}
if (wwan_rfkill) {
dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
- dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
+ dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
}
out:
- release_buffer();
+ dell_smbios_release_buffer();
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
@@ -833,6 +686,7 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
static int __init dell_setup_rfkill(void)
{
+ struct calling_interface_buffer *buffer;
int status, ret, whitelisted;
const char *product;
@@ -848,11 +702,11 @@ static int __init dell_setup_rfkill(void)
if (!force_rfkill && !whitelisted)
return 0;
- get_buffer();
- dell_send_request(buffer, 17, 11);
+ buffer = dell_smbios_get_buffer();
+ dell_smbios_send_request(17, 11);
ret = buffer->output[0];
status = buffer->output[1];
- release_buffer();
+ dell_smbios_release_buffer();
/* dell wireless info smbios call is not supported */
if (ret != 0)
@@ -1005,51 +859,53 @@ static void dell_cleanup_rfkill(void)
static int dell_send_intensity(struct backlight_device *bd)
{
- int token;
+ struct calling_interface_buffer *buffer;
+ struct calling_interface_token *token;
int ret;
- token = find_token_location(BRIGHTNESS_TOKEN);
- if (token == -1)
+ token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+ if (!token)
return -ENODEV;
- get_buffer();
- buffer->input[0] = token;
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = token->location;
buffer->input[1] = bd->props.brightness;
if (power_supply_is_system_supplied() > 0)
- dell_send_request(buffer, 1, 2);
+ dell_smbios_send_request(1, 2);
else
- dell_send_request(buffer, 1, 1);
+ dell_smbios_send_request(1, 1);
- ret = dell_smi_error(buffer->output[0]);
+ ret = dell_smbios_error(buffer->output[0]);
- release_buffer();
+ dell_smbios_release_buffer();
return ret;
}
static int dell_get_intensity(struct backlight_device *bd)
{
- int token;
+ struct calling_interface_buffer *buffer;
+ struct calling_interface_token *token;
int ret;
- token = find_token_location(BRIGHTNESS_TOKEN);
- if (token == -1)
+ token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+ if (!token)
return -ENODEV;
- get_buffer();
- buffer->input[0] = token;
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = token->location;
if (power_supply_is_system_supplied() > 0)
- dell_send_request(buffer, 0, 2);
+ dell_smbios_send_request(0, 2);
else
- dell_send_request(buffer, 0, 1);
+ dell_smbios_send_request(0, 1);
if (buffer->output[0])
- ret = dell_smi_error(buffer->output[0]);
+ ret = dell_smbios_error(buffer->output[0]);
else
ret = buffer->output[1];
- release_buffer();
+ dell_smbios_release_buffer();
return ret;
}
@@ -1293,17 +1149,18 @@ static bool kbd_led_present;
static int kbd_get_info(struct kbd_info *info)
{
+ struct calling_interface_buffer *buffer;
u8 units;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
buffer->input[0] = 0x0;
- dell_send_request(buffer, 4, 11);
+ dell_smbios_send_request(4, 11);
ret = buffer->output[0];
if (ret) {
- ret = dell_smi_error(ret);
+ ret = dell_smbios_error(ret);
goto out;
}
@@ -1323,7 +1180,7 @@ static int kbd_get_info(struct kbd_info *info)
info->days = (buffer->output[3] >> 24) & 0xFF;
out:
- release_buffer();
+ dell_smbios_release_buffer();
return ret;
}
@@ -1382,16 +1239,17 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
static int kbd_get_state(struct kbd_state *state)
{
+ struct calling_interface_buffer *buffer;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
buffer->input[0] = 0x1;
- dell_send_request(buffer, 4, 11);
+ dell_smbios_send_request(4, 11);
ret = buffer->output[0];
if (ret) {
- ret = dell_smi_error(ret);
+ ret = dell_smbios_error(ret);
goto out;
}
@@ -1407,15 +1265,16 @@ static int kbd_get_state(struct kbd_state *state)
state->level = (buffer->output[2] >> 16) & 0xFF;
out:
- release_buffer();
+ dell_smbios_release_buffer();
return ret;
}
static int kbd_set_state(struct kbd_state *state)
{
+ struct calling_interface_buffer *buffer;
int ret;
- get_buffer();
+ buffer = dell_smbios_get_buffer();
buffer->input[0] = 0x2;
buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
buffer->input[1] |= (state->triggers & 0xFF) << 16;
@@ -1423,11 +1282,11 @@ static int kbd_set_state(struct kbd_state *state)
buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
buffer->input[2] = state->als_setting & 0xFF;
buffer->input[2] |= (state->level & 0xFF) << 16;
- dell_send_request(buffer, 4, 11);
+ dell_smbios_send_request(4, 11);
ret = buffer->output[0];
- release_buffer();
+ dell_smbios_release_buffer();
- return dell_smi_error(ret);
+ return dell_smbios_error(ret);
}
static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
@@ -1452,50 +1311,52 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
static int kbd_set_token_bit(u8 bit)
{
- int id;
+ struct calling_interface_buffer *buffer;
+ struct calling_interface_token *token;
int ret;
if (bit >= ARRAY_SIZE(kbd_tokens))
return -EINVAL;
- id = find_token_id(kbd_tokens[bit]);
- if (id == -1)
+ token = dell_smbios_find_token(kbd_tokens[bit]);
+ if (!token)
return -EINVAL;
- get_buffer();
- buffer->input[0] = da_tokens[id].location;
- buffer->input[1] = da_tokens[id].value;
- dell_send_request(buffer, 1, 0);
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = token->location;
+ buffer->input[1] = token->value;
+ dell_smbios_send_request(1, 0);
ret = buffer->output[0];
- release_buffer();
+ dell_smbios_release_buffer();
- return dell_smi_error(ret);
+ return dell_smbios_error(ret);
}
static int kbd_get_token_bit(u8 bit)
{
- int id;
+ struct calling_interface_buffer *buffer;
+ struct calling_interface_token *token;
int ret;
int val;
if (bit >= ARRAY_SIZE(kbd_tokens))
return -EINVAL;
- id = find_token_id(kbd_tokens[bit]);
- if (id == -1)
+ token = dell_smbios_find_token(kbd_tokens[bit]);
+ if (!token)
return -EINVAL;
- get_buffer();
- buffer->input[0] = da_tokens[id].location;
- dell_send_request(buffer, 0, 0);
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = token->location;
+ dell_smbios_send_request(0, 0);
ret = buffer->output[0];
val = buffer->output[1];
- release_buffer();
+ dell_smbios_release_buffer();
if (ret)
- return dell_smi_error(ret);
+ return dell_smbios_error(ret);
- return (val == da_tokens[id].value);
+ return (val == token->value);
}
static int kbd_get_first_active_token_bit(void)
@@ -1597,7 +1458,7 @@ static inline void kbd_init_tokens(void)
int i;
for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
- if (find_token_id(kbd_tokens[i]) != -1)
+ if (dell_smbios_find_token(kbd_tokens[i]))
kbd_token_bits |= BIT(i);
}
@@ -2111,8 +1972,9 @@ static void kbd_led_exit(void)
static int __init dell_init(void)
{
+ struct calling_interface_buffer *buffer;
+ struct calling_interface_token *token;
int max_intensity = 0;
- int token;
int ret;
if (!dmi_check_system(dell_device_table))
@@ -2122,13 +1984,6 @@ static int __init dell_init(void)
/* find if this machine support other functions */
dmi_check_system(dell_quirks);
- dmi_walk(find_tokens, NULL);
-
- if (!da_tokens) {
- pr_info("Unable to find dmi tokens\n");
- return -ENODEV;
- }
-
ret = platform_driver_register(&platform_driver);
if (ret)
goto fail_platform_driver;
@@ -2141,16 +1996,6 @@ static int __init dell_init(void)
if (ret)
goto fail_platform_device2;
- /*
- * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
- * is passed to SMI handler.
- */
- buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
- if (!buffer) {
- ret = -ENOMEM;
- goto fail_buffer;
- }
-
ret = dell_setup_rfkill();
if (ret) {
@@ -2171,14 +2016,14 @@ static int __init dell_init(void)
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;
- token = find_token_location(BRIGHTNESS_TOKEN);
- if (token != -1) {
- get_buffer();
- buffer->input[0] = token;
- dell_send_request(buffer, 0, 2);
+ token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+ if (token) {
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = token->location;
+ dell_smbios_send_request(0, 2);
if (buffer->output[0] == 0)
max_intensity = buffer->output[3];
- release_buffer();
+ dell_smbios_release_buffer();
}
if (max_intensity) {
@@ -2208,15 +2053,12 @@ static int __init dell_init(void)
fail_backlight:
dell_cleanup_rfkill();
fail_rfkill:
- free_page((unsigned long)buffer);
-fail_buffer:
platform_device_del(platform_device);
fail_platform_device2:
platform_device_put(platform_device);
fail_platform_device1:
platform_driver_unregister(&platform_driver);
fail_platform_driver:
- kfree(da_tokens);
return ret;
}
@@ -2232,8 +2074,6 @@ static void __exit dell_exit(void)
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
- kfree(da_tokens);
- free_page((unsigned long)buffer);
}
/* dell-rbtn.c driver export functions which will not work correctly (and could
diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c
index cd410e392550..b51a2008d782 100644
--- a/drivers/platform/x86/dell-rbtn.c
+++ b/drivers/platform/x86/dell-rbtn.c
@@ -217,6 +217,21 @@ static void rbtn_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id rbtn_ids[] = {
{ "DELRBTN", 0 },
{ "DELLABCE", 0 },
+
+ /*
+ * This driver can also handle the "DELLABC6" device that
+ * appears on the XPS 13 9350, but that device is disabled
+ * by the DSDT unless booted with acpi_osi="!Windows 2012"
+ * acpi_osi="!Windows 2013". Even if we boot that and bind
+ * the driver, we seem to have inconsistent behavior in
+ * which NetworkManager can get out of sync with the rfkill
+ * state.
+ *
+ * On the XPS 13 9350 and similar laptops, we're not supposed to
+ * use DELLABC6 at all. Instead, we handle the rfkill button
+ * via the intel-hid driver.
+ */
+
{ "", 0 },
};
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
new file mode 100644
index 000000000000..d2412ab097da
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios.c
@@ -0,0 +1,193 @@
+/*
+ * Common functions for kernel modules using Dell SMBIOS
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * Based on documentation in the libsmbios package:
+ * Copyright (C) 2005-2014 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include "../../firmware/dcdbas.h"
+#include "dell-smbios.h"
+
+struct calling_interface_structure {
+ struct dmi_header header;
+ u16 cmdIOAddress;
+ u8 cmdIOCode;
+ u32 supportedCmds;
+ struct calling_interface_token tokens[];
+} __packed;
+
+static struct calling_interface_buffer *buffer;
+static DEFINE_MUTEX(buffer_mutex);
+
+static int da_command_address;
+static int da_command_code;
+static int da_num_tokens;
+static struct calling_interface_token *da_tokens;
+
+int dell_smbios_error(int value)
+{
+ switch (value) {
+ case 0: /* Completed successfully */
+ return 0;
+ case -1: /* Completed with error */
+ return -EIO;
+ case -2: /* Function not supported */
+ return -ENXIO;
+ default: /* Unknown error */
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(dell_smbios_error);
+
+struct calling_interface_buffer *dell_smbios_get_buffer(void)
+{
+ mutex_lock(&buffer_mutex);
+ dell_smbios_clear_buffer();
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
+
+void dell_smbios_clear_buffer(void)
+{
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+}
+EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
+
+void dell_smbios_release_buffer(void)
+{
+ mutex_unlock(&buffer_mutex);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
+
+void dell_smbios_send_request(int class, int select)
+{
+ struct smi_cmd command;
+
+ command.magic = SMI_CMD_MAGIC;
+ command.command_address = da_command_address;
+ command.command_code = da_command_code;
+ command.ebx = virt_to_phys(buffer);
+ command.ecx = 0x42534931;
+
+ buffer->class = class;
+ buffer->select = select;
+
+ dcdbas_smi_request(&command);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_send_request);
+
+struct calling_interface_token *dell_smbios_find_token(int tokenid)
+{
+ int i;
+
+ for (i = 0; i < da_num_tokens; i++) {
+ if (da_tokens[i].tokenID == tokenid)
+ return &da_tokens[i];
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(dell_smbios_find_token);
+
+static void __init parse_da_table(const struct dmi_header *dm)
+{
+ /* Final token is a terminator, so we don't want to copy it */
+ int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
+ struct calling_interface_token *new_da_tokens;
+ struct calling_interface_structure *table =
+ container_of(dm, struct calling_interface_structure, header);
+
+ /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
+ 6 bytes of entry */
+
+ if (dm->length < 17)
+ return;
+
+ da_command_address = table->cmdIOAddress;
+ da_command_code = table->cmdIOCode;
+
+ new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
+ sizeof(struct calling_interface_token),
+ GFP_KERNEL);
+
+ if (!new_da_tokens)
+ return;
+ da_tokens = new_da_tokens;
+
+ memcpy(da_tokens+da_num_tokens, table->tokens,
+ sizeof(struct calling_interface_token) * tokens);
+
+ da_num_tokens += tokens;
+}
+
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
+{
+ switch (dm->type) {
+ case 0xd4: /* Indexed IO */
+ case 0xd5: /* Protected Area Type 1 */
+ case 0xd6: /* Protected Area Type 2 */
+ break;
+ case 0xda: /* Calling interface */
+ parse_da_table(dm);
+ break;
+ }
+}
+
+static int __init dell_smbios_init(void)
+{
+ int ret;
+
+ dmi_walk(find_tokens, NULL);
+
+ if (!da_tokens) {
+ pr_info("Unable to find dmi tokens\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
+ * is passed to SMI handler.
+ */
+ buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto fail_buffer;
+ }
+
+ return 0;
+
+fail_buffer:
+ kfree(da_tokens);
+ return ret;
+}
+
+static void __exit dell_smbios_exit(void)
+{
+ kfree(da_tokens);
+ free_page((unsigned long)buffer);
+}
+
+subsys_initcall(dell_smbios_init);
+module_exit(dell_smbios_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
new file mode 100644
index 000000000000..ec7d40ae5e6e
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios.h
@@ -0,0 +1,46 @@
+/*
+ * Common functions for kernel modules using Dell SMBIOS
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * Based on documentation in the libsmbios package:
+ * Copyright (C) 2005-2014 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DELL_SMBIOS_H_
+#define _DELL_SMBIOS_H_
+
+/* This structure will be modified by the firmware when we enter
+ * system management mode, hence the volatiles */
+
+struct calling_interface_buffer {
+ u16 class;
+ u16 select;
+ volatile u32 input[4];
+ volatile u32 output[4];
+} __packed;
+
+struct calling_interface_token {
+ u16 tokenID;
+ u16 location;
+ union {
+ u16 value;
+ u16 stringlength;
+ };
+};
+
+int dell_smbios_error(int value);
+
+struct calling_interface_buffer *dell_smbios_get_buffer(void);
+void dell_smbios_clear_buffer(void);
+void dell_smbios_release_buffer(void);
+void dell_smbios_send_request(int class, int select);
+
+struct calling_interface_token *dell_smbios_find_token(int tokenid);
+#endif
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 368e193c2741..15c6f1191aec 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -37,6 +37,7 @@
#include <linux/string.h>
#include <linux/dmi.h>
#include <acpi/video.h>
+#include "dell-smbios.h"
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
@@ -47,10 +48,37 @@ MODULE_LICENSE("GPL");
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
static u32 dell_wmi_interface_version;
+static bool wmi_requires_smbios_request;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+ wmi_requires_smbios_request = 1;
+ return 1;
+}
+
+static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron M5110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
+ },
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Vostro V131",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ },
+ { }
+};
+
/*
* Certain keys are flagged as KE_IGNORE. All of these are either
* notifications (rather than requests for change) or are also sent
@@ -90,8 +118,11 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
- /* Shortcut and audio panel keys */
- { KE_IGNORE, 0xe025, { KEY_RESERVED } },
+ /* Dell Instant Launch key */
+ { KE_KEY, 0xe025, { KEY_PROG4 } },
+ { KE_KEY, 0xe029, { KEY_PROG4 } },
+
+ /* Audio panel key */
{ KE_IGNORE, 0xe026, { KEY_RESERVED } },
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
@@ -120,7 +151,10 @@ struct dell_bios_hotkey_table {
};
-static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
+struct dell_dmi_results {
+ int err;
+ struct key_entry *keymap;
+};
/* Uninitialized entries here are KEY_RESERVED == 0. */
static const u16 bios_to_linux_keycode[256] __initconst = {
@@ -166,6 +200,30 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
[255] = KEY_PROG3,
};
+/*
+ * These are applied if the 0xB2 DMI hotkey table is present and doesn't
+ * override them.
+ */
+static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
+ /* Fn-lock */
+ { KE_IGNORE, 0x151, { KEY_RESERVED } },
+
+ /* Change keyboard illumination */
+ { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
+
+ /*
+ * Radio disable (notify only -- there is no model for which the
+ * WMI event is supposed to trigger an action).
+ */
+ { KE_IGNORE, 0x153, { KEY_RFKILL } },
+
+ /* RGB keyboard backlight control */
+ { KE_IGNORE, 0x154, { KEY_RESERVED } },
+
+ /* Stealth mode toggle */
+ { KE_IGNORE, 0x155, { KEY_RESERVED } },
+};
+
static struct input_dev *dell_wmi_input_dev;
static void dell_wmi_process_key(int reported_key)
@@ -188,6 +246,9 @@ static void dell_wmi_process_key(int reported_key)
acpi_video_handles_brightness_key_presses())
return;
+ if (reported_key == 0xe025 && !wmi_requires_smbios_request)
+ return;
+
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
}
@@ -337,20 +398,60 @@ static void dell_wmi_notify(u32 value, void *context)
kfree(obj);
}
-static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
+static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
{
- int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
- sizeof(struct dell_bios_keymap_entry);
- struct key_entry *keymap;
int i;
- keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
- if (!keymap)
- return NULL;
+ for (i = 0; i < len; i++)
+ if (keymap[i].code == scancode)
+ return true;
+
+ return false;
+}
+
+static void __init handle_dmi_entry(const struct dmi_header *dm,
+
+ void *opaque)
+
+{
+ struct dell_dmi_results *results = opaque;
+ struct dell_bios_hotkey_table *table;
+ int hotkey_num, i, pos = 0;
+ struct key_entry *keymap;
+ int num_bios_keys;
+
+ if (results->err || results->keymap)
+ return; /* We already found the hotkey table. */
+
+ if (dm->type != 0xb2)
+ return;
+
+ table = container_of(dm, struct dell_bios_hotkey_table, header);
+
+ hotkey_num = (table->header.length -
+ sizeof(struct dell_bios_hotkey_table)) /
+ sizeof(struct dell_bios_keymap_entry);
+ if (hotkey_num < 1) {
+ /*
+ * Historically, dell-wmi would ignore a DMI entry of
+ * fewer than 7 bytes. Sizes between 4 and 8 bytes are
+ * nonsensical (both the header and all entries are 4
+ * bytes), so we approximate the old behavior by
+ * ignoring tables with fewer than one entry.
+ */
+ return;
+ }
+
+ keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
+ sizeof(struct key_entry), GFP_KERNEL);
+ if (!keymap) {
+ results->err = -ENOMEM;
+ return;
+ }
for (i = 0; i < hotkey_num; i++) {
const struct dell_bios_keymap_entry *bios_entry =
- &dell_bios_hotkey_table->keymap[i];
+ &table->keymap[i];
/* Uninitialized entries are 0 aka KEY_RESERVED. */
u16 keycode = (bios_entry->keycode <
@@ -370,20 +471,39 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
}
if (keycode == KEY_KBDILLUMTOGGLE)
- keymap[i].type = KE_IGNORE;
+ keymap[pos].type = KE_IGNORE;
else
- keymap[i].type = KE_KEY;
- keymap[i].code = bios_entry->scancode;
- keymap[i].keycode = keycode;
+ keymap[pos].type = KE_KEY;
+ keymap[pos].code = bios_entry->scancode;
+ keymap[pos].keycode = keycode;
+
+ pos++;
+ }
+
+ num_bios_keys = pos;
+
+ for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
+ const struct key_entry *entry = &dell_wmi_extra_keymap[i];
+
+ /*
+ * Check if we've already found this scancode. This takes
+ * quadratic time, but it doesn't matter unless the list
+ * of extra keys gets very long.
+ */
+ if (!have_scancode(entry->code, keymap, num_bios_keys)) {
+ keymap[pos] = *entry;
+ pos++;
+ }
}
- keymap[hotkey_num].type = KE_END;
+ keymap[pos].type = KE_END;
- return keymap;
+ results->keymap = keymap;
}
static int __init dell_wmi_input_setup(void)
{
+ struct dell_dmi_results dmi_results = {};
int err;
dell_wmi_input_dev = input_allocate_device();
@@ -394,20 +514,31 @@ static int __init dell_wmi_input_setup(void)
dell_wmi_input_dev->phys = "wmi/input0";
dell_wmi_input_dev->id.bustype = BUS_HOST;
- if (dell_new_hk_type) {
- const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
- if (!keymap) {
- err = -ENOMEM;
- goto err_free_dev;
- }
+ if (dmi_walk(handle_dmi_entry, &dmi_results)) {
+ /*
+ * Historically, dell-wmi ignored dmi_walk errors. A failure
+ * is certainly surprising, but it probably just indicates
+ * a very old laptop.
+ */
+ pr_warn("no DMI; using the old-style hotkey interface\n");
+ }
- err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
+ if (dmi_results.err) {
+ err = dmi_results.err;
+ goto err_free_dev;
+ }
+
+ if (dmi_results.keymap) {
+ dell_new_hk_type = true;
+
+ err = sparse_keymap_setup(dell_wmi_input_dev,
+ dmi_results.keymap, NULL);
/*
* Sparse keymap library makes a copy of keymap so we
* don't need the original one that was allocated.
*/
- kfree(keymap);
+ kfree(dmi_results.keymap);
} else {
err = sparse_keymap_setup(dell_wmi_input_dev,
dell_wmi_legacy_keymap, NULL);
@@ -434,15 +565,6 @@ static void dell_wmi_input_destroy(void)
input_unregister_device(dell_wmi_input_dev);
}
-static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
-{
- if (dm->type == 0xb2 && dm->length > 6) {
- dell_new_hk_type = true;
- dell_bios_hotkey_table =
- container_of(dm, struct dell_bios_hotkey_table, header);
- }
-}
-
/*
* Descriptor buffer is 128 byte long and contains:
*
@@ -509,6 +631,38 @@ static int __init dell_wmi_check_descriptor_buffer(void)
return 0;
}
+/*
+ * According to Dell SMBIOS documentation:
+ *
+ * 17 3 Application Program Registration
+ *
+ * cbArg1 Application ID 1 = 0x00010000
+ * cbArg2 Application ID 2
+ * QUICKSET/DCP = 0x51534554 "QSET"
+ * ALS Driver = 0x416c7353 "AlsS"
+ * Latitude ON = 0x4c6f6e52 "LonR"
+ * cbArg3 Application version or revision number
+ * cbArg4 0 = Unregister application
+ * 1 = Register application
+ * cbRes1 Standard return codes (0, -1, -2)
+ */
+
+static int dell_wmi_events_set_enabled(bool enable)
+{
+ struct calling_interface_buffer *buffer;
+ int ret;
+
+ buffer = dell_smbios_get_buffer();
+ buffer->input[0] = 0x10000;
+ buffer->input[1] = 0x51534554;
+ buffer->input[3] = enable;
+ dell_smbios_send_request(17, 3);
+ ret = buffer->output[0];
+ dell_smbios_release_buffer();
+
+ return dell_smbios_error(ret);
+}
+
static int __init dell_wmi_init(void)
{
int err;
@@ -524,8 +678,6 @@ static int __init dell_wmi_init(void)
if (err)
return err;
- dmi_walk(find_hk_type, NULL);
-
err = dell_wmi_input_setup();
if (err)
return err;
@@ -538,12 +690,26 @@ static int __init dell_wmi_init(void)
return -ENODEV;
}
+ dmi_check_system(dell_wmi_smbios_list);
+
+ if (wmi_requires_smbios_request) {
+ err = dell_wmi_events_set_enabled(true);
+ if (err) {
+ pr_err("Failed to enable WMI events\n");
+ wmi_remove_notify_handler(DELL_EVENT_GUID);
+ dell_wmi_input_destroy();
+ return err;
+ }
+ }
+
return 0;
}
module_init(dell_wmi_init);
static void __exit dell_wmi_exit(void)
{
+ if (wmi_requires_smbios_request)
+ dell_wmi_events_set_enabled(false);
wmi_remove_notify_handler(DELL_EVENT_GUID);
dell_wmi_input_destroy();
}
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 1c62caff93fd..ffc84cc7b1c7 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -114,6 +114,7 @@
#define KEY2_CODE 0x411
#define KEY3_CODE 0x412
#define KEY4_CODE 0x413
+#define KEY5_CODE 0x420
#define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40
@@ -149,7 +150,7 @@ struct fujitsu_t {
char phys[32];
struct backlight_device *bl_device;
struct platform_device *pf_device;
- int keycode1, keycode2, keycode3, keycode4;
+ int keycode1, keycode2, keycode3, keycode4, keycode5;
unsigned int max_brightness;
unsigned int brightness_changed;
@@ -823,6 +824,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
set_bit(fujitsu->keycode2, input->keybit);
set_bit(fujitsu->keycode3, input->keybit);
set_bit(fujitsu->keycode4, input->keybit);
+ set_bit(fujitsu->keycode5, input->keybit);
set_bit(KEY_UNKNOWN, input->keybit);
error = input_register_device(input);
@@ -962,6 +964,9 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
case KEY4_CODE:
keycode = fujitsu->keycode4;
break;
+ case KEY5_CODE:
+ keycode = fujitsu->keycode5;
+ break;
case 0:
keycode = 0;
break;
@@ -1072,6 +1077,7 @@ static int __init fujitsu_init(void)
fujitsu->keycode2 = KEY_PROG2;
fujitsu->keycode3 = KEY_PROG3;
fujitsu->keycode4 = KEY_PROG4;
+ fujitsu->keycode5 = KEY_RFKILL;
dmi_check_system(fujitsu_dmi_table);
result = acpi_bus_register_driver(&acpi_fujitsu_driver);
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index fb4dd7b3ee71..6f145f2d004d 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -157,7 +157,6 @@ static struct platform_device *hp_wmi_platform_dev;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
-static struct rfkill *gps_rfkill;
struct rfkill2_device {
u8 id;
@@ -613,10 +612,6 @@ static void hp_wmi_notify(u32 value, void *context)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
- if (gps_rfkill)
- rfkill_set_states(gps_rfkill,
- hp_wmi_get_sw_state(HPWMI_GPS),
- hp_wmi_get_hw_state(HPWMI_GPS));
break;
case HPWMI_CPU_BATTERY_THROTTLE:
pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
@@ -746,7 +741,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
(void *) HPWMI_BLUETOOTH);
if (!bluetooth_rfkill) {
err = -ENOMEM;
- goto register_wifi_error;
+ goto register_bluetooth_error;
}
rfkill_init_sw_state(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
@@ -764,7 +759,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
(void *) HPWMI_WWAN);
if (!wwan_rfkill) {
err = -ENOMEM;
- goto register_bluetooth_error;
+ goto register_wwan_error;
}
rfkill_init_sw_state(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN));
@@ -775,35 +770,13 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
goto register_wwan_error;
}
- if (wireless & 0x8) {
- gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
- RFKILL_TYPE_GPS,
- &hp_wmi_rfkill_ops,
- (void *) HPWMI_GPS);
- if (!gps_rfkill) {
- err = -ENOMEM;
- goto register_wwan_error;
- }
- rfkill_init_sw_state(gps_rfkill,
- hp_wmi_get_sw_state(HPWMI_GPS));
- rfkill_set_hw_state(gps_rfkill,
- hp_wmi_get_hw_state(HPWMI_GPS));
- err = rfkill_register(gps_rfkill);
- if (err)
- goto register_gps_error;
- }
-
return 0;
-register_gps_error:
- rfkill_destroy(gps_rfkill);
- gps_rfkill = NULL;
- if (bluetooth_rfkill)
- rfkill_unregister(bluetooth_rfkill);
+
register_wwan_error:
rfkill_destroy(wwan_rfkill);
wwan_rfkill = NULL;
- if (gps_rfkill)
- rfkill_unregister(gps_rfkill);
+ if (bluetooth_rfkill)
+ rfkill_unregister(bluetooth_rfkill);
register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
bluetooth_rfkill = NULL;
@@ -907,7 +880,6 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
wifi_rfkill = NULL;
bluetooth_rfkill = NULL;
wwan_rfkill = NULL;
- gps_rfkill = NULL;
rfkill2_count = 0;
if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
@@ -960,10 +932,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
- if (gps_rfkill) {
- rfkill_unregister(gps_rfkill);
- rfkill_destroy(gps_rfkill);
- }
return 0;
}
@@ -999,10 +967,6 @@ static int hp_wmi_resume_handler(struct device *device)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
- if (gps_rfkill)
- rfkill_set_states(gps_rfkill,
- hp_wmi_get_sw_state(HPWMI_GPS),
- hp_wmi_get_hw_state(HPWMI_GPS));
return 0;
}
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index d78ee151c9e4..be3bc2f4edd4 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -865,6 +865,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo ideapad Y700-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad Y700 Touch-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
+ },
+ },
+ {
.ident = "Lenovo ideapad Y700-17ISK",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index e20f23e04c24..f93abc8c1424 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -180,8 +180,7 @@ static int intel_hid_probe(struct platform_device *device)
return -ENODEV;
}
- priv = devm_kzalloc(&device->dev,
- sizeof(struct intel_hid_priv *), GFP_KERNEL);
+ priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_set_drvdata(&device->dev, priv);
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 092519e37de6..3fb1d85c70a8 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -67,7 +67,8 @@
/* exported resources from IFWI */
#define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000
-#define PLAT_RESOURCE_GCR_SIZE 0x1000
+#define PLAT_RESOURCE_GCR_OFFSET 0x1008
+#define PLAT_RESOURCE_GCR_SIZE 0x4
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
@@ -766,7 +767,7 @@ static int ipc_plat_get_res(struct platform_device *pdev)
}
ipcdev.ipc_base = addr;
- ipcdev.gcr_base = res->start + size;
+ ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET;
ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
dev_info(&pdev->dev, "ipc res: %pR\n", res);
@@ -824,7 +825,8 @@ static int ipc_plat_probe(struct platform_device *pdev)
goto err_device;
}
- if (request_irq(ipcdev.irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+ if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
+ "intel_pmc_ipc", &ipcdev)) {
dev_err(&pdev->dev, "Failed to request irq\n");
ret = -EBUSY;
goto err_irq;
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index f94b730540e2..e81daff65f62 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -24,7 +24,6 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sfi.h>
-#include <linux/module.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
@@ -611,28 +610,6 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
}
-/**
- * ipc_remove - remove a bound IPC device
- * @pdev: PCI device
- *
- * In practice the SCU is not removable but this function is also
- * called for each device on a module unload or cleanup which is the
- * path that will get used.
- *
- * Free up the mappings and release the PCI resources
- */
-static void ipc_remove(struct pci_dev *pdev)
-{
- struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev);
-
- mutex_lock(&ipclock);
- scu->dev = NULL;
- mutex_unlock(&ipclock);
-
- iounmap(scu->i2c_base);
- intel_scu_devices_destroy();
-}
-
static const struct pci_device_id pci_ids[] = {
{
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT),
@@ -650,17 +627,13 @@ static const struct pci_device_id pci_ids[] = {
0,
}
};
-MODULE_DEVICE_TABLE(pci, pci_ids);
static struct pci_driver ipc_driver = {
+ .driver = {
+ .suppress_bind_attrs = true,
+ },
.name = "intel_scu_ipc",
.id_table = pci_ids,
.probe = ipc_probe,
- .remove = ipc_remove,
};
-
-module_pci_driver(ipc_driver);
-
-MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>");
-MODULE_DESCRIPTION("Intel SCU IPC driver");
-MODULE_LICENSE("GPL");
+builtin_pci_driver(ipc_driver);
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index f97019b0106f..397119f83e82 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -1030,8 +1030,19 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
switch (telem_unit) {
case TELEM_PSS:
ret = intel_punit_ipc_command(
+ IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
+ 0, 0, NULL, &temp);
+ if (ret) {
+ pr_err("PSS TRACE_CTRL Read Failed\n");
+ goto out;
+ }
+
+ TELEM_CLEAR_VERBOSITY_BITS(temp);
+ TELEM_SET_VERBOSITY_BITS(temp, verbosity);
+
+ ret = intel_punit_ipc_command(
IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
- 0, 0, &verbosity, NULL);
+ 0, 0, &temp, NULL);
if (ret) {
pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
goto out;
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index a268a7abf8ab..e305ab541a22 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -6653,18 +6653,16 @@ static void __init tpacpi_detect_brightness_capabilities(void)
switch (b) {
case 16:
bright_maxlvl = 15;
- pr_info("detected a 16-level brightness capable ThinkPad\n");
break;
case 8:
case 0:
bright_maxlvl = 7;
- pr_info("detected a 8-level brightness capable ThinkPad\n");
break;
default:
- pr_info("Unsupported brightness interface\n");
tp_features.bright_unkfw = 1;
bright_maxlvl = b - 1;
}
+ pr_debug("detected %u brightness levels\n", bright_maxlvl + 1);
}
static int __init brightness_init(struct ibm_init_struct *iibm)
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 73833079bac8..df1f1a76a862 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -36,6 +36,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
@@ -117,6 +118,7 @@ MODULE_LICENSE("GPL");
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
#define HCI_ACCELEROMETER 0x006d
+#define HCI_COOLING_METHOD 0x007f
#define HCI_KBD_ILLUMINATION 0x0095
#define HCI_ECO_MODE 0x0097
#define HCI_ACCELEROMETER2 0x00a6
@@ -186,6 +188,7 @@ struct toshiba_acpi_dev {
int usbsc_bat_level;
int usbsc_mode_base;
int hotkey_event_type;
+ int max_cooling_method;
unsigned int illumination_supported:1;
unsigned int video_supported:1;
@@ -205,6 +208,7 @@ struct toshiba_acpi_dev {
unsigned int panel_power_on_supported:1;
unsigned int usb_three_supported:1;
unsigned int wwan_supported:1;
+ unsigned int cooling_method_supported:1;
unsigned int sysfs_created:1;
unsigned int special_functions;
@@ -217,6 +221,10 @@ struct toshiba_acpi_dev {
static struct toshiba_acpi_dev *toshiba_acpi;
+static bool disable_hotkeys;
+module_param(disable_hotkeys, bool, 0444);
+MODULE_PARM_DESC(disable_hotkeys, "Disables the hotkeys activation");
+
static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6207", 0},
@@ -1194,6 +1202,53 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
return out[0] == TOS_SUCCESS ? 0 : -EIO;
}
+/* Cooling Method */
+static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev)
+{
+ u32 in[TCI_WORDS] = { HCI_GET, HCI_COOLING_METHOD, 0, 0, 0, 0 };
+ u32 out[TCI_WORDS];
+ acpi_status status;
+
+ dev->cooling_method_supported = 0;
+ dev->max_cooling_method = 0;
+
+ status = tci_raw(dev, in, out);
+ if (ACPI_FAILURE(status))
+ pr_err("ACPI call to get Cooling Method failed\n");
+
+ if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
+ return;
+
+ dev->cooling_method_supported = 1;
+ dev->max_cooling_method = out[3];
+}
+
+static int toshiba_cooling_method_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+ u32 result = hci_read(dev, HCI_COOLING_METHOD, state);
+
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get Cooling Method failed\n");
+
+ if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
+}
+
+static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+ u32 result = hci_write(dev, HCI_COOLING_METHOD, state);
+
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get Cooling Method failed\n");
+
+ if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
+}
+
/* Transflective Backlight */
static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status)
{
@@ -2239,6 +2294,54 @@ static ssize_t usb_three_store(struct device *dev,
}
static DEVICE_ATTR_RW(usb_three);
+static ssize_t cooling_method_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+ int ret;
+
+ ret = toshiba_cooling_method_get(toshiba, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d %d\n", state, toshiba->max_cooling_method);
+}
+
+static ssize_t cooling_method_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+ int ret;
+
+ ret = kstrtoint(buf, 0, &state);
+ if (ret)
+ return ret;
+
+ /*
+ * Check for supported values
+ * Depending on the laptop model, some only support these two:
+ * 0 - Maximum Performance
+ * 1 - Battery Optimized
+ *
+ * While some others support all three methods:
+ * 0 - Maximum Performance
+ * 1 - Performance
+ * 2 - Battery Optimized
+ */
+ if (state < 0 || state > toshiba->max_cooling_method)
+ return -EINVAL;
+
+ ret = toshiba_cooling_method_set(toshiba, state);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_RW(cooling_method);
+
static struct attribute *toshiba_attributes[] = {
&dev_attr_version.attr,
&dev_attr_fan.attr,
@@ -2255,6 +2358,7 @@ static struct attribute *toshiba_attributes[] = {
&dev_attr_kbd_function_keys.attr,
&dev_attr_panel_power_on.attr,
&dev_attr_usb_three.attr,
+ &dev_attr_cooling_method.attr,
NULL,
};
@@ -2289,6 +2393,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
exists = (drv->panel_power_on_supported) ? true : false;
else if (attr == &dev_attr_usb_three.attr)
exists = (drv->usb_three_supported) ? true : false;
+ else if (attr == &dev_attr_cooling_method.attr)
+ exists = (drv->cooling_method_supported) ? true : false;
return exists ? attr->mode : 0;
}
@@ -2591,6 +2697,11 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
acpi_handle ec_handle;
int error;
+ if (disable_hotkeys) {
+ pr_info("Hotkeys disabled by module parameter\n");
+ return 0;
+ }
+
if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) {
pr_info("WMI event detected, hotkeys will not be monitored\n");
return 0;
@@ -2779,6 +2890,8 @@ static void print_supported_features(struct toshiba_acpi_dev *dev)
pr_cont(" usb3");
if (dev->wwan_supported)
pr_cont(" wwan");
+ if (dev->cooling_method_supported)
+ pr_cont(" cooling-method");
pr_cont("\n");
}
@@ -2963,6 +3076,8 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
if (dev->wwan_supported)
toshiba_acpi_setup_wwan_rfkill(dev);
+ toshiba_cooling_method_available(dev);
+
print_supported_features(dev);
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,