diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-25 14:35:37 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-25 14:35:37 -0800 |
commit | 94eae8034002401d71ae950106659e16add36e77 (patch) | |
tree | 111846689f951b9615cc67cd285699090b924447 /drivers/platform | |
parent | 5d8a00eee2ed2e548a5d21b0edf495f3f7bf8bb4 (diff) | |
parent | af050abb5c2e5e7d3e1368475d63cbac597dc34f (diff) | |
download | linux-stable-94eae8034002401d71ae950106659e16add36e77.tar.gz linux-stable-94eae8034002401d71ae950106659e16add36e77.tar.bz2 linux-stable-94eae8034002401d71ae950106659e16add36e77.zip |
Merge tag 'platform-drivers-x86-v4.11-1' of git://git.infradead.org/linux-platform-drivers-x86
Pull x86 platform driver updates from Darren Hart:
"Big picture:
- New intel_turbo_max_3 driver, providing max core frequency
information to the scheduler. Intel PMC APL support, s0ix read API,
and fixes.
- New Silead touchscreen platform touchscreen descriptions.
Additional hotkey support for the intel-hid driver.
- New model support for dell-laptop and hp_accel.
- Several cleanups, especially to the fujitsu-laptop and
intel_mid_powerbtn drivers.
Detail summary:
platorm/x86:
- silead depends on I2C being built-in
- add support for devices with Silead touchscreens
- Support Turbo Boost Max 3.0 for non HWP systems
intel_turbo_max_3:
- make it explicitly non-modular
dell-laptop:
- Add Latitude 7480 and others to the DMI whitelist
intel-hid:
- Support 5 button array
thinkpad_acpi:
- Call led_classdev_notify_brightness_hw_changed on kbd brightness change
- Use brightness_set_blocking callback for LEDs
- Stop setting led_classdev brightness directly
acer-wmi:
- add another KEY_WLAN keycode
- Inform firmware that RF Button Driver is active
- setup accelerometer when machine has appropriate notify event
asus-wireless:
- Fix indentation
- Use per-HID HSWC parameters
intel_pmc_ipc:
- Add APL PMC PCI Id
- read s0ix residency API
- Remove unused iTCO_version variable
alienware-wmi:
- Remove header duplicate
intel_pmc_core:
- fix out-of-bounds accesses on stack
intel_mid_powerbtn:
- Use SCU IPC directly
- Unify IRQ acknowledgment
- Move comment to where it belongs
- Unify PBSTATUS access
- Remove snail address
- Sort headers alphabetically
- Join string literals
- Enable driver for Merrifield
- Acknowledge interrupts
- Factor out mfld_ack()
- Introduce driver data
- Substitute mfld by mid
- Convert to use devm_*()
fujitsu-laptop:
- make hotkey handling functions more similar
- break up complex loop condition
- move keycode processing to separate functions
- decrease indentation in acpi_fujitsu_hotkey_notify()
- simplify logolamp_get()
- rework logolamp_set() to properly handle errors
- set default trigger for radio LED to rfkill-any
dell-smbios:
- Auto-select as needed
intel_mid_thermal:
- Fix module autoload
- Remove duplicated platform device ID
mlx-platform:
- mlxcpld-hotplug driver style fixes
hp_accel:
- Add support for HP ZBook 17"
* tag 'platform-drivers-x86-v4.11-1' of git://git.infradead.org/linux-platform-drivers-x86: (45 commits)
platform/x86: intel_turbo_max_3: make it explicitly non-modular
platform/x86: dell-laptop: Add Latitude 7480 and others to the DMI whitelist
platform/x86: intel-hid: Support 5 button array
platform/x86: thinkpad_acpi: Call led_classdev_notify_brightness_hw_changed on kbd brightness change
platform/x86: thinkpad_acpi: Use brightness_set_blocking callback for LEDs
platform/x86: thinkpad_acpi: Stop setting led_classdev brightness directly
leds: class: Add new optional brightness_hw_changed attribute
platform/x86: acer-wmi: add another KEY_WLAN keycode
platform/x86: acer-wmi: Inform firmware that RF Button Driver is active
platform/x86: asus-wireless: Fix indentation
platform/x86: asus-wireless: Use per-HID HSWC parameters
platform/x86: intel_pmc_ipc: Add APL PMC PCI Id
platform/x86: intel_pmc_ipc: read s0ix residency API
platform/x86: alienware-wmi: Remove header duplicate
platform/x86: intel_mid_powerbtn: Use SCU IPC directly
platform/x86: intel_mid_powerbtn: Unify IRQ acknowledgment
platform/x86: intel_mid_powerbtn: Move comment to where it belongs
platform/x86: intel_mid_powerbtn: Unify PBSTATUS access
platform/x86: intel_pmc_core: fix out-of-bounds accesses on stack
platform/x86: silead depends on I2C being built-in
...
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 31 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 97 | ||||
-rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wireless.c | 60 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 220 | ||||
-rw-r--r-- | drivers/platform/x86/hp_accel.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 96 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_powerbtn.c | 187 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_thermal.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 67 | ||||
-rw-r--r-- | drivers/platform/x86/intel_turbo_max_3.c | 151 | ||||
-rw-r--r-- | drivers/platform/x86/mlx-platform.c | 84 | ||||
-rw-r--r-- | drivers/platform/x86/silead_dmi.c | 136 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 124 |
17 files changed, 951 insertions, 320 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a9e9c91cf4d4..4bc88eb52712 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -92,9 +92,8 @@ 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 + tristate + select DCDBAS ---help--- This module provides common functions for kernel modules using Dell SMBIOS. @@ -103,16 +102,15 @@ config DELL_SMBIOS config DELL_LAPTOP tristate "Dell Laptop Extras" - depends on DELL_SMBIOS depends on DMI depends on BACKLIGHT_CLASS_DEVICE depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n depends on SERIO_I8042 + select DELL_SMBIOS select POWER_SUPPLY select LEDS_CLASS select NEW_LEDS - default n ---help--- This driver adds support for rfkill and backlight control to Dell laptops (except for some models covered by the Compal driver). @@ -123,7 +121,7 @@ config DELL_WMI depends on DMI depends on INPUT depends on ACPI_VIDEO || ACPI_VIDEO = n - depends on DELL_SMBIOS + select DELL_SMBIOS select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. @@ -1069,6 +1067,27 @@ config MLX_CPLD_PLATFORM This driver handles hot-plug events for the power suppliers, power cables and fans on the wide range Mellanox IB and Ethernet systems. +config INTEL_TURBO_MAX_3 + bool "Intel Turbo Boost Max Technology 3.0 enumeration driver" + depends on X86_64 && SCHED_MC_PRIO + ---help--- + This driver reads maximum performance ratio of each CPU and set up + the scheduler priority metrics. In this way scheduler can prefer + CPU with higher performance to schedule tasks. + This driver is only required when the system is not using Hardware + P-States (HWP). In HWP mode, priority can be read from ACPI tables. + +config SILEAD_DMI + bool "Tablets with Silead touchscreens" + depends on ACPI && DMI && I2C=y && INPUT + ---help--- + Certain ACPI based tablets with Silead touchscreens do not have + enough data in ACPI tables for the touchscreen driver to handle + the touchscreen properly, as OEMs expected the data to be baked + into the tablet model specific version of the driver shipped + with the OS-image for the device. This option supplies the missing + information. Enable this for x86 tablets with Silead touchscreens. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index cf9fc3e930c7..299d0f9e40f7 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o +obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o @@ -76,3 +77,4 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o +obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a66192f692e3..dac0fbe87460 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -128,6 +128,7 @@ static const struct key_entry acer_wmi_keymap[] __initconst = { {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} }, {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} }, + {KE_KEY, 0x86, {KEY_WLAN} }, {KE_END, 0} }; @@ -150,15 +151,30 @@ struct event_return_value { #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ -struct lm_input_params { +/* Hotkey Customized Setting and Acer Application Status. + * Set Device Default Value and Report Acer Application Status. + * When Acer Application starts, it will run this method to inform + * BIOS/EC that Acer Application is on. + * App Status + * Bit[0]: Launch Manager Status + * Bit[1]: ePM Status + * Bit[2]: Device Control Status + * Bit[3]: Acer Power Button Utility Status + * Bit[4]: RF Button Status + * Bit[5]: ODD PM Status + * Bit[6]: Device Default Value Control + * Bit[7]: Hall Sensor Application Status + */ +struct func_input_params { u8 function_num; /* Function Number */ u16 commun_devices; /* Communication type devices default status */ u16 devices; /* Other type devices default status */ - u8 lm_status; /* Launch Manager Status */ - u16 reserved; + u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */ + u8 app_mask; /* Bit mask to app_status */ + u8 reserved; } __attribute__((packed)); -struct lm_return_value { +struct func_return_value { u8 error_code; /* Error Code */ u8 ec_return_value; /* EC Return Value */ u16 reserved; @@ -1769,13 +1785,13 @@ static void acer_wmi_notify(u32 value, void *context) } static acpi_status __init -wmid3_set_lm_mode(struct lm_input_params *params, - struct lm_return_value *return_value) +wmid3_set_function_mode(struct func_input_params *params, + struct func_return_value *return_value) { acpi_status status; union acpi_object *obj; - struct acpi_buffer input = { sizeof(struct lm_input_params), params }; + struct acpi_buffer input = { sizeof(struct func_input_params), params }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); @@ -1796,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params, return AE_ERROR; } - *return_value = *((struct lm_return_value *)obj->buffer.pointer); + *return_value = *((struct func_return_value *)obj->buffer.pointer); kfree(obj); return status; @@ -1804,16 +1820,17 @@ wmid3_set_lm_mode(struct lm_input_params *params, static int __init acer_wmi_enable_ec_raw(void) { - struct lm_return_value return_value; + struct func_return_value return_value; acpi_status status; - struct lm_input_params params = { + struct func_input_params params = { .function_num = 0x1, .commun_devices = 0xFFFF, .devices = 0xFFFF, - .lm_status = 0x00, /* Launch Manager Deactive */ + .app_status = 0x00, /* Launch Manager Deactive */ + .app_mask = 0x01, }; - status = wmid3_set_lm_mode(¶ms, &return_value); + status = wmid3_set_function_mode(¶ms, &return_value); if (return_value.error_code || return_value.ec_return_value) pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n", @@ -1827,16 +1844,17 @@ static int __init acer_wmi_enable_ec_raw(void) static int __init acer_wmi_enable_lm(void) { - struct lm_return_value return_value; + struct func_return_value return_value; acpi_status status; - struct lm_input_params params = { + struct func_input_params params = { .function_num = 0x1, .commun_devices = 0xFFFF, .devices = 0xFFFF, - .lm_status = 0x01, /* Launch Manager Active */ + .app_status = 0x01, /* Launch Manager Active */ + .app_mask = 0x01, }; - status = wmid3_set_lm_mode(¶ms, &return_value); + status = wmid3_set_function_mode(¶ms, &return_value); if (return_value.error_code || return_value.ec_return_value) pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n", @@ -1846,11 +1864,46 @@ static int __init acer_wmi_enable_lm(void) return status; } +static int __init acer_wmi_enable_rf_button(void) +{ + struct func_return_value return_value; + acpi_status status; + struct func_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .app_status = 0x10, /* RF Button Active */ + .app_mask = 0x10, + }; + + status = wmid3_set_function_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n", + return_value.error_code, + return_value.ec_return_value); + + return status; +} + +#define ACER_WMID_ACCEL_HID "BST0001" + static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, void *ctx, void **retval) { + struct acpi_device *dev; + + if (!strcmp(ctx, "SENR")) { + if (acpi_bus_get_device(ah, &dev)) + return AE_OK; + if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) + return AE_OK; + } else + return AE_OK; + *(acpi_handle *)retval = ah; - return AE_OK; + + return AE_CTRL_TERMINATE; } static int __init acer_wmi_get_handle(const char *name, const char *prop, @@ -1877,7 +1930,7 @@ static int __init acer_wmi_accel_setup(void) { int err; - err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle); + err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); if (err) return err; @@ -2216,6 +2269,9 @@ static int __init acer_wmi_init(void) interface->capability &= ~ACER_CAP_BRIGHTNESS; if (wmi_has_guid(WMID_GUID3)) { + if (ACPI_FAILURE(acer_wmi_enable_rf_button())) + pr_warn("Cannot enable RF Button Driver\n"); + if (ec_raw_mode) { if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { pr_err("Cannot enable EC raw mode\n"); @@ -2233,10 +2289,11 @@ static int __init acer_wmi_init(void) err = acer_wmi_input_setup(); if (err) return err; + err = acer_wmi_accel_setup(); + if (err) + return err; } - acer_wmi_accel_setup(); - err = platform_driver_register(&acer_platform_driver); if (err) { pr_err("Unable to register platform driver\n"); diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 005629447b0c..d6b34923fb4e 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dmi.h> -#include <linux/acpi.h> #include <linux/leds.h> #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 9f31bc1a47d0..f3796164329e 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -17,19 +17,41 @@ #include <linux/pci_ids.h> #include <linux/leds.h> -#define ASUS_WIRELESS_LED_STATUS 0x2 -#define ASUS_WIRELESS_LED_OFF 0x4 -#define ASUS_WIRELESS_LED_ON 0x5 +struct hswc_params { + u8 on; + u8 off; + u8 status; +}; struct asus_wireless_data { struct input_dev *idev; struct acpi_device *adev; + const struct hswc_params *hswc_params; struct workqueue_struct *wq; struct work_struct led_work; struct led_classdev led; int led_state; }; +static const struct hswc_params atk4001_id_params = { + .on = 0x0, + .off = 0x1, + .status = 0x2, +}; + +static const struct hswc_params atk4002_id_params = { + .on = 0x5, + .off = 0x4, + .status = 0x2, +}; + +static const struct acpi_device_id device_ids[] = { + {"ATK4001", (kernel_ulong_t)&atk4001_id_params}, + {"ATK4002", (kernel_ulong_t)&atk4002_id_params}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, device_ids); + static u64 asus_wireless_method(acpi_handle handle, const char *method, int param) { @@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led) data = container_of(led, struct asus_wireless_data, led); s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC", - ASUS_WIRELESS_LED_STATUS); - if (s == ASUS_WIRELESS_LED_ON) + data->hswc_params->status); + if (s == data->hswc_params->on) return LED_FULL; return LED_OFF; } @@ -76,14 +98,13 @@ static void led_state_update(struct work_struct *work) data->led_state); } -static void led_state_set(struct led_classdev *led, - enum led_brightness value) +static void led_state_set(struct led_classdev *led, enum led_brightness value) { struct asus_wireless_data *data; data = container_of(led, struct asus_wireless_data, led); - data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF : - ASUS_WIRELESS_LED_ON; + data->led_state = value == LED_OFF ? data->hswc_params->off : + data->hswc_params->on; queue_work(data->wq, &data->led_work); } @@ -104,12 +125,14 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event) static int asus_wireless_add(struct acpi_device *adev) { struct asus_wireless_data *data; + const struct acpi_device_id *id; int err; data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; adev->driver_data = data; + data->adev = adev; data->idev = devm_input_allocate_device(&adev->dev); if (!data->idev) @@ -124,7 +147,16 @@ static int asus_wireless_add(struct acpi_device *adev) if (err) return err; - data->adev = adev; + for (id = device_ids; id->id[0]; id++) { + if (!strcmp((char *) id->id, acpi_device_hid(adev))) { + data->hswc_params = + (const struct hswc_params *)id->driver_data; + break; + } + } + if (!data->hswc_params) + return 0; + data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); if (!data->wq) return -ENOMEM; @@ -137,6 +169,7 @@ static int asus_wireless_add(struct acpi_device *adev) err = devm_led_classdev_register(&adev->dev, &data->led); if (err) destroy_workqueue(data->wq); + return err; } @@ -149,13 +182,6 @@ static int asus_wireless_remove(struct acpi_device *adev) return 0; } -static const struct acpi_device_id device_ids[] = { - {"ATK4001", 0}, - {"ATK4002", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, device_ids); - static struct acpi_driver asus_wireless_driver = { .name = "Asus Wireless Radio Control Driver", .class = "hotkey", diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 14392a01ab36..f57dd282a002 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -106,6 +106,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = { }, }, { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ + }, + }, + { .ident = "Dell Computer Corporation", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 82d67715ce76..2b218b1d13e5 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -202,6 +202,7 @@ static int radio_led_set(struct led_classdev *cdev, static struct led_classdev radio_led = { .name = "fujitsu::radio_led", + .default_trigger = "rfkill-any", .brightness_get = radio_led_get, .brightness_set_blocking = radio_led_set }; @@ -270,15 +271,20 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) static int logolamp_set(struct led_classdev *cdev, enum led_brightness brightness) { - if (brightness >= LED_FULL) { - call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); - } else if (brightness >= LED_HALF) { - call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); - } else { - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); - } + int poweron = FUNC_LED_ON, always = FUNC_LED_ON; + int ret; + + if (brightness < LED_HALF) + poweron = FUNC_LED_OFF; + + if (brightness < LED_FULL) + always = FUNC_LED_OFF; + + ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); + if (ret < 0) + return ret; + + return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); } static int kblamps_set(struct led_classdev *cdev, @@ -313,17 +319,17 @@ static int eco_led_set(struct led_classdev *cdev, static enum led_brightness logolamp_get(struct led_classdev *cdev) { - enum led_brightness brightness = LED_OFF; - int poweron, always; - - poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); - if (poweron == FUNC_LED_ON) { - brightness = LED_HALF; - always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); - if (always == FUNC_LED_ON) - brightness = LED_FULL; - } - return brightness; + int ret; + + ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); + if (ret == FUNC_LED_ON) + return LED_FULL; + + ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); + if (ret == FUNC_LED_ON) + return LED_HALF; + + return LED_OFF; } static enum led_brightness kblamps_get(struct led_classdev *cdev) @@ -1029,107 +1035,117 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) return 0; } +static void acpi_fujitsu_hotkey_press(int keycode) +{ + struct input_dev *input = fujitsu_hotkey->input; + int status; + + status = kfifo_in_locked(&fujitsu_hotkey->fifo, + (unsigned char *)&keycode, sizeof(keycode), + &fujitsu_hotkey->fifo_lock); + if (status != sizeof(keycode)) { + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Could not push keycode [0x%x]\n", keycode); + return; + } + input_report_key(input, keycode, 1); + input_sync(input); + vdbg_printk(FUJLAPTOP_DBG_TRACE, + "Push keycode into ringbuffer [%d]\n", keycode); +} + +static void acpi_fujitsu_hotkey_release(void) +{ + struct input_dev *input = fujitsu_hotkey->input; + int keycode, status; + + while (true) { + status = kfifo_out_locked(&fujitsu_hotkey->fifo, + (unsigned char *)&keycode, + sizeof(keycode), + &fujitsu_hotkey->fifo_lock); + if (status != sizeof(keycode)) + return; + input_report_key(input, keycode, 0); + input_sync(input); + vdbg_printk(FUJLAPTOP_DBG_TRACE, + "Pop keycode from ringbuffer [%d]\n", keycode); + } +} + static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) { struct input_dev *input; - int keycode, keycode_r; + int keycode; unsigned int irb = 1; - int i, status; + int i; input = fujitsu_hotkey->input; + if (event != ACPI_FUJITSU_NOTIFY_CODE1) { + keycode = KEY_UNKNOWN; + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Unsupported event [0x%x]\n", event); + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + return; + } + if (fujitsu_hotkey->rfkill_supported) fujitsu_hotkey->rfkill_state = call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); - switch (event) { - case ACPI_FUJITSU_NOTIFY_CODE1: - i = 0; - while ((irb = - call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 - && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { - switch (irb & 0x4ff) { - case KEY1_CODE: - keycode = fujitsu->keycode1; - break; - case KEY2_CODE: - keycode = fujitsu->keycode2; - break; - case KEY3_CODE: - keycode = fujitsu->keycode3; - break; - case KEY4_CODE: - keycode = fujitsu->keycode4; - break; - case KEY5_CODE: - keycode = fujitsu->keycode5; - break; - case 0: - keycode = 0; - break; - default: - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unknown GIRB result [%x]\n", irb); - keycode = -1; - break; - } - if (keycode > 0) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Push keycode into ringbuffer [%d]\n", - keycode); - status = kfifo_in_locked(&fujitsu_hotkey->fifo, - (unsigned char *)&keycode, - sizeof(keycode), - &fujitsu_hotkey->fifo_lock); - if (status != sizeof(keycode)) { - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Could not push keycode [0x%x]\n", - keycode); - } else { - input_report_key(input, keycode, 1); - input_sync(input); - } - } else if (keycode == 0) { - while ((status = - kfifo_out_locked( - &fujitsu_hotkey->fifo, - (unsigned char *) &keycode_r, - sizeof(keycode_r), - &fujitsu_hotkey->fifo_lock)) - == sizeof(keycode_r)) { - input_report_key(input, keycode_r, 0); - input_sync(input); - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Pop keycode from ringbuffer [%d]\n", - keycode_r); - } - } + i = 0; + while ((irb = + call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { + switch (irb & 0x4ff) { + case KEY1_CODE: + keycode = fujitsu->keycode1; + break; + case KEY2_CODE: + keycode = fujitsu->keycode2; + break; + case KEY3_CODE: + keycode = fujitsu->keycode3; + break; + case KEY4_CODE: + keycode = fujitsu->keycode4; + break; + case KEY5_CODE: + keycode = fujitsu->keycode5; + break; + case 0: + keycode = 0; + break; + default: + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Unknown GIRB result [%x]\n", irb); + keycode = -1; + break; } - /* On some models (first seen on the Skylake-based Lifebook - * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is - * handled in software; its state is queried using FUNC_RFKILL - */ - if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && - (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { - keycode = KEY_TOUCHPAD_TOGGLE; - input_report_key(input, keycode, 1); - input_sync(input); - input_report_key(input, keycode, 0); - input_sync(input); - } + if (keycode > 0) + acpi_fujitsu_hotkey_press(keycode); + else if (keycode == 0) + acpi_fujitsu_hotkey_release(); + } - break; - default: - keycode = KEY_UNKNOWN; - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unsupported event [0x%x]\n", event); + /* On some models (first seen on the Skylake-based Lifebook + * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is + * handled in software; its state is queried using FUNC_RFKILL + */ + if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && + (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { + keycode = KEY_TOUCHPAD_TOGGLE; input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); - break; } + } /* Initialization */ diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 09356684c32f..493d8910a74e 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -251,6 +251,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index cb3ab2b212b1..bcf438f38781 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -1,5 +1,5 @@ /* - * Intel HID event driver for Windows 8 + * Intel HID event & 5 button array driver * * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> @@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = { { KE_END }, }; +/* 5 button array notification value. */ +static const struct key_entry intel_array_keymap[] = { + { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */ + { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */ + { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */ + { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ + { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ + { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ + { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ + { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ + { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ + { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ + { KE_END }, +}; + struct intel_hid_priv { struct input_dev *input_dev; + struct input_dev *array; }; static int intel_hid_set_enable(struct device *device, int enable) @@ -78,15 +94,43 @@ static int intel_hid_set_enable(struct device *device, int enable) return 0; } +static void intel_button_array_enable(struct device *device, bool enable) +{ + struct intel_hid_priv *priv = dev_get_drvdata(device); + acpi_handle handle = ACPI_HANDLE(device); + unsigned long long button_cap; + acpi_status status; + + if (!priv->array) + return; + + /* Query supported platform features */ + status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap); + if (ACPI_FAILURE(status)) { + dev_warn(device, "failed to get button capability\n"); + return; + } + + /* Enable|disable features - power button is always enabled */ + status = acpi_execute_simple_method(handle, "BTNE", + enable ? button_cap : 1); + if (ACPI_FAILURE(status)) + dev_warn(device, "failed to set button capability\n"); +} + static int intel_hid_pl_suspend_handler(struct device *device) { intel_hid_set_enable(device, 0); + intel_button_array_enable(device, false); + return 0; } static int intel_hid_pl_resume_handler(struct device *device) { intel_hid_set_enable(device, 1); + intel_button_array_enable(device, true); + return 0; } @@ -126,6 +170,27 @@ err_free_device: return ret; } +static int intel_button_array_input_setup(struct platform_device *device) +{ + struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); + int ret; + + /* Setup input device for 5 button array */ + priv->array = devm_input_allocate_device(&device->dev); + if (!priv->array) + return -ENOMEM; + + ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL); + if (ret) + return ret; + + priv->array->dev.parent = &device->dev; + priv->array->name = "Intel HID 5 button array"; + priv->array->id.bustype = BUS_HOST; + + return input_register_device(priv->array); +} + static void intel_hid_input_destroy(struct platform_device *device) { struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); @@ -140,10 +205,11 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) unsigned long long ev_index; acpi_status status; - /* The platform spec only defines one event code: 0xC0. */ + /* 0xC0 is for HID events, other values are for 5 button array */ if (event != 0xc0) { - dev_warn(&device->dev, "received unknown event (0x%x)\n", - event); + if (!priv->array || + !sparse_keymap_report_event(priv->array, event, 1, true)) + dev_info(&device->dev, "unknown event 0x%x\n", event); return; } @@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) static int intel_hid_probe(struct platform_device *device) { acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long event_cap, mode; struct intel_hid_priv *priv; - unsigned long long mode; acpi_status status; int err; @@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device) return err; } + /* Setup 5 button array */ + status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap); + if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) { + dev_info(&device->dev, "platform supports 5 button array\n"); + err = intel_button_array_input_setup(device); + if (err) + pr_err("Failed to setup Intel 5 button array hotkeys\n"); + } + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler, @@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device) if (err) goto err_remove_notify; + if (priv->array) { + intel_button_array_enable(&device->dev, true); + + /* Call button load method to enable HID power button */ + status = acpi_evaluate_object(handle, "BTNL", NULL, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&device->dev, + "failed to enable HID power button\n"); + } + return 0; err_remove_notify: @@ -224,6 +309,7 @@ static int intel_hid_remove(struct platform_device *device) acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); intel_hid_input_destroy(device); intel_hid_set_enable(&device->dev, 0); + intel_button_array_enable(&device->dev, false); /* * Even if we failed to shut off the event stream, we can still diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 361770568ad0..871cfa682519 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -1,7 +1,10 @@ /* - * Power button driver for Medfield. + * Power button driver for Intel MID platforms. * - * Copyright (C) 2010 Intel Corp + * Copyright (C) 2010,2017 Intel Corp + * + * Author: Hong Liu <hong.liu@intel.com> + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,20 +14,20 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <linux/module.h> #include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/platform_device.h> #include <linux/input.h> +#include <linux/interrupt.h> #include <linux/mfd/intel_msic.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/pm_wakeirq.h> +#include <linux/slab.h> + +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> +#include <asm/intel_scu_ipc.h> #define DRIVER_NAME "msic_power_btn" @@ -36,37 +39,113 @@ */ #define MSIC_PWRBTNM (1 << 0) -static irqreturn_t mfld_pb_isr(int irq, void *dev_id) +/* Intel Tangier */ +#define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */ + +/* Basin Cove PMIC */ +#define BCOVE_PBIRQ 0x02 +#define BCOVE_IRQLVL1MSK 0x0c +#define BCOVE_PBIRQMASK 0x0d +#define BCOVE_PBSTATUS 0x27 + +struct mid_pb_ddata { + struct device *dev; + int irq; + struct input_dev *input; + unsigned short mirqlvl1_addr; + unsigned short pbstat_addr; + u8 pbstat_mask; + int (*setup)(struct mid_pb_ddata *ddata); +}; + +static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) { - struct input_dev *input = dev_id; + struct input_dev *input = ddata->input; int ret; u8 pbstat; - ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat); + ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat); + if (ret) + return ret; + dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); + *value = !(pbstat & ddata->pbstat_mask); + return 0; +} + +static int mid_irq_ack(struct mid_pb_ddata *ddata) +{ + return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM); +} + +static int mrfld_setup(struct mid_pb_ddata *ddata) +{ + /* Unmask the PBIRQ and MPBIRQ on Tangier */ + intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); + intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); + + return 0; +} + +static irqreturn_t mid_pb_isr(int irq, void *dev_id) +{ + struct mid_pb_ddata *ddata = dev_id; + struct input_dev *input = ddata->input; + int value = 0; + int ret; + + ret = mid_pbstat(ddata, &value); if (ret < 0) { - dev_err(input->dev.parent, "Read error %d while reading" - " MSIC_PB_STATUS\n", ret); + dev_err(input->dev.parent, + "Read error %d while reading MSIC_PB_STATUS\n", ret); } else { - input_event(input, EV_KEY, KEY_POWER, - !(pbstat & MSIC_PB_LEVEL)); + input_event(input, EV_KEY, KEY_POWER, value); input_sync(input); } + mid_irq_ack(ddata); return IRQ_HANDLED; } -static int mfld_pb_probe(struct platform_device *pdev) +static struct mid_pb_ddata mfld_ddata = { + .mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK, + .pbstat_addr = INTEL_MSIC_PBSTATUS, + .pbstat_mask = MSIC_PB_LEVEL, +}; + +static struct mid_pb_ddata mrfld_ddata = { + .mirqlvl1_addr = BCOVE_IRQLVL1MSK, + .pbstat_addr = BCOVE_PBSTATUS, + .pbstat_mask = BCOVE_PB_LEVEL, + .setup = mrfld_setup, +}; + +#define ICPU(model, ddata) \ + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } + +static const struct x86_cpu_id mid_pb_cpu_ids[] = { + ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata), + ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata), + {} +}; + +static int mid_pb_probe(struct platform_device *pdev) { + const struct x86_cpu_id *id; + struct mid_pb_ddata *ddata; struct input_dev *input; int irq = platform_get_irq(pdev, 0); int error; + id = x86_match_cpu(mid_pb_cpu_ids); + if (!id) + return -ENODEV; + if (irq < 0) return -EINVAL; - input = input_allocate_device(); + input = devm_input_allocate_device(&pdev->dev); if (!input) return -ENOMEM; @@ -77,25 +156,36 @@ static int mfld_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT, - DRIVER_NAME, input); - if (error) { - dev_err(&pdev->dev, "Unable to request irq %d for mfld power" - "button\n", irq); - goto err_free_input; + ddata = (struct mid_pb_ddata *)id->driver_data; + if (!ddata) + return -ENODATA; + + ddata->dev = &pdev->dev; + ddata->irq = irq; + ddata->input = input; + + if (ddata->setup) { + error = ddata->setup(ddata); + if (error) + return error; } - device_init_wakeup(&pdev->dev, true); - dev_pm_set_wake_irq(&pdev->dev, irq); + error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr, + IRQF_ONESHOT, DRIVER_NAME, ddata); + if (error) { + dev_err(&pdev->dev, + "Unable to request irq %d for MID power button\n", irq); + return error; + } error = input_register_device(input); if (error) { - dev_err(&pdev->dev, "Unable to register input dev, error " - "%d\n", error); - goto err_free_irq; + dev_err(&pdev->dev, + "Unable to register input dev, error %d\n", error); + return error; } - platform_set_drvdata(pdev, input); + platform_set_drvdata(pdev, ddata); /* * SCU firmware might send power button interrupts to IA core before @@ -107,46 +197,39 @@ static int mfld_pb_probe(struct platform_device *pdev) * initialization. The race happens rarely. So we needn't worry * about it. */ - error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM); + error = mid_irq_ack(ddata); if (error) { - dev_err(&pdev->dev, "Unable to clear power button interrupt, " - "error: %d\n", error); - goto err_free_irq; + dev_err(&pdev->dev, + "Unable to clear power button interrupt, error: %d\n", + error); + return error; } - return 0; + device_init_wakeup(&pdev->dev, true); + dev_pm_set_wake_irq(&pdev->dev, irq); -err_free_irq: - free_irq(irq, input); -err_free_input: - input_free_device(input); - return error; + return 0; } -static int mfld_pb_remove(struct platform_device *pdev) +static int mid_pb_remove(struct platform_device *pdev) { - struct input_dev *input = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); - free_irq(irq, input); - input_unregister_device(input); return 0; } -static struct platform_driver mfld_pb_driver = { +static struct platform_driver mid_pb_driver = { .driver = { .name = DRIVER_NAME, }, - .probe = mfld_pb_probe, - .remove = mfld_pb_remove, + .probe = mid_pb_probe, + .remove = mid_pb_remove, }; -module_platform_driver(mfld_pb_driver); +module_platform_driver(mid_pb_driver); MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); -MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); +MODULE_DESCRIPTION("Intel MID Power Button Driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 0df3c9d37509..008a76903cbf 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -549,9 +549,9 @@ static int mid_thermal_remove(struct platform_device *pdev) static const struct platform_device_id therm_id_table[] = { { DRIVER_NAME, 1 }, - { "msic_thermal", 1 }, { } }; +MODULE_DEVICE_TABLE(platform, therm_id_table); static struct platform_driver mid_thermal_driver = { .driver = { diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index b130b8c9b9d7..914bcd2edbde 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -188,8 +188,7 @@ static int pmc_core_check_read_lock_bit(void) u32 value; value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET); - return test_bit(SPT_PMC_READ_DISABLE_BIT, - (unsigned long *)&value); + return value & BIT(SPT_PMC_READ_DISABLE_BIT); } #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -238,8 +237,7 @@ static int pmc_core_mtpmc_link_status(void) u32 value; value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET); - return test_bit(SPT_PMC_MSG_FULL_STS_BIT, - (unsigned long *)&value); + return value & BIT(SPT_PMC_MSG_FULL_STS_BIT); } static int pmc_core_send_msg(u32 *addr_xram) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 0bf51d574fa9..0651d47b8eeb 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -32,7 +32,10 @@ #include <linux/notifier.h> #include <linux/suspend.h> #include <linux/acpi.h> +#include <linux/io-64-nonatomic-lo-hi.h> + #include <asm/intel_pmc_ipc.h> + #include <linux/platform_data/itco_wdt.h> /* @@ -54,6 +57,18 @@ #define IPC_WRITE_BUFFER 0x80 #define IPC_READ_BUFFER 0x90 +/* PMC Global Control Registers */ +#define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078 +#define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080 + +/* Residency with clock rate at 19.2MHz to usecs */ +#define S0IX_RESIDENCY_IN_USECS(d, s) \ +({ \ + u64 result = 10ull * ((d) + (s)); \ + do_div(result, 192); \ + result; \ +}) + /* * 16-byte buffer for sending data associated with IPC command. */ @@ -68,7 +83,7 @@ #define PLAT_RESOURCE_IPC_INDEX 0 #define PLAT_RESOURCE_IPC_SIZE 0x1000 #define PLAT_RESOURCE_GCR_OFFSET 0x1008 -#define PLAT_RESOURCE_GCR_SIZE 0x4 +#define PLAT_RESOURCE_GCR_SIZE 0x1000 #define PLAT_RESOURCE_BIOS_DATA_INDEX 1 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 #define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 @@ -97,8 +112,6 @@ #define TCO_PMC_OFFSET 0x8 #define TCO_PMC_SIZE 0x4 -static const int iTCO_version = 3; - static struct intel_pmc_ipc_dev { struct device *dev; void __iomem *ipc_base; @@ -115,6 +128,7 @@ static struct intel_pmc_ipc_dev { /* gcr */ resource_size_t gcr_base; int gcr_size; + bool has_gcr_regs; /* punit */ struct platform_device *punit_dev; @@ -180,6 +194,11 @@ static inline u32 ipc_data_readl(u32 offset) return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); } +static inline u64 gcr_data_readq(u32 offset) +{ + return readq(ipcdev.ipc_base + offset); +} + static int intel_pmc_ipc_check_status(void) { int status; @@ -389,6 +408,7 @@ static void ipc_pci_remove(struct pci_dev *pdev) static const struct pci_device_id ipc_pci_ids[] = { {PCI_VDEVICE(INTEL, 0x0a94), 0}, {PCI_VDEVICE(INTEL, 0x1a94), 0}, + {PCI_VDEVICE(INTEL, 0x5a94), 0}, { 0,} }; MODULE_DEVICE_TABLE(pci, ipc_pci_ids); @@ -712,7 +732,8 @@ static int ipc_plat_get_res(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get ipc resource\n"); return -ENXIO; } - size = PLAT_RESOURCE_IPC_SIZE; + size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE; + if (!request_mem_region(res->start, size, pdev->name)) { dev_err(&pdev->dev, "Failed to request ipc resource\n"); return -EBUSY; @@ -748,6 +769,28 @@ static int ipc_plat_get_res(struct platform_device *pdev) return 0; } +/** + * intel_pmc_s0ix_counter_read() - Read S0ix residency. + * @data: Out param that contains current S0ix residency count. + * + * Return: an error code or 0 on success. + */ +int intel_pmc_s0ix_counter_read(u64 *data) +{ + u64 deep, shlw; + + if (!ipcdev.has_gcr_regs) + return -EACCES; + + deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET); + shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET); + + *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); + #ifdef CONFIG_ACPI static const struct acpi_device_id ipc_acpi_ids[] = { { "INT34D2", 0}, @@ -797,6 +840,8 @@ static int ipc_plat_probe(struct platform_device *pdev) goto err_sys; } + ipcdev.has_gcr_regs = true; + return 0; err_sys: free_irq(ipcdev.irq, &ipcdev); @@ -808,8 +853,11 @@ err_device: iounmap(ipcdev.ipc_base); res = platform_get_resource(pdev, IORESOURCE_MEM, PLAT_RESOURCE_IPC_INDEX); - if (res) - release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); + if (res) { + release_mem_region(res->start, + PLAT_RESOURCE_IPC_SIZE + + PLAT_RESOURCE_GCR_SIZE); + } return ret; } @@ -825,8 +873,11 @@ static int ipc_plat_remove(struct platform_device *pdev) iounmap(ipcdev.ipc_base); res = platform_get_resource(pdev, IORESOURCE_MEM, PLAT_RESOURCE_IPC_INDEX); - if (res) - release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); + if (res) { + release_mem_region(res->start, + PLAT_RESOURCE_IPC_SIZE + + PLAT_RESOURCE_GCR_SIZE); + } ipcdev.dev = NULL; return 0; } diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c new file mode 100644 index 000000000000..4f60d8e32a0a --- /dev/null +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -0,0 +1,151 @@ +/* + * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver + * Copyright (c) 2017, Intel Corporation. + * All rights reserved. + * + * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/topology.h> +#include <linux/workqueue.h> +#include <linux/cpuhotplug.h> +#include <linux/cpufeature.h> +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> + +#define MSR_OC_MAILBOX 0x150 +#define MSR_OC_MAILBOX_CMD_OFFSET 32 +#define MSR_OC_MAILBOX_RSP_OFFSET 32 +#define MSR_OC_MAILBOX_BUSY_BIT 63 +#define OC_MAILBOX_FC_CONTROL_CMD 0x1C + +/* + * Typical latency to get mail box response is ~3us, It takes +3 us to + * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz + * system. So for most of the time, the first mailbox read should have the + * response, but to avoid some boundary cases retry twice. + */ +#define OC_MAILBOX_RETRY_COUNT 2 + +static int get_oc_core_priority(unsigned int cpu) +{ + u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD; + int ret, i; + + /* Issue favored core read command */ + value = cmd << MSR_OC_MAILBOX_CMD_OFFSET; + /* Set the busy bit to indicate OS is trying to issue command */ + value |= BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT); + ret = wrmsrl_safe(MSR_OC_MAILBOX, value); + if (ret) { + pr_debug("cpu %d OC mailbox write failed\n", cpu); + return ret; + } + + for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) { + ret = rdmsrl_safe(MSR_OC_MAILBOX, &value); + if (ret) { + pr_debug("cpu %d OC mailbox read failed\n", cpu); + break; + } + + if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) { + pr_debug("cpu %d OC mailbox still processing\n", cpu); + ret = -EBUSY; + continue; + } + + if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) { + pr_debug("cpu %d OC mailbox cmd failed\n", cpu); + ret = -ENXIO; + break; + } + + ret = value & 0xff; + pr_debug("cpu %d max_ratio %d\n", cpu, ret); + break; + } + + return ret; +} + +/* + * The work item is needed to avoid CPU hotplug locking issues. The function + * itmt_legacy_set_priority() is called from CPU online callback, so can't + * call sched_set_itmt_support() from there as this function will aquire + * hotplug locks in its path. + */ +static void itmt_legacy_work_fn(struct work_struct *work) +{ + sched_set_itmt_support(); +} + +static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn); + +static int itmt_legacy_cpu_online(unsigned int cpu) +{ + static u32 max_highest_perf = 0, min_highest_perf = U32_MAX; + int priority; + + priority = get_oc_core_priority(cpu); + if (priority < 0) + return 0; + + sched_set_itmt_core_prio(priority, cpu); + + /* Enable ITMT feature when a core with different priority is found */ + if (max_highest_perf <= min_highest_perf) { + if (priority > max_highest_perf) + max_highest_perf = priority; + + if (priority < min_highest_perf) + min_highest_perf = priority; + + if (max_highest_perf > min_highest_perf) + schedule_work(&sched_itmt_work); + } + + return 0; +} + +#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } + +static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { + ICPU(INTEL_FAM6_BROADWELL_X), + {} +}; + +static int __init itmt_legacy_init(void) +{ + const struct x86_cpu_id *id; + int ret; + + id = x86_match_cpu(itmt_legacy_cpu_ids); + if (!id) + return -ENODEV; + + if (boot_cpu_has(X86_FEATURE_HWP)) + return -ENODEV; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "platform/x86/turbo_max_3:online", + itmt_legacy_cpu_online, NULL); + if (ret < 0) + return ret; + + return 0; +} +late_initcall(itmt_legacy_init) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 25f15df5c2d7..8f98c211b440 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -45,6 +45,10 @@ /* LPC bus IO offsets */ #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 +#define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR 0x253a +#define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558 +#define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564 +#define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588 #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda @@ -56,6 +60,17 @@ MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) +/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ +#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08 +#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08 +#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40 +#define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \ + MLXPLAT_CPLD_AGGR_FAN_MASK_DEF) +#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04 +#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) +#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) +#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) + /* Start channel numbers */ #define MLXPLAT_CPLD_CH1 2 #define MLXPLAT_CPLD_CH2 10 @@ -123,7 +138,7 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { }; /* Platform hotplug devices */ -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = { { .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, .bus = 10, @@ -134,7 +149,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { }, }; -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = { { .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, .bus = 10, @@ -145,7 +160,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { }, }; -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = { { .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, .bus = 11, @@ -166,38 +181,38 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { /* Platform hotplug default data */ static -struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = { - .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), - .top_aggr_mask = 0x48, - .top_aggr_psu_mask = 0x08, - .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58), - .psu_mask = 0x03, - .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu), - .psu = mlxplat_mlxcpld_hotplug_psu, - .top_aggr_pwr_mask = 0x08, - .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), - .pwr_mask = 0x03, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), - .pwr = mlxplat_mlxcpld_hotplug_pwr, - .top_aggr_fan_mask = 0x40, - .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88), - .fan_mask = 0x0f, - .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan), - .fan = mlxplat_mlxcpld_hotplug_fan, +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data = { + .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, + .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF, + .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, + .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR, + .psu_mask = MLXPLAT_CPLD_PSU_MASK, + .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu), + .psu = mlxplat_mlxcpld_psu, + .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, + .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, + .pwr_mask = MLXPLAT_CPLD_PWR_MASK, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), + .pwr = mlxplat_mlxcpld_pwr, + .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, + .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR, + .fan_mask = MLXPLAT_CPLD_FAN_MASK, + .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan), + .fan = mlxplat_mlxcpld_fan, }; /* Platform hotplug MSN21xx system family data */ static -struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = { - .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), - .top_aggr_mask = 0x04, - .top_aggr_pwr_mask = 0x04, - .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), - .pwr_mask = 0x03, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { + .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, + .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, + .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, + .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, + .pwr_mask = MLXPLAT_CPLD_PWR_MASK, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), }; -static struct resource mlxplat_mlxcpld_hotplug_resources[] = { +static struct resource mlxplat_mlxcpld_resources[] = { [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), }; @@ -213,7 +228,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_default_channels[i]); } - mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data; + mlxplat_hotplug = &mlxplat_mlxcpld_default_data; return 1; }; @@ -227,7 +242,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_msn21xx_channels); } - mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data; + mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; return 1; }; @@ -314,9 +329,10 @@ static int __init mlxplat_init(void) } priv->pdev_hotplug = platform_device_register_resndata( - &mlxplat_dev->dev, "mlxcpld-hotplug", -1, - mlxplat_mlxcpld_hotplug_resources, - ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources), + &mlxplat_dev->dev, "mlxcpld-hotplug", + PLATFORM_DEVID_NONE, + mlxplat_mlxcpld_resources, + ARRAY_SIZE(mlxplat_mlxcpld_resources), mlxplat_hotplug, sizeof(*mlxplat_hotplug)); if (IS_ERR(priv->pdev_hotplug)) { err = PTR_ERR(priv->pdev_hotplug); diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c new file mode 100644 index 000000000000..02e11fdbf375 --- /dev/null +++ b/drivers/platform/x86/silead_dmi.c @@ -0,0 +1,136 @@ +/* + * Silead touchscreen driver DMI based configuration code + * + * Copyright (c) 2017 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/notifier.h> +#include <linux/property.h> +#include <linux/string.h> + +struct silead_ts_dmi_data { + const char *acpi_name; + struct property_entry *properties; +}; + +static struct property_entry cube_iwork8_air_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), + PROPERTY_ENTRY_U32("touchscreen-size-y", 900), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct silead_ts_dmi_data cube_iwork8_air_data = { + .acpi_name = "MSSL1680:00", + .properties = cube_iwork8_air_props, +}; + +static struct property_entry jumper_ezpad_mini3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = { + .acpi_name = "MSSL1680:00", + .properties = jumper_ezpad_mini3_props, +}; + +static const struct dmi_system_id silead_ts_dmi_table[] = { + { + /* CUBE iwork8 Air */ + .driver_data = (void *)&cube_iwork8_air_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "cube"), + DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + }, + { + /* Jumper EZpad mini3 */ + .driver_data = (void *)&jumper_ezpad_mini3_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + /* jumperx.T87.KFBNEEA02 with the version-nr dropped */ + DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"), + }, + }, + { }, +}; + +static void silead_ts_dmi_add_props(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + const struct dmi_system_id *dmi_id; + const struct silead_ts_dmi_data *ts_data; + int error; + + dmi_id = dmi_first_match(silead_ts_dmi_table); + if (!dmi_id) + return; + + ts_data = dmi_id->driver_data; + if (has_acpi_companion(dev) && + !strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) { + error = device_add_properties(dev, ts_data->properties); + if (error) + dev_err(dev, "failed to add properties: %d\n", error); + } +} + +static int silead_ts_dmi_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + silead_ts_dmi_add_props(dev); + break; + + default: + break; + } + + return 0; +} + +static struct notifier_block silead_ts_dmi_notifier = { + .notifier_call = silead_ts_dmi_notifier_call, +}; + +static int __init silead_ts_dmi_init(void) +{ + int error; + + error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier); + if (error) + pr_err("%s: failed to register i2c bus notifier: %d\n", + __func__, error); + + return error; +} + +/* + * We are registering out notifier after i2c core is initialized and i2c bus + * itself is ready (which happens at postcore initcall level), but before + * ACPI starts enumerating devices (at subsys initcall level). + */ +arch_initcall(silead_ts_dmi_init); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cacb43fb1df7..1d18b32628ec 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -163,6 +163,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ + TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */ TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ @@ -372,11 +373,9 @@ enum led_status_t { TPACPI_LED_BLINK, }; -/* Special LED class that can defer work */ +/* tpacpi LED class */ struct tpacpi_led_classdev { struct led_classdev led_classdev; - struct work_struct work; - enum led_status_t new_state; int led; }; @@ -1959,7 +1958,7 @@ enum { /* Positions of some of the keys in hotkey masks */ TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, - TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, + TP_ACPI_HKEY_KBD_LIGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, @@ -2344,7 +2343,7 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); } - if (m & TP_ACPI_HKEY_THNKLGHT_MASK) { + if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) { d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); } @@ -5084,18 +5083,27 @@ static struct ibm_struct video_driver_data = { * Keyboard backlight subdriver */ +static enum led_brightness kbdlight_brightness; +static DEFINE_MUTEX(kbdlight_mutex); + static int kbdlight_set_level(int level) { + int ret = 0; + if (!hkey_handle) return -ENXIO; + mutex_lock(&kbdlight_mutex); + if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) - return -EIO; + ret = -EIO; + else + kbdlight_brightness = level; - return 0; -} + mutex_unlock(&kbdlight_mutex); -static int kbdlight_set_level_and_update(int level); + return ret; +} static int kbdlight_get_level(void) { @@ -5158,24 +5166,10 @@ static bool kbdlight_is_supported(void) return status & BIT(9); } -static void kbdlight_set_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - kbdlight_set_level_and_update(data->new_state); -} - -static void kbdlight_sysfs_set(struct led_classdev *led_cdev, +static int kbdlight_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct tpacpi_led_classdev *data = - container_of(led_cdev, - struct tpacpi_led_classdev, - led_classdev); - data->new_state = brightness; - queue_work(tpacpi_wq, &data->work); + return kbdlight_set_level(brightness); } static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) @@ -5193,7 +5187,8 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = { .led_classdev = { .name = "tpacpi::kbd_backlight", .max_brightness = 2, - .brightness_set = &kbdlight_sysfs_set, + .flags = LED_BRIGHT_HW_CHANGED, + .brightness_set_blocking = &kbdlight_sysfs_set, .brightness_get = &kbdlight_sysfs_get, } }; @@ -5205,7 +5200,6 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); TPACPI_ACPIHANDLE_INIT(hkey); - INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); if (!kbdlight_is_supported()) { tp_features.kbdlight = 0; @@ -5213,6 +5207,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) return 1; } + kbdlight_brightness = kbdlight_sysfs_get(NULL); tp_features.kbdlight = 1; rc = led_classdev_register(&tpacpi_pdev->dev, @@ -5222,6 +5217,8 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) return rc; } + tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | + TP_ACPI_HKEY_KBD_LIGHT_MASK); return 0; } @@ -5229,7 +5226,6 @@ static void kbdlight_exit(void) { if (tp_features.kbdlight) led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); - flush_workqueue(tpacpi_wq); } static int kbdlight_set_level_and_update(int level) @@ -5358,25 +5354,11 @@ static int light_set_status(int status) return -ENXIO; } -static void light_set_status_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - light_set_status((data->new_state != TPACPI_LED_OFF)); -} - -static void light_sysfs_set(struct led_classdev *led_cdev, +static int light_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct tpacpi_led_classdev *data = - container_of(led_cdev, - struct tpacpi_led_classdev, - led_classdev); - data->new_state = (brightness != LED_OFF) ? - TPACPI_LED_ON : TPACPI_LED_OFF; - queue_work(tpacpi_wq, &data->work); + return light_set_status((brightness != LED_OFF) ? + TPACPI_LED_ON : TPACPI_LED_OFF); } static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) @@ -5387,7 +5369,7 @@ static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) static struct tpacpi_led_classdev tpacpi_led_thinklight = { .led_classdev = { .name = "tpacpi::thinklight", - .brightness_set = &light_sysfs_set, + .brightness_set_blocking = &light_sysfs_set, .brightness_get = &light_sysfs_get, } }; @@ -5403,7 +5385,6 @@ static int __init light_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(lght); } TPACPI_ACPIHANDLE_INIT(cmos); - INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; @@ -5437,7 +5418,6 @@ static int __init light_init(struct ibm_init_struct *iibm) static void light_exit(void) { led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); - flush_workqueue(tpacpi_wq); } static int light_read(struct seq_file *m) @@ -5704,29 +5684,21 @@ static int led_set_status(const unsigned int led, return rc; } -static void led_set_status_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - led_set_status(data->led, data->new_state); -} - -static void led_sysfs_set(struct led_classdev *led_cdev, +static int led_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct tpacpi_led_classdev *data = container_of(led_cdev, struct tpacpi_led_classdev, led_classdev); + enum led_status_t new_state; if (brightness == LED_OFF) - data->new_state = TPACPI_LED_OFF; + new_state = TPACPI_LED_OFF; else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK) - data->new_state = TPACPI_LED_ON; + new_state = TPACPI_LED_ON; else - data->new_state = TPACPI_LED_BLINK; + new_state = TPACPI_LED_BLINK; - queue_work(tpacpi_wq, &data->work); + return led_set_status(data->led, new_state); } static int led_sysfs_blink_set(struct led_classdev *led_cdev, @@ -5743,10 +5715,7 @@ static int led_sysfs_blink_set(struct led_classdev *led_cdev, } else if ((*delay_on != 500) || (*delay_off != 500)) return -EINVAL; - data->new_state = TPACPI_LED_BLINK; - queue_work(tpacpi_wq, &data->work); - - return 0; + return led_set_status(data->led, TPACPI_LED_BLINK); } static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) @@ -5775,7 +5744,6 @@ static void led_exit(void) led_classdev_unregister(&tpacpi_leds[i].led_classdev); } - flush_workqueue(tpacpi_wq); kfree(tpacpi_leds); } @@ -5789,7 +5757,7 @@ static int __init tpacpi_init_led(unsigned int led) if (!tpacpi_led_names[led]) return 0; - tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; + tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set; tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; if (led_supported == TPACPI_LED_570) tpacpi_leds[led].led_classdev.brightness_get = @@ -5797,8 +5765,6 @@ static int __init tpacpi_init_led(unsigned int led) tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; - INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker); - rc = led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev); if (rc < 0) @@ -9169,6 +9135,24 @@ static void tpacpi_driver_event(const unsigned int hkey_event) volume_alsa_notify_change(); } } + if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) { + enum led_brightness brightness; + + mutex_lock(&kbdlight_mutex); + + /* + * Check the brightness actually changed, setting the brightness + * through kbdlight_set_level() also triggers this event. + */ + brightness = kbdlight_sysfs_get(NULL); + if (kbdlight_brightness != brightness) { + kbdlight_brightness = brightness; + led_classdev_notify_brightness_hw_changed( + &tpacpi_led_kbdlight.led_classdev, brightness); + } + + mutex_unlock(&kbdlight_mutex); + } } static void hotkey_driver_event(const unsigned int scancode) |