diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-23 17:20:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-23 17:20:59 -0700 |
commit | 5a010c73cdb760c9bdf37b28824b6566789cc005 (patch) | |
tree | a12508a6d3b20807eaa822bedd95973df47d6307 /drivers/platform | |
parent | b615d3d406ead1157c6b846c417b71a3b6600776 (diff) | |
parent | fffcad87d4e7c5f6f6f6e5fc9d337bd6f197f80f (diff) | |
download | op-kernel-dev-5a010c73cdb760c9bdf37b28824b6566789cc005.zip op-kernel-dev-5a010c73cdb760c9bdf37b28824b6566789cc005.tar.gz |
Merge tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86
Pull x86 platform driver updates from Darren Hart:
"Significant refactoring of Dell laptop drivers, modularizing the
smbios code. Multiple new platforms added for ideapad, asus, dell,
and alienware using existing quirks. A few fixes and cleanups.
hp-wmi:
- Remove GPS rfkill support via pre-2009 interface
- fix unregister order in hp_wmi_rfkill_setup() once again
ideapad-laptop:
- Add ideapad Y700 (15) to the no_hw_rfkill DMI list
fujitsu-laptop:
- Support radio toggle button
intel-hid:
- allocate correct amount of memory for private struct
platform/x86:
- Make intel_scu_ipc explicitly non-modular
intel_pmc_ipc:
- Avoid pending IPC1 command during legacy suspend
- Fix GCR register base address and length
asus-nb-wmi:
- add wapf=4 quirk for ASUS X75VD
intel_telemetry_pltdrv:
- Change verbosity control bits
dell-rbtn:
- Add a comment about the XPS 13 9350
dell-wmi, dell-laptop:
- depends DMI
dell-wmi:
- support Dell Inspiron M5110
- properly process Dell Instant Launch hotkey
- enable receiving WMI events on Dell Vostro V131
- Support new hotkeys on the XPS 13 9350 (Skylake)
- Clean up hotkey table size check
- Stop storing pointers to DMI tables
dell-laptop:
- move dell_smi_error() to dell-smbios
- use dell_smbios_find_token() instead of find_token_location()
- use dell_smbios_find_token() instead of find_token_id()
- extract SMBIOS-related code to a separate module
dell-smbios:
- rename dell_smi_error() to dell_smbios_error()
- make da_tokens static
- remove find_token_{id,location}()
- implement new function for finding DMI table 0xDA tokens
- make the SMBIOS buffer static
- return the SMBIOS buffer from dell_smbios_get_buffer()
- don't return an SMBIOS buffer from dell_smbios_send_request()
- don't pass an SMBIOS buffer to dell_smbios_send_request()
- rename dell_send_request() to dell_smbios_send_request()
- rename release_buffer() to dell_smbios_release_buffer()
- rename clear_buffer() to dell_smbios_clear_buffer()
- rename get_buffer() to dell_smbios_get_buffer()
dell-led:
- use dell_smbios_send_request() for performing SMBIOS calls
- use dell_smbios_find_token() for finding mic DMI tokens
toshiba_acpi:
- Add a module parameter to disable hotkeys registration
- Add sysfs entries for the Cooling Method feature
- Add support for cooling method feature
Documentation/ABI:
- Update sysfs-driver-toshiba_acpi file
thinkpad_acpi:
- Remove ambiguous logging for "Unsupported brightness interface"
alienware-wmi:
- whitespace improvements
- Add support for two new systems: ASM200 and ASM201.
- Add support for deep sleep control.
- Add initial support for alienware graphics amplifier.
- Add support for new platform: X51-R3
- Clean up whitespace for ASM100 platform"
* tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (47 commits)
hp-wmi: Remove GPS rfkill support via pre-2009 interface
hp-wmi: fix unregister order in hp_wmi_rfkill_setup() once again
dell-wmi: support Dell Inspiron M5110
dell-wmi: properly process Dell Instant Launch hotkey
dell-wmi: enable receiving WMI events on Dell Vostro V131
dell-smbios: rename dell_smi_error() to dell_smbios_error()
dell-laptop: move dell_smi_error() to dell-smbios
ideapad-laptop: Add ideapad Y700 (15) to the no_hw_rfkill DMI list
fujitsu-laptop: Support radio toggle button
intel-hid: allocate correct amount of memory for private struct
platform/x86: Make intel_scu_ipc explicitly non-modular
intel_pmc_ipc: Avoid pending IPC1 command during legacy suspend
intel_pmc_ipc: Fix GCR register base address and length
asus-nb-wmi: add wapf=4 quirk for ASUS X75VD
intel_telemetry_pltdrv: Change verbosity control bits
dell-rbtn: Add a comment about the XPS 13 9350
dell-wmi: Support new hotkeys on the XPS 13 9350 (Skylake)
dell-wmi: Clean up hotkey table size check
dell-wmi, dell-laptop: depends DMI
dell-wmi: Stop storing pointers to DMI tables
...
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 15 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 286 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 370 | ||||
-rw-r--r-- | drivers/platform/x86/dell-rbtn.c | 15 | ||||
-rw-r--r-- | drivers/platform/x86/dell-smbios.c | 193 | ||||
-rw-r--r-- | drivers/platform/x86/dell-smbios.h | 46 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 238 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 46 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 14 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 3 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 35 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_pltdrv.c | 13 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 115 |
18 files changed, 995 insertions, 424 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 69f93a5..ed2004b 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 40574e7..448443c 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 1e1e594..0056294 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, &lifier_attribute_group); +} + +static int create_amplifier(struct platform_device *dev) +{ + int ret; + + ret = sysfs_create_group(&dev->dev.kobj, &lifier_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 131fee2..091ca7a 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 aaeeae8..2c2f02b 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 cd410e3..b51a200 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 0000000..d2412ab --- /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 0000000..ec7d40a --- /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 368e193..15c6f11 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 1c62caf..ffc84cc 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 fb4dd7b..6f145f2 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 d78ee15..be3bc2f 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 e20f23e..f93abc8 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 092519e..3fb1d85 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 f94b730..e81daff 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 f97019b..397119f 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 a268a7a..e305ab5 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 7383307..df1f1a7 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, |