From b116fd009a73fc83f05241152ea9300304e6826c Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Wed, 9 Sep 2015 11:28:19 -0600 Subject: [PATCH 01/18] toshiba_acpi: Unify hotkey enabling functions Currently the driver has two functions enabling hotkeys support, but these two functions can be merged into one. This patch merges these two functions, moving some checks to the *enable_hotkeys function, simplifying code in the process. Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- drivers/platform/x86/toshiba_acpi.c | 36 ++++++++++------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index f2372f400ddb..5510d3f593f4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -198,6 +198,7 @@ struct toshiba_acpi_dev { unsigned int panel_power_on_supported:1; unsigned int usb_three_supported:1; unsigned int sysfs_created:1; + unsigned int special_functions; bool kbd_led_registered; bool illumination_led_registered; @@ -2253,7 +2254,16 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev) if (ACPI_FAILURE(status)) return -ENODEV; - result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE); + /* + * Enable the "Special Functions" mode only if they are + * supported and if they are activated. + */ + if (dev->kbd_function_keys_supported && dev->special_functions) + result = hci_write(dev, HCI_HOTKEY_EVENT, + HCI_HOTKEY_SPECIAL_FUNCTIONS); + else + result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE); + if (result == TOS_FAILURE) return -EIO; else if (result == TOS_NOT_SUPPORTED) @@ -2262,20 +2272,6 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev) return 0; } -static void toshiba_acpi_enable_special_functions(struct toshiba_acpi_dev *dev) -{ - u32 result; - - /* - * Re-activate the hotkeys, but this time, we are using the - * "Special Functions" mode. - */ - result = hci_write(dev, HCI_HOTKEY_EVENT, - HCI_HOTKEY_SPECIAL_FUNCTIONS); - if (result != TOS_SUCCESS) - pr_err("Could not enable the Special Function mode\n"); -} - static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { @@ -2631,7 +2627,6 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev; const char *hci_method; - u32 special_functions; u32 dummy; int ret = 0; @@ -2673,7 +2668,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) * with the new keyboard layout, query for its presence to help * determine the keymap layout to use. */ - ret = toshiba_function_keys_get(dev, &special_functions); + ret = toshiba_function_keys_get(dev, &dev->special_functions); dev->kbd_function_keys_supported = !ret; if (toshiba_acpi_setup_keyboard(dev)) @@ -2748,13 +2743,6 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) print_supported_features(dev); - /* - * Enable the "Special Functions" mode only if they are - * supported and if they are activated. - */ - if (dev->kbd_function_keys_supported && special_functions) - toshiba_acpi_enable_special_functions(dev); - ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, &toshiba_attr_group); if (ret) { From 52cbae0127ade4120f51a6c5b46f28534723b9bf Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Wed, 9 Sep 2015 11:28:20 -0600 Subject: [PATCH 02/18] toshiba_acpi: Change default Hotkey enabling value The driver currently uses the hotkey enabling value of 0x09 to enable hotkey events, but windows uses a different value (0x01). All Toshiba laptops accept the following "hotkey" parameters: 0x01 - Enable hotkey and system events. 0x03 - Enable system events only. 0x09 - Enable hotkey events only. 0x0b - Disable (hotkey and system) events. This patch changes the default hotkey enabling value from 0x09 to 0x01, enabling both the hotkey and system events. Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- drivers/platform/x86/toshiba_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 5510d3f593f4..803e96752d5c 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -131,7 +131,7 @@ MODULE_LICENSE("GPL"); /* Field definitions */ #define HCI_ACCEL_MASK 0x7fff #define HCI_HOTKEY_DISABLE 0x0b -#define HCI_HOTKEY_ENABLE 0x09 +#define HCI_HOTKEY_ENABLE 0x01 #define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10 #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) From 0b498201e67df447dc81542a71b36db0fae2878d Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Wed, 9 Sep 2015 11:30:09 -0600 Subject: [PATCH 03/18] toshiba_acpi: Add 0x prefix to available_kbd_modes_show function This patch adds the 0x prefix to the values printed by such function, the values are already being printed in hex, but without the prefix, causing confusion, even though the file under Documentation/ABI clearly states that hey are hex values. Simply add the 0x prefix to avoid such confusion. Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- drivers/platform/x86/toshiba_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 803e96752d5c..878f506f6b51 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1669,10 +1669,10 @@ static ssize_t available_kbd_modes_show(struct device *dev, struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); if (toshiba->kbd_type == 1) - return sprintf(buf, "%x %x\n", + return sprintf(buf, "0x%x 0x%x\n", SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO); - return sprintf(buf, "%x %x %x\n", + return sprintf(buf, "0x%x 0x%x 0x%x\n", SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF); } static DEVICE_ATTR_RO(available_kbd_modes); From 10e6aaabc37171a8b2c0f531696db91f5ac442f9 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Fri, 18 Sep 2015 22:45:34 -0600 Subject: [PATCH 04/18] toshiba_acpi: Remove unneeded u32 variables from *setup_keyboard The function toshiba_acpi_setup_keyboard currently has two u32 variables used to store the Hotkey Event Type and the result of the HCI_SYSTEM_EVENT query. This patch removes those two variables, as we already have a global variable named "hotkey_event_type" and the result of the HCI_SYSTEM_EVENT query can be checked directly from the function. Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- drivers/platform/x86/toshiba_acpi.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 878f506f6b51..beb709f26fc4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2381,8 +2381,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) { const struct key_entry *keymap = toshiba_acpi_keymap; acpi_handle ec_handle; - u32 events_type; - u32 hci_result; int error; if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) { @@ -2394,11 +2392,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) if (error) return error; - if (toshiba_hotkey_event_type_get(dev, &events_type)) + if (toshiba_hotkey_event_type_get(dev, &dev->hotkey_event_type)) pr_notice("Unable to query Hotkey Event Type\n"); - dev->hotkey_event_type = events_type; - dev->hotkey_dev = input_allocate_device(); if (!dev->hotkey_dev) return -ENOMEM; @@ -2407,14 +2403,15 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) dev->hotkey_dev->phys = "toshiba_acpi/input0"; dev->hotkey_dev->id.bustype = BUS_HOST; - if (events_type == HCI_SYSTEM_TYPE1 || + if (dev->hotkey_event_type == HCI_SYSTEM_TYPE1 || !dev->kbd_function_keys_supported) keymap = toshiba_acpi_keymap; - else if (events_type == HCI_SYSTEM_TYPE2 || + else if (dev->hotkey_event_type == HCI_SYSTEM_TYPE2 || dev->kbd_function_keys_supported) keymap = toshiba_acpi_alt_keymap; else - pr_info("Unknown event type received %x\n", events_type); + pr_info("Unknown event type received %x\n", + dev->hotkey_event_type); error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL); if (error) goto err_free_dev; @@ -2445,11 +2442,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) */ if (acpi_has_method(dev->acpi_dev->handle, "INFO")) dev->info_supported = 1; - else { - hci_result = hci_write(dev, HCI_SYSTEM_EVENT, 1); - if (hci_result == TOS_SUCCESS) - dev->system_event_supported = 1; - } + else if (hci_write(dev, HCI_SYSTEM_EVENT, 1) == TOS_SUCCESS) + dev->system_event_supported = 1; if (!dev->info_supported && !dev->system_event_supported) { pr_warn("No hotkey query interface found\n"); From 307340493f3d62935db0bd48a9909bb746ffef1e Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 14 Sep 2015 11:16:30 +0200 Subject: [PATCH 05/18] asus-wmi: restore kbd led level after resume Afters suspend/resume cycle with closed lid the kbd backlight level is lost. This patch will will restore this value to last known level. Signed-off-by: Oleksij Rempel Signed-off-by: Darren Hart --- drivers/platform/x86/asus-wmi.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index efbc3f0c592b..1f7d80ff8cb4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -582,7 +582,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) static int asus_wmi_led_init(struct asus_wmi *asus) { - int rv = 0; + int rv = 0, led_val; asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!asus->led_workqueue) @@ -602,9 +602,11 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (kbd_led_read(asus, NULL, NULL) >= 0) { + led_val = kbd_led_read(asus, NULL, NULL); + if (led_val >= 0) { INIT_WORK(&asus->kbd_led_work, kbd_led_update); + asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.brightness_set = kbd_led_set; asus->kbd_led.brightness_get = kbd_led_get; @@ -2160,6 +2162,16 @@ static int asus_hotk_thaw(struct device *device) return 0; } +static int asus_hotk_resume(struct device *device) +{ + struct asus_wmi *asus = dev_get_drvdata(device); + + if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) + queue_work(asus->led_workqueue, &asus->kbd_led_work); + + return 0; +} + static int asus_hotk_restore(struct device *device) { struct asus_wmi *asus = dev_get_drvdata(device); @@ -2190,6 +2202,8 @@ static int asus_hotk_restore(struct device *device) bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB); rfkill_set_sw_state(asus->uwb.rfkill, bl); } + if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) + queue_work(asus->led_workqueue, &asus->kbd_led_work); return 0; } @@ -2197,6 +2211,7 @@ static int asus_hotk_restore(struct device *device) static const struct dev_pm_ops asus_pm_ops = { .thaw = asus_hotk_thaw, .restore = asus_hotk_restore, + .resume = asus_hotk_resume, }; static int asus_wmi_probe(struct platform_device *pdev) From d9a427ec81e7f1f81cb30777e8fe7dc1f4d03803 Mon Sep 17 00:00:00 2001 From: Roald Frederickx Date: Sat, 12 Sep 2015 22:00:16 +0200 Subject: [PATCH 06/18] compal-laptop: Add charge control limit Add charge control limit to the power supply subsystem of the Compal platform driver. This apparently was present in the original driver by Cezary Jackiewicz at http://eko.one.pl/index.php?page=compal-laptop but it seems to have been overlooked. The Kconfig description is updated to reflect this addition. It now also mentions the hwmon interface that was already present. Signed-off-by: Roald Frederickx Signed-off-by: Darren Hart --- drivers/platform/x86/Kconfig | 4 +-- drivers/platform/x86/compal-laptop.c | 43 +++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c69bb703f483..2f9026d31444 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -309,8 +309,8 @@ config COMPAL_LAPTOP This is a driver for laptops built by Compal, and some models by other brands (e.g. Dell, Toshiba). - It adds support for rfkill, Bluetooth, WLAN and LCD brightness - control. + It adds support for rfkill, Bluetooth, WLAN, LCD brightness, hwmon + and battery charging level control. For a (possibly incomplete) list of supported laptops, please refer to: Documentation/platform/x86-laptop-drivers.txt diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index f2706d27adff..e1c2b6d4b24a 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -151,6 +151,8 @@ #define BAT_STATUS2 0xF1 #define BAT_STOP_CHARGE1 0xF2 #define BAT_STOP_CHARGE2 0xF3 +#define BAT_CHARGE_LIMIT 0x03 +#define BAT_CHARGE_LIMIT_MAX 100 #define BAT_S0_DISCHARGE (1 << 0) #define BAT_S0_DISCHRG_CRITICAL (1 << 2) @@ -601,6 +603,12 @@ static int bat_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_NOW: val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000; break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + val->intval = ec_read_u8(BAT_CHARGE_LIMIT); + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = BAT_CHARGE_LIMIT_MAX; + break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = ec_read_u8(BAT_CAPACITY); break; @@ -634,6 +642,36 @@ static int bat_get_property(struct power_supply *psy, return 0; } +static int bat_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int level; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + level = val->intval; + if (level < 0 || level > BAT_CHARGE_LIMIT_MAX) + return -EINVAL; + if (ec_write(BAT_CHARGE_LIMIT, level) < 0) + return -EIO; + break; + default: + break; + } + return 0; +} + +static int bat_writeable_property(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return 1; + default: + return 0; + } +} @@ -726,6 +764,8 @@ static enum power_supply_property compal_bat_properties[] = { POWER_SUPPLY_PROP_POWER_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, @@ -880,11 +920,12 @@ static const struct power_supply_desc psy_bat_desc = { .properties = compal_bat_properties, .num_properties = ARRAY_SIZE(compal_bat_properties), .get_property = bat_get_property, + .set_property = bat_set_property, + .property_is_writeable = bat_writeable_property, }; static void initialize_power_supply_data(struct compal_data *data) { - ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR, data->bat_manufacturer_name, BAT_MANUFACTURER_NAME_LEN); From daea5a65dec21de4b1f83c05162b5cb66b1f6c4c Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 21 Sep 2015 16:47:01 +0100 Subject: [PATCH 07/18] intel_mid_powerbtn: Remove misuse of IRQF_NO_SUSPEND flag The IRQF_NO_SUSPEND flag is used to identify the interrupts that should be left enabled so as to allow them to work as expected during the suspend-resume cycle, but doesn't guarantee that it will wake the system from a suspended state, enable_irq_wake is recommended to be used for the wakeup. This patch removes the use of IRQF_NO_SUSPEND flags and uses newly introduce PM wakeup APIs dev_pm_{set,clear}_wake_irq. Cc: Darren Hart Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Darren Hart --- drivers/platform/x86/intel_mid_powerbtn.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 22606d6b2af3..1fc0de870ff8 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -24,6 +24,7 @@ #include #include #include +#include #define DRIVER_NAME "msic_power_btn" @@ -76,14 +77,17 @@ 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_NO_SUSPEND, - DRIVER_NAME, input); + error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0, + DRIVER_NAME, input); if (error) { dev_err(&pdev->dev, "Unable to request irq %d for mfld power" "button\n", irq); goto err_free_input; } + device_init_wakeup(&pdev->dev, true); + dev_pm_set_wake_irq(&pdev->dev, irq); + error = input_register_device(input); if (error) { dev_err(&pdev->dev, "Unable to register input dev, error " @@ -124,6 +128,8 @@ static int mfld_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); From 963406ffa6e77ae85b400a9bc8b747813c4497cf Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 24 Sep 2015 16:00:22 +0200 Subject: [PATCH 08/18] sony-laptop: Fix handling sony_nc_hotkeys_decode result sony_nv_hotkeys_decode can return a negative value. real_ev is a u32 variable. The check for real_ev > 0 is incorrect. Use an intermediate ret variable. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/assign_signed_to_unsigned.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2046107 Signed-off-by: Andrzej Hajda [dvhart: clarify commit msg, drop superfluous else block] Signed-off-by: Darren Hart --- drivers/platform/x86/sony-laptop.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index aeb80d1c2b07..f73c29558cd3 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1204,6 +1204,8 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) { u32 real_ev = event; u8 ev_type = 0; + int ret; + dprintk("sony_nc_notify, event: 0x%.2x\n", event); if (event >= 0x90) { @@ -1225,13 +1227,12 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) case 0x0100: case 0x0127: ev_type = HOTKEY; - real_ev = sony_nc_hotkeys_decode(event, handle); + ret = sony_nc_hotkeys_decode(event, handle); - if (real_ev > 0) - sony_laptop_report_input_event(real_ev); - else - /* restore the original event for reporting */ - real_ev = event; + if (ret > 0) { + sony_laptop_report_input_event(ret); + real_ev = ret; + } break; From 14991fc7dfc6cd18b0d79ebc8f39b1350cb34fc9 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Mon, 28 Sep 2015 20:32:28 -0600 Subject: [PATCH 09/18] platform/x86: Toshiba WMI Hotkey Driver Toshiba laptops that feature WMI events for hotkeys were left unsupported by the toshiba_acpi driver, however, commit a88bc06e5aec ("toshiba_acpi: Avoid registering input device on WMI event laptops") added hardware support for such laptops, but the hotkeys are not handled there. This driver adds support for hotkey monitoring on certain Toshiba laptops that manage the hotkeys via WMI events instead of the Toshiba Configuration Interface (TCI). The toshiba_acpi driver and this one can co-exist, as this only takes care of hotkeys, while the proper takes care of hardware related stuff. Currently the driver is under the EXPERIMENTAL flag, as the keymap and the notify function are incomplete (due to lack of hardware to test). Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- MAINTAINERS | 6 ++ drivers/platform/x86/Kconfig | 18 ++++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/toshiba-wmi.c | 138 +++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 drivers/platform/x86/toshiba-wmi.c diff --git a/MAINTAINERS b/MAINTAINERS index 9f6685f6c5a9..adb8e5820506 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10529,6 +10529,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/toshiba_haps.c +TOSHIBA WMI HOTKEYS DRIVER +M: Azael Avalos +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/toshiba-wmi.c + TOSHIBA SMM DRIVER M: Jonathan Buzzard W: http://www.buzzard.org.uk/toshiba/ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2f9026d31444..7b492d9b3ec4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -700,6 +700,24 @@ config TOSHIBA_HAPS If you have a recent Toshiba laptop with a built-in accelerometer device, say Y. +config TOSHIBA_WMI + tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)" + default n + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + ---help--- + This driver adds hotkey monitoring support to some Toshiba models + that manage the hotkeys via WMI events. + + WARNING: This driver is incomplete as it lacks a proper keymap and the + *notify function only prints the ACPI event type value. Be warned that + you will need to provide some information if you have a Toshiba model + with WMI event hotkeys and want to help with the develpment of this + driver. + + If you have a WMI-based hotkeys Toshiba laptop, say Y or M here. + config ACPI_CMPC tristate "CMPC Laptop Extras" depends on X86 && ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index ada512819028..3ca78a3eb6f8 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o +obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c new file mode 100644 index 000000000000..feac4576b837 --- /dev/null +++ b/drivers/platform/x86/toshiba-wmi.c @@ -0,0 +1,138 @@ +/* + * toshiba_wmi.c - Toshiba WMI Hotkey Driver + * + * Copyright (C) 2015 Azael Avalos + * + * 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. + * + * This program is distributed in the hope that 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 +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Azael Avalos"); +MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" + +MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID); + +static struct input_dev *toshiba_wmi_input_dev; + +static const struct key_entry toshiba_wmi_keymap[] __initconst = { + /* TODO: Add keymap values once found... */ + /*{ KE_KEY, 0x00, { KEY_ } },*/ + { KE_END, 0 } +}; + +static void toshiba_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (ACPI_FAILURE(status)) { + pr_err("Bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + if (!obj) + return; + + /* TODO: Add proper checks once we have data */ + pr_debug("Unknown event received, obj type %x\n", obj->type); + + kfree(response.pointer); +} + +static int __init toshiba_wmi_input_setup(void) +{ + acpi_status status; + int err; + + toshiba_wmi_input_dev = input_allocate_device(); + if (!toshiba_wmi_input_dev) + return -ENOMEM; + + toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys"; + toshiba_wmi_input_dev->phys = "wmi/input0"; + toshiba_wmi_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(toshiba_wmi_input_dev, + toshiba_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID, + toshiba_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_keymap; + } + + err = input_register_device(toshiba_wmi_input_dev); + if (err) + goto err_remove_notifier; + + return 0; + + err_remove_notifier: + wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + err_free_keymap: + sparse_keymap_free(toshiba_wmi_input_dev); + err_free_dev: + input_free_device(toshiba_wmi_input_dev); + return err; +} + +static void toshiba_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + sparse_keymap_free(toshiba_wmi_input_dev); + input_unregister_device(toshiba_wmi_input_dev); +} + +static int __init toshiba_wmi_init(void) +{ + int ret; + + if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + return -ENODEV; + + ret = toshiba_wmi_input_setup(); + if (ret) { + pr_err("Failed to setup input device\n"); + return ret; + } + + pr_info("Toshiba WMI Hotkey Driver\n"); + + return 0; +} + +static void __exit toshiba_wmi_exit(void) +{ + if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + toshiba_wmi_input_destroy(); +} + +module_init(toshiba_wmi_init); +module_exit(toshiba_wmi_exit); From 3a4bceeffae5b170af523cd9f2798d5a0d14a2d7 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Tue, 6 Oct 2015 23:17:04 +0100 Subject: [PATCH 10/18] MAINTAINERS: Add drivers/platform/olpc to drivers/platform/x86 Andy Shevchenko sent me a simple patch that's been sitting on LKML for 10 months for OLPC and asked if I'd take it via platform/drivers/x86. OLPC appears to be unmaintained per MAINTAINERS. Add it to platform/drivers/x86 (unless someone else really wants it!) Signed-off-by: Darren Hart --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index adb8e5820506..37997b3c6ddc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11459,6 +11459,7 @@ L: platform-driver-x86@vger.kernel.org T: git git://git.infradead.org/users/dvhart/linux-platform-drivers-x86.git S: Maintained F: drivers/platform/x86/ +F: drivers/platform/olpc/ X86 MCE INFRASTRUCTURE M: Tony Luck From 7d3777d1069137800cdd1420a6c75cd94151d877 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Dec 2014 14:26:50 +0200 Subject: [PATCH 11/18] OLPC: Use %*ph specifier instead of passing direct values The %*ph specifier allows to dump small buffers in hex format. Let's use it instead of passing direct values via stack. Signed-off-by: Andy Shevchenko Acked-by: Paul Fox Signed-off-by: Darren Hart --- drivers/platform/olpc/olpc-ec.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index f9119525f557..f99b183d5296 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -192,18 +192,15 @@ static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf, for (i = 0; i <= ec_cmd_bytes; i++) ec_cmd[i] = ec_cmd_int[i]; - pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %02x %02x %02x %02x %02x, want %d returns\n", - ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2], - ec_cmd[3], ec_cmd[4], ec_cmd[5], ec_dbgfs_resp_bytes); + pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %5ph, want %d returns\n", + ec_cmd[0], ec_cmd_bytes, ec_cmd + 1, + ec_dbgfs_resp_bytes); olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1], ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes); - pr_debug("olpc-ec: response %02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n", - ec_dbgfs_resp[0], ec_dbgfs_resp[1], ec_dbgfs_resp[2], - ec_dbgfs_resp[3], ec_dbgfs_resp[4], ec_dbgfs_resp[5], - ec_dbgfs_resp[6], ec_dbgfs_resp[7], - ec_dbgfs_resp_bytes); + pr_debug("olpc-ec: response %8ph (%d bytes expected)\n", + ec_dbgfs_resp, ec_dbgfs_resp_bytes); out: mutex_unlock(&ec_dbgfs_lock); From c4602280869e7aceb1245f525eff923e548ec9f3 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Mon, 5 Oct 2015 18:17:42 +0800 Subject: [PATCH 12/18] acer-wmi: remove threeg and interface sysfs interfaces Since v3.0 kernel, acer-wmi driver auto detects internal 3G device and provides the threeg rfkill interface. So the sysfs interface of 3G is no longer necessary now. The 7b8aca65 patch added kernel information log to remind to user space for the threeg sysfs interface will be removed in 2012, then b58b9ffc patch updated the interface removing time to 2014. I think the 3 years lead time is enough for user space application to use rfkill instead of sysfs interface to control 3G device. This patch removes code about threeg sysfs interface. And it also removes the unused interface sysfs that exposes which ACPI-WMI method used by acer-wmi driver on the machine. The information is already exposed by the acer-wmi initial log. Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Martin Kepplinger Cc: Darren Hart Signed-off-by: Lee, Chun-Yi Signed-off-by: Darren Hart --- drivers/platform/x86/acer-wmi.c | 92 --------------------------------- 1 file changed, 92 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index d773b9dc48a0..1062fa42ff26 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1662,58 +1662,6 @@ static void acer_rfkill_exit(void) return; } -/* - * sysfs interface - */ -static ssize_t show_bool_threeg(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u32 result; \ - acpi_status status; - - pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n", - current->comm); - status = get_u32(&result, ACER_CAP_THREEG); - if (ACPI_SUCCESS(status)) - return sprintf(buf, "%u\n", result); - return sprintf(buf, "Read error\n"); -} - -static ssize_t set_bool_threeg(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - u32 tmp = simple_strtoul(buf, NULL, 10); - acpi_status status = set_u32(tmp, ACER_CAP_THREEG); - pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n", - current->comm); - if (ACPI_FAILURE(status)) - return -EINVAL; - return count; -} -static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, - set_bool_threeg); - -static ssize_t show_interface(struct device *dev, struct device_attribute *attr, - char *buf) -{ - pr_info("This interface sysfs will be removed in 2014 - used by: %s\n", - current->comm); - switch (interface->type) { - case ACER_AMW0: - return sprintf(buf, "AMW0\n"); - case ACER_AMW0_V2: - return sprintf(buf, "AMW0 v2\n"); - case ACER_WMID: - return sprintf(buf, "WMID\n"); - case ACER_WMID_v2: - return sprintf(buf, "WMID v2\n"); - default: - return sprintf(buf, "Error!\n"); - } -} - -static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); - static void acer_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -2127,39 +2075,6 @@ static struct platform_driver acer_platform_driver = { static struct platform_device *acer_platform_device; -static int remove_sysfs(struct platform_device *device) -{ - if (has_cap(ACER_CAP_THREEG)) - device_remove_file(&device->dev, &dev_attr_threeg); - - device_remove_file(&device->dev, &dev_attr_interface); - - return 0; -} - -static int __init create_sysfs(void) -{ - int retval = -ENOMEM; - - if (has_cap(ACER_CAP_THREEG)) { - retval = device_create_file(&acer_platform_device->dev, - &dev_attr_threeg); - if (retval) - goto error_sysfs; - } - - retval = device_create_file(&acer_platform_device->dev, - &dev_attr_interface); - if (retval) - goto error_sysfs; - - return 0; - -error_sysfs: - remove_sysfs(acer_platform_device); - return retval; -} - static void remove_debugfs(void) { debugfs_remove(interface->debug.devices); @@ -2290,10 +2205,6 @@ static int __init acer_wmi_init(void) if (err) goto error_device_add; - err = create_sysfs(); - if (err) - goto error_create_sys; - if (wmi_has_guid(WMID_GUID2)) { interface->debug.wmid_devices = get_wmid_devices(); err = create_debugfs(); @@ -2307,8 +2218,6 @@ static int __init acer_wmi_init(void) return 0; error_create_debugfs: - remove_sysfs(acer_platform_device); -error_create_sys: platform_device_del(acer_platform_device); error_device_add: platform_device_put(acer_platform_device); @@ -2331,7 +2240,6 @@ static void __exit acer_wmi_exit(void) if (has_cap(ACER_CAP_ACCEL)) acer_wmi_accel_destroy(); - remove_sysfs(acer_platform_device); remove_debugfs(); platform_device_unregister(acer_platform_device); platform_driver_unregister(&acer_platform_driver); From f63fbcee6720a529b8f94c7d72539c15a1a7a7f8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Oct 2015 14:19:44 +0300 Subject: [PATCH 13/18] intel_scu_ipc: Fix error path by turning to devm_* / pcim_* The error handling is broken right now since it leaves resources unfreed. Convert the code to use managed resources to fix the error handling. Signed-off-by: Andy Shevchenko Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipc.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 187d1086d15c..7148535b216a 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -563,7 +563,6 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) { int err; struct intel_scu_ipc_pdata_t *pdata; - resource_size_t base; if (ipcdev.pdev) /* We support only one SCU */ return -EBUSY; @@ -573,32 +572,26 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) ipcdev.pdev = pci_dev_get(dev); ipcdev.irq_mode = pdata->irq_mode; - err = pci_enable_device(dev); + err = pcim_enable_device(dev); if (err) return err; - err = pci_request_regions(dev, "intel_scu_ipc"); + err = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); if (err) return err; - base = pci_resource_start(dev, 0); - if (!base) - return -ENOMEM; - init_completion(&ipcdev.cmd_complete); - if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) - return -EBUSY; + err = devm_request_irq(&dev->dev, dev->irq, ioc, 0, "intel_scu_ipc", + &ipcdev); + if (err) + return err; - ipcdev.ipc_base = ioremap_nocache(base, pci_resource_len(dev, 0)); - if (!ipcdev.ipc_base) - return -ENOMEM; + ipcdev.ipc_base = pcim_iomap_table(dev)[0]; ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len); - if (!ipcdev.i2c_base) { - iounmap(ipcdev.ipc_base); + if (!ipcdev.i2c_base) return -ENOMEM; - } intel_scu_devices_create(); @@ -617,10 +610,7 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) */ static void ipc_remove(struct pci_dev *pdev) { - free_irq(pdev->irq, &ipcdev); - pci_release_regions(pdev); pci_dev_put(ipcdev.pdev); - iounmap(ipcdev.ipc_base); iounmap(ipcdev.i2c_base); ipcdev.pdev = NULL; intel_scu_devices_destroy(); From b0b3f578a1c363585d0f74f3b80a1dc968d150b4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Oct 2015 14:19:45 +0300 Subject: [PATCH 14/18] intel_scu_ipc: Propagate pointer to struct intel_scu_ipc_dev As much as possible propagate a pointer to struct intel_scu_ipc_dev. There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipc.c | 134 +++++++++++++++------------ 1 file changed, 74 insertions(+), 60 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 7148535b216a..6c9367fe569c 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -118,28 +118,30 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ /* + * Send ipc command * Command Register (Write Only): * A write to this register results in an interrupt to the SCU core processor * Format: * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| */ -static inline void ipc_command(u32 cmd) /* Send ipc command */ +static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd) { - if (ipcdev.irq_mode) { - reinit_completion(&ipcdev.cmd_complete); - writel(cmd | IPC_IOC, ipcdev.ipc_base); + if (scu->irq_mode) { + reinit_completion(&scu->cmd_complete); + writel(cmd | IPC_IOC, scu->ipc_base); } - writel(cmd, ipcdev.ipc_base); + writel(cmd, scu->ipc_base); } /* + * Write ipc data * IPC Write Buffer (Write Only): * 16-byte buffer for sending data associated with IPC command to * SCU. Size of the data is specified in the IPC_COMMAND_REG register */ -static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ +static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset) { - writel(data, ipcdev.ipc_base + 0x80 + offset); + writel(data, scu->ipc_base + 0x80 + offset); } /* @@ -149,35 +151,37 @@ static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ * Format: * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| */ -static inline u8 ipc_read_status(void) +static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu) { - return __raw_readl(ipcdev.ipc_base + 0x04); + return __raw_readl(scu->ipc_base + 0x04); } -static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ +/* Read ipc byte data */ +static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset) { - return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); + return readb(scu->ipc_base + IPC_READ_BUFFER + offset); } -static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */ +/* Read ipc u32 data */ +static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset) { - return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); + return readl(scu->ipc_base + IPC_READ_BUFFER + offset); } /* Wait till scu status is busy */ -static inline int busy_loop(void) +static inline int busy_loop(struct intel_scu_ipc_dev *scu) { - u32 status = ipc_read_status(); + u32 status = ipc_read_status(scu); u32 loop_count = 100000; /* break if scu doesn't reset busy bit after huge retry */ while ((status & BIT(0)) && --loop_count) { udelay(1); /* scu processing time is in few u secods */ - status = ipc_read_status(); + status = ipc_read_status(scu); } if (status & BIT(0)) { - dev_err(&ipcdev.pdev->dev, "IPC timed out"); + dev_err(&scu->pdev->dev, "IPC timed out"); return -ETIMEDOUT; } @@ -188,31 +192,32 @@ static inline int busy_loop(void) } /* Wait till ipc ioc interrupt is received or timeout in 3 HZ */ -static inline int ipc_wait_for_interrupt(void) +static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) { int status; - if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) { - struct device *dev = &ipcdev.pdev->dev; + if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) { + struct device *dev = &scu->pdev->dev; dev_err(dev, "IPC timed out\n"); return -ETIMEDOUT; } - status = ipc_read_status(); + status = ipc_read_status(scu); if (status & BIT(1)) return -EIO; return 0; } -static int intel_scu_ipc_check_status(void) +static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu) { - return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop(); + return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu); } /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) { + struct intel_scu_ipc_dev *scu = &ipcdev; int nc; u32 offset = 0; int err; @@ -223,7 +228,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) mutex_lock(&ipclock); - if (ipcdev.pdev == NULL) { + if (scu->pdev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } @@ -235,27 +240,27 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) if (id == IPC_CMD_PCNTRL_R) { for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count * 2) << 16 | id << 12 | 0 << 8 | op); + ipc_data_writel(scu, wbuf[nc], offset); + ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op); } else if (id == IPC_CMD_PCNTRL_W) { for (nc = 0; nc < count; nc++, offset += 1) cbuf[offset] = data[nc]; for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op); + ipc_data_writel(scu, wbuf[nc], offset); + ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op); } else if (id == IPC_CMD_PCNTRL_M) { cbuf[offset] = data[0]; cbuf[offset + 1] = data[1]; - ipc_data_writel(wbuf[0], 0); /* Write wbuff */ - ipc_command(4 << 16 | id << 12 | 0 << 8 | op); + ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */ + ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op); } - err = intel_scu_ipc_check_status(); + err = intel_scu_ipc_check_status(scu); if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ /* Workaround: values are read as 0 without memcpy_fromio */ - memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); + memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16); for (nc = 0; nc < count; nc++) - data[nc] = ipc_data_readb(nc); + data[nc] = ipc_data_readb(scu, nc); } mutex_unlock(&ipclock); return err; @@ -436,15 +441,16 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register); */ int intel_scu_ipc_simple_command(int cmd, int sub) { + struct intel_scu_ipc_dev *scu = &ipcdev; int err; mutex_lock(&ipclock); - if (ipcdev.pdev == NULL) { + if (scu->pdev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } - ipc_command(sub << 12 | cmd); - err = intel_scu_ipc_check_status(); + ipc_command(scu, sub << 12 | cmd); + err = intel_scu_ipc_check_status(scu); mutex_unlock(&ipclock); return err; } @@ -465,23 +471,24 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command); int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, u32 *out, int outlen) { + struct intel_scu_ipc_dev *scu = &ipcdev; int i, err; mutex_lock(&ipclock); - if (ipcdev.pdev == NULL) { + if (scu->pdev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } for (i = 0; i < inlen; i++) - ipc_data_writel(*in++, 4 * i); + ipc_data_writel(scu, *in++, 4 * i); - ipc_command((inlen << 16) | (sub << 12) | cmd); - err = intel_scu_ipc_check_status(); + ipc_command(scu, (inlen << 16) | (sub << 12) | cmd); + err = intel_scu_ipc_check_status(scu); if (!err) { for (i = 0; i < outlen; i++) - *out++ = ipc_data_readl(4 * i); + *out++ = ipc_data_readl(scu, 4 * i); } mutex_unlock(&ipclock); @@ -507,25 +514,26 @@ EXPORT_SYMBOL(intel_scu_ipc_command); */ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) { + struct intel_scu_ipc_dev *scu = &ipcdev; u32 cmd = 0; mutex_lock(&ipclock); - if (ipcdev.pdev == NULL) { + if (scu->pdev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } cmd = (addr >> 24) & 0xFF; if (cmd == IPC_I2C_READ) { - writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); + writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR); /* Write not getting updated without delay */ mdelay(1); - *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR); + *data = readl(scu->i2c_base + I2C_DATA_ADDR); } else if (cmd == IPC_I2C_WRITE) { - writel(*data, ipcdev.i2c_base + I2C_DATA_ADDR); + writel(*data, scu->i2c_base + I2C_DATA_ADDR); mdelay(1); - writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); + writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR); } else { - dev_err(&ipcdev.pdev->dev, + dev_err(&scu->pdev->dev, "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); mutex_unlock(&ipclock); @@ -545,8 +553,10 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); */ static irqreturn_t ioc(int irq, void *dev_id) { - if (ipcdev.irq_mode) - complete(&ipcdev.cmd_complete); + struct intel_scu_ipc_dev *scu = dev_id; + + if (scu->irq_mode) + complete(&scu->cmd_complete); return IRQ_HANDLED; } @@ -562,15 +572,16 @@ static irqreturn_t ioc(int irq, void *dev_id) static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) { int err; + struct intel_scu_ipc_dev *scu = &ipcdev; struct intel_scu_ipc_pdata_t *pdata; - if (ipcdev.pdev) /* We support only one SCU */ + if (scu->pdev) /* We support only one SCU */ return -EBUSY; pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data; - ipcdev.pdev = pci_dev_get(dev); - ipcdev.irq_mode = pdata->irq_mode; + scu->pdev = pci_dev_get(dev); + scu->irq_mode = pdata->irq_mode; err = pcim_enable_device(dev); if (err) @@ -580,21 +591,22 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) if (err) return err; - init_completion(&ipcdev.cmd_complete); + init_completion(&scu->cmd_complete); err = devm_request_irq(&dev->dev, dev->irq, ioc, 0, "intel_scu_ipc", - &ipcdev); + scu); if (err) return err; - ipcdev.ipc_base = pcim_iomap_table(dev)[0]; + scu->ipc_base = pcim_iomap_table(dev)[0]; - ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len); - if (!ipcdev.i2c_base) + scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len); + if (!scu->i2c_base) return -ENOMEM; intel_scu_devices_create(); + pci_set_drvdata(dev, scu); return 0; } @@ -610,9 +622,11 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) */ static void ipc_remove(struct pci_dev *pdev) { - pci_dev_put(ipcdev.pdev); - iounmap(ipcdev.i2c_base); - ipcdev.pdev = NULL; + struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev); + + pci_dev_put(scu->pdev); + scu->pdev = NULL; + iounmap(scu->i2c_base); intel_scu_devices_destroy(); } From 20903169fed97e1a972a85ed0b02f20465ae2ff8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Oct 2015 14:19:46 +0300 Subject: [PATCH 15/18] intel_scu_ipc: Convert to use struct device * Switch the code to use struct device * instead of struct pci_dev * since there is no reason to access PCI related features in the driver. Signed-off-by: Andy Shevchenko Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipc.c | 41 ++++++++++++---------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6c9367fe569c..5087485a1fa7 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -92,11 +92,8 @@ static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = { .irq_mode = 0, }; -static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); -static void ipc_remove(struct pci_dev *pdev); - struct intel_scu_ipc_dev { - struct pci_dev *pdev; + struct device *dev; void __iomem *ipc_base; void __iomem *i2c_base; struct completion cmd_complete; @@ -181,7 +178,7 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu) } if (status & BIT(0)) { - dev_err(&scu->pdev->dev, "IPC timed out"); + dev_err(scu->dev, "IPC timed out"); return -ETIMEDOUT; } @@ -197,8 +194,7 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) int status; if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) { - struct device *dev = &scu->pdev->dev; - dev_err(dev, "IPC timed out\n"); + dev_err(scu->dev, "IPC timed out\n"); return -ETIMEDOUT; } @@ -228,7 +224,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) mutex_lock(&ipclock); - if (scu->pdev == NULL) { + if (scu->dev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } @@ -445,7 +441,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub) int err; mutex_lock(&ipclock); - if (scu->pdev == NULL) { + if (scu->dev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } @@ -475,7 +471,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, int i, err; mutex_lock(&ipclock); - if (scu->pdev == NULL) { + if (scu->dev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } @@ -518,7 +514,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) u32 cmd = 0; mutex_lock(&ipclock); - if (scu->pdev == NULL) { + if (scu->dev == NULL) { mutex_unlock(&ipclock); return -ENODEV; } @@ -533,7 +529,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) mdelay(1); writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR); } else { - dev_err(&scu->pdev->dev, + dev_err(scu->dev, "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); mutex_unlock(&ipclock); @@ -563,42 +559,42 @@ static irqreturn_t ioc(int irq, void *dev_id) /** * ipc_probe - probe an Intel SCU IPC - * @dev: the PCI device matching + * @pdev: the PCI device matching * @id: entry in the match table * * Enable and install an intel SCU IPC. This appears in the PCI space * but uses some hard coded addresses as well. */ -static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) +static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int err; struct intel_scu_ipc_dev *scu = &ipcdev; struct intel_scu_ipc_pdata_t *pdata; - if (scu->pdev) /* We support only one SCU */ + if (scu->dev) /* We support only one SCU */ return -EBUSY; pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data; - scu->pdev = pci_dev_get(dev); + scu->dev = &pdev->dev; scu->irq_mode = pdata->irq_mode; - err = pcim_enable_device(dev); + err = pcim_enable_device(pdev); if (err) return err; - err = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); + err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); if (err) return err; init_completion(&scu->cmd_complete); - err = devm_request_irq(&dev->dev, dev->irq, ioc, 0, "intel_scu_ipc", + err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc", scu); if (err) return err; - scu->ipc_base = pcim_iomap_table(dev)[0]; + scu->ipc_base = pcim_iomap_table(pdev)[0]; scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len); if (!scu->i2c_base) @@ -606,7 +602,7 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) intel_scu_devices_create(); - pci_set_drvdata(dev, scu); + pci_set_drvdata(pdev, scu); return 0; } @@ -624,8 +620,7 @@ static void ipc_remove(struct pci_dev *pdev) { struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev); - pci_dev_put(scu->pdev); - scu->pdev = NULL; + scu->dev = NULL; iounmap(scu->i2c_base); intel_scu_devices_destroy(); } From 51c58f2b4f24214950d3dc6ab31af7c2532b096f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Oct 2015 14:19:47 +0300 Subject: [PATCH 16/18] intel_scu_ipc: Switch to use module_pci_driver() macro Eliminate some boilerplate code by using module_pci_driver() instead of init/exit, moving the salient bits from init into probe. Signed-off-by: Andy Shevchenko Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipc.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 5087485a1fa7..9de2029aa909 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -567,10 +567,15 @@ static irqreturn_t ioc(int irq, void *dev_id) */ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + int platform; /* Platform type */ int err; struct intel_scu_ipc_dev *scu = &ipcdev; struct intel_scu_ipc_pdata_t *pdata; + platform = intel_mid_identify_cpu(); + if (platform == 0) + return -ENODEV; + if (scu->dev) /* We support only one SCU */ return -EBUSY; @@ -651,24 +656,8 @@ static struct pci_driver ipc_driver = { .remove = ipc_remove, }; -static int __init intel_scu_ipc_init(void) -{ - int platform; /* Platform type */ - - platform = intel_mid_identify_cpu(); - if (platform == 0) - return -ENODEV; - return pci_register_driver(&ipc_driver); -} - -static void __exit intel_scu_ipc_exit(void) -{ - pci_unregister_driver(&ipc_driver); -} +module_pci_driver(ipc_driver); MODULE_AUTHOR("Sreedhara DS "); MODULE_DESCRIPTION("Intel SCU IPC driver"); MODULE_LICENSE("GPL"); - -module_init(intel_scu_ipc_init); -module_exit(intel_scu_ipc_exit); From 9d1d459bf52b4a07a934558e055bb61ad82f44cd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Oct 2015 14:19:48 +0300 Subject: [PATCH 17/18] intel_scu_ipc: Protect dev member assignment on ->remove() Protect the dev member assignment in ->remove() since user may potentially call unbind from a sysfs even if the driver is built-in. The latter might be racy with ongoing SCU communication. Signed-off-by: Andy Shevchenko Signed-off-by: Darren Hart --- drivers/platform/x86/intel_scu_ipc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 9de2029aa909..f94b730540e2 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -625,7 +625,10 @@ 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(); } From d2f20619942fe4618160a7fa3dbdcbac335cff59 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Wed, 4 Nov 2015 09:28:26 -0700 Subject: [PATCH 18/18] toshiba_acpi: Initialize hotkey_event_type variable Commit 53147b6cabee5e8d1997b5682fcc0c3b72ddf9c2 ("toshiba_acpi: Fix hotkeys registration on some toshiba models") fixed an issue on some laptops regarding hotkeys registration, however, if failed to address the initialization of the hotkey_event_type variable, and thus, it can lead to potential unwanted effects as the variable is being checked. This patch initializes such variable to avoid such unwanted effects. Cc: # 4.1+ Signed-off-by: Azael Avalos Signed-off-by: Darren Hart --- drivers/platform/x86/toshiba_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index beb709f26fc4..c01302989ee4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2665,6 +2665,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ret = toshiba_function_keys_get(dev, &dev->special_functions); dev->kbd_function_keys_supported = !ret; + dev->hotkey_event_type = 0; if (toshiba_acpi_setup_keyboard(dev)) pr_info("Unable to activate hotkeys\n");