From 4215484f8e33c8a4664aecb6db5ce2c544d3b908 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 10 Jul 2019 13:33:26 +0800 Subject: [PATCH 01/53] platform/x86: hp_accel: Add support for HP ZBook 17 G5 HP ZBook 17 G5 needs a non-standard mapping, x_inverted. Signed-off-by: Kai-Heng Feng Signed-off-by: Andy Shevchenko --- drivers/platform/x86/hp_accel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 7a2747455237..799cbe2ffcf3 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -240,6 +240,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("HPZBook17G5", "HP ZBook 17 G5", x_inverted), AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), { NULL, } /* Laptop models without axis info (yet): From 340f25ff1d4fd3bfe0d3e0769261fec8fb32ae9f Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Thu, 4 Jul 2019 00:29:51 +0800 Subject: [PATCH 02/53] platform/x86: asus-wmi: Use kmemdup rather than duplicating its implementation kmemdup is introduced to duplicate a region of memory in a neat way. Rather than kmalloc/kzalloc + memcpy, which the programmer needs to write the size twice (sometimes lead to mistakes), kmemdup improves readability, leads to smaller code and also reduce the chances of mistakes. Suggestion to use kmemdup rather than using kmalloc/kzalloc + memcpy. Signed-off-by: Fuqian Huang Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ca28d27dae63..0c6a810fcb72 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -292,12 +292,11 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) * Copy to dma capable address otherwise memory corruption occurs as * bios has to be able to access it. */ - input.pointer = kzalloc(args.length, GFP_DMA | GFP_KERNEL); + input.pointer = kmemdup(args.pointer, args.length, GFP_DMA | GFP_KERNEL); input.length = args.length; if (!input.pointer) return -ENOMEM; phys_addr = virt_to_phys(input.pointer); - memcpy(input.pointer, args.pointer, args.length); status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN, phys_addr, 0, &retval); From 35b7c80827e9352b88a2379f06cbb9aa3dd64c14 Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Thu, 4 Jul 2019 00:30:00 +0800 Subject: [PATCH 03/53] platform/x86: thinkpad_acpi: Use kmemdup rather than duplicating its implementation kmemdup is introduced to duplicate a region of memory in a neat way. Rather than kmalloc/kzalloc + memcpy, which the programmer needs to write the size twice (sometimes lead to mistakes), kmemdup improves readability, leads to smaller code and also reduce the chances of mistakes. Suggestion to use kmemdup rather than using kmalloc/kzalloc + memcpy. Signed-off-by: Fuqian Huang Signed-off-by: Andy Shevchenko --- drivers/platform/x86/thinkpad_acpi.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7bde4640ef34..d379bdf98a0f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3647,22 +3647,19 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) goto err_exit; /* Set up key map */ - hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, - GFP_KERNEL); - if (!hotkey_keycode_map) { - pr_err("failed to allocate memory for key map\n"); - res = -ENOMEM; - goto err_exit; - } - keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, ARRAY_SIZE(tpacpi_keymap_qtable)); BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps)); dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using keymap number %lu\n", keymap_id); - memcpy(hotkey_keycode_map, &tpacpi_keymaps[keymap_id], - TPACPI_HOTKEY_MAP_SIZE); + hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id], + TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); + if (!hotkey_keycode_map) { + pr_err("failed to allocate memory for key map\n"); + res = -ENOMEM; + goto err_exit; + } input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; From 5599e98fe06a88db55133ae8ac53f69216b82c6a Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 20:23:20 +0800 Subject: [PATCH 04/53] platform/x86: ISST: Use dev_get_drvdata Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c index f7266a115a08..ad8c7c0df4d9 100644 --- a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c +++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c @@ -132,11 +132,9 @@ static void isst_if_remove(struct pci_dev *pdev) static int __maybe_unused isst_if_suspend(struct device *device) { - struct pci_dev *pdev = to_pci_dev(device); - struct isst_if_device *punit_dev; + struct isst_if_device *punit_dev = dev_get_drvdata(device); int i; - punit_dev = pci_get_drvdata(pdev); for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) punit_dev->range_0[i] = readl(punit_dev->punit_mmio + mmio_range[0].beg + 4 * i); @@ -149,11 +147,9 @@ static int __maybe_unused isst_if_suspend(struct device *device) static int __maybe_unused isst_if_resume(struct device *device) { - struct pci_dev *pdev = to_pci_dev(device); - struct isst_if_device *punit_dev; + struct isst_if_device *punit_dev = dev_get_drvdata(device); int i; - punit_dev = pci_get_drvdata(pdev); for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) writel(punit_dev->range_0[i], punit_dev->punit_mmio + mmio_range[0].beg + 4 * i); From bad9da86f9f10a2e68a1574dd34edde6f8556d5f Mon Sep 17 00:00:00 2001 From: Kelsey Skunberg Date: Sun, 21 Jul 2019 21:11:58 -0600 Subject: [PATCH 05/53] platform/x86: wmi: Remove acpi_has_method() call acpi_has_method() is unnecessary within __query_block() and should be removed to avoid extra work. wc_status is initialized to AE_ERROR before the acpi_has_method() call. acpi_has_method() and acpi_execute_simple_method() failing due to the method not existing will result in the same outcome from __query_block(). Signed-off-by: Kelsey Skunberg Signed-off-by: Andy Shevchenko --- drivers/platform/x86/wmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 784cea8572c2..59e9aa0f9643 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -340,9 +340,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, * expensive, but have no corresponding WCxx method. So we * should not fail if this happens. */ - if (acpi_has_method(handle, wc_method)) - wc_status = acpi_execute_simple_method(handle, - wc_method, 1); + wc_status = acpi_execute_simple_method(handle, wc_method, 1); } strcpy(method, "WQ"); From 6fe9363b15767babf3f5b9eb41a664de2cff4030 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Jul 2019 23:34:11 +0300 Subject: [PATCH 06/53] platform/x86: acer-wmi: Switch to acpi_dev_get_first_match_dev() The acpi_dev_get_first_match_dev() helper will find and return an ACPI device pointer of the first registered device in the system by its HID. Use it instead of open coded variant. Cc: Chun-Yi Lee Signed-off-by: Andy Shevchenko --- drivers/platform/x86/acer-wmi.c | 49 +++++---------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 62b54e137231..60c18f21588d 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1881,52 +1881,17 @@ static int __init acer_wmi_enable_rf_button(void) 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_CTRL_TERMINATE; -} - -static int __init acer_wmi_get_handle(const char *name, const char *prop, - acpi_handle *ah) -{ - acpi_status status; - acpi_handle handle; - - BUG_ON(!name || !ah); - - handle = NULL; - status = acpi_get_devices(prop, acer_wmi_get_handle_cb, - (void *)name, &handle); - if (ACPI_SUCCESS(status) && handle) { - *ah = handle; - return 0; - } else { - return -ENODEV; - } -} - static int __init acer_wmi_accel_setup(void) { + struct acpi_device *adev; int err; - err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); - if (err) - return err; + adev = acpi_dev_get_first_match_dev("BST0001", NULL, -1); + if (!adev) + return -ENODEV; + + gsensor_handle = acpi_device_handle(adev); + acpi_dev_put(adev); interface->capability |= ACER_CAP_ACCEL; From ff32e6e2dfd3cfdcaef63a0688bbfb9259aebe02 Mon Sep 17 00:00:00 2001 From: Rhys Kidd Date: Wed, 24 Jul 2019 18:14:13 +1000 Subject: [PATCH 07/53] platform/x86: dell-wmi: Ignore keyboard backlight change KBD_LED_ON_TOKEN There's a wmi event generated by dell-wmi when pressing keyboard backlight toggle key: [1224203.948894] dell_wmi: Unknown key with type 0x0011 and code 0x01e2 pressed This event is for notification purposes, let's ignore it. Signed-off-by: Rhys Kidd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/dell-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index acc653f9c16f..d9cd59d60b66 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -312,6 +312,7 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = { /* Keyboard backlight level changed */ { KE_IGNORE, 0x01e1, { KEY_RESERVED } }, + { KE_IGNORE, 0x01e2, { KEY_RESERVED } }, { KE_IGNORE, 0x02ea, { KEY_RESERVED } }, { KE_IGNORE, 0x02eb, { KEY_RESERVED } }, { KE_IGNORE, 0x02ec, { KEY_RESERVED } }, From 1be4311063a44ba8f4292a3a4a1f537be1f0b344 Mon Sep 17 00:00:00 2001 From: Rhys Kidd Date: Wed, 24 Jul 2019 18:14:14 +1000 Subject: [PATCH 08/53] platform/x86: dell-wmi: Ignore keyboard backlight change KBD_LED_AUTO_TOKEN There's a wmi event generated by dell-wmi when pressing keyboard backlight toggle key: [1224178.355650] dell_wmi: Unknown key with type 0x0011 and code 0x01e3 pressed This event is for notification purposes, let's ignore it. Signed-off-by: Rhys Kidd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/dell-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index d9cd59d60b66..31a092e049a0 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -313,6 +313,7 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = { /* Keyboard backlight level changed */ { KE_IGNORE, 0x01e1, { KEY_RESERVED } }, { KE_IGNORE, 0x01e2, { KEY_RESERVED } }, + { KE_IGNORE, 0x01e3, { KEY_RESERVED } }, { KE_IGNORE, 0x02ea, { KEY_RESERVED } }, { KE_IGNORE, 0x02eb, { KEY_RESERVED } }, { KE_IGNORE, 0x02ec, { KEY_RESERVED } }, From aaed5c9c2a146640170e0fbc84ca97e352c065ce Mon Sep 17 00:00:00 2001 From: Rhys Kidd Date: Wed, 24 Jul 2019 18:14:15 +1000 Subject: [PATCH 09/53] platform/x86: dell-wmi: Use existing defined KBD_LED_* magic values These values have already been defined in platform/x86/dell-smbios.h Signed-off-by: Rhys Kidd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/dell-wmi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 31a092e049a0..6669db2555fb 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -311,13 +311,13 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = { { KE_IGNORE, 0xfff1, { KEY_RESERVED } }, /* Keyboard backlight level changed */ - { KE_IGNORE, 0x01e1, { KEY_RESERVED } }, - { KE_IGNORE, 0x01e2, { KEY_RESERVED } }, - { KE_IGNORE, 0x01e3, { KEY_RESERVED } }, - { KE_IGNORE, 0x02ea, { KEY_RESERVED } }, - { KE_IGNORE, 0x02eb, { KEY_RESERVED } }, - { KE_IGNORE, 0x02ec, { KEY_RESERVED } }, - { KE_IGNORE, 0x02f6, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_25_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_50_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_75_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_100_TOKEN, { KEY_RESERVED } }, }; static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) From c03f282e506f1e873ef9b270cf245509d68dd53b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Jul 2019 22:05:50 +0300 Subject: [PATCH 10/53] platform/x86: i2c-multi-instantiate: Use struct_size() helper One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. Signed-off-by: Andy Shevchenko Reviewed-by: Hans de Goede --- drivers/platform/x86/i2c-multi-instantiate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c index 197d8a192721..61fe341a85aa 100644 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ b/drivers/platform/x86/i2c-multi-instantiate.c @@ -81,9 +81,7 @@ static int i2c_multi_inst_probe(struct platform_device *pdev) if (ret < 0) return ret; - multi = devm_kmalloc(dev, - offsetof(struct i2c_multi_inst_data, clients[ret]), - GFP_KERNEL); + multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL); if (!multi) return -ENOMEM; From 6a0694b6a244bc43ee79b65d0ae5435fb7b74401 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 26 Jul 2019 08:21:15 +0200 Subject: [PATCH 11/53] platform/x86: pcengines-apuv2: add mpcie reset gpio export On APUx we have also mpcie2/mpcie3 reset pins. To make it possible to reset the ports from the userspace, add the definition to this platform device. The gpio can then be exported by the legancy gpio subsystem to toggle the mpcie reset pin. Signed-off-by: Florian Eckert Acked-by: Enrico Weigelt Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pcengines-apuv2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index e4c68efac0c2..56703656e8e6 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -32,6 +32,8 @@ #define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 #define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1 #define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2 +#define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO59_DEVSLP0 +#define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51 /* order in which the gpio lines are defined in the register list */ #define APU2_GPIO_LINE_LED1 0 @@ -39,6 +41,8 @@ #define APU2_GPIO_LINE_LED3 2 #define APU2_GPIO_LINE_MODESW 3 #define APU2_GPIO_LINE_SIMSWAP 4 +#define APU2_GPIO_LINE_MPCIE2 5 +#define APU2_GPIO_LINE_MPCIE3 6 /* gpio device */ @@ -48,6 +52,8 @@ static int apu2_gpio_regs[] = { [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3, [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW, [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP, + [APU2_GPIO_LINE_MPCIE2] = APU2_GPIO_REG_MPCIE2, + [APU2_GPIO_LINE_MPCIE3] = APU2_GPIO_REG_MPCIE3, }; static const char * const apu2_gpio_names[] = { @@ -56,6 +62,8 @@ static const char * const apu2_gpio_names[] = { [APU2_GPIO_LINE_LED3] = "front-led3", [APU2_GPIO_LINE_MODESW] = "front-button", [APU2_GPIO_LINE_SIMSWAP] = "simswap", + [APU2_GPIO_LINE_MPCIE2] = "mpcie2_reset", + [APU2_GPIO_LINE_MPCIE3] = "mpcie3_reset", }; static const struct amd_fch_gpio_pdata board_apu2 = { From 5037d4ddda31c2dbbb018109655f61054b1756dc Mon Sep 17 00:00:00 2001 From: Enrico Weigelt Date: Mon, 29 Jul 2019 16:39:26 +0200 Subject: [PATCH 12/53] platform/x86: pcengines-apuv2: wire up simswitch gpio as led The APU3+ boards have two SIM sockets, while only one of them can be routed to the mpcie slots at a time. Selection is done via simswap gpio. We currently don't have a fitting subsystem for those cases yet, so just wire it up to a LED for the time being. While this isn't really semantically correct, it's a good compromise. Signed-off-by: Enrico Weigelt Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pcengines-apuv2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 56703656e8e6..48b112b4f0b0 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -77,7 +77,8 @@ static const struct amd_fch_gpio_pdata board_apu2 = { static const struct gpio_led apu2_leds[] = { { .name = "apu:green:1" }, { .name = "apu:green:2" }, - { .name = "apu:green:3" } + { .name = "apu:green:3" }, + { .name = "apu:simswap" }, }; static const struct gpio_led_platform_data apu2_leds_pdata = { @@ -94,6 +95,8 @@ static struct gpiod_lookup_table gpios_led_table = { NULL, 1, GPIO_ACTIVE_LOW), GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_REG_SIMSWAP, + NULL, 3, GPIO_ACTIVE_LOW), } }; From 2889ffcfc2522d6d25e5bda704275064062bbb21 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 29 Jul 2019 16:27:37 +0800 Subject: [PATCH 13/53] platform/x86: asus-wmi: cleanup AGFN fan handling The asus-wmi driver currently uses the "AGFN" interface and the FAN_CTRL device for fan control. According to the spec, this interface is very dated and marked as pending removal from products currently in development. Clean up the way that the AGFN fan is detected and handled, also preparing the driver for the introduction of an alternate fan control method needed to support recent Asus products. Not anticipating further development of this interface, simplify the code by dropping any notion of being able to control multiple AGFN fans (this was already limited to just a single fan through only exposing a single fan in sysfs). Check for the presence of AGFN fans at probe time, simplifying the code flow in asus_hwmon_sysfs_is_visible(). Signed-off-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 240 ++++++++++----------- include/linux/platform_data/x86/asus-wmi.h | 4 +- 2 files changed, 110 insertions(+), 134 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 0c6a810fcb72..fc2939ac1cfe 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -65,6 +65,8 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_MFUN 0x13 #define ASUS_FAN_SFUN_READ 0x06 #define ASUS_FAN_SFUN_WRITE 0x07 + +/* Based on standard hwmon pwmX_enable values */ #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 @@ -120,7 +122,7 @@ struct agfn_args { } __packed; /* struct used for calling fan read and write methods */ -struct fan_args { +struct agfn_fan_args { struct agfn_args agfn; /* common fields */ u8 fan; /* fan number: 0: set auto mode 1: 1st fan */ u32 speed; /* read: RPM/100 - write: 0-255 */ @@ -148,6 +150,11 @@ struct asus_rfkill { u32 dev_id; }; +enum fan_type { + FAN_TYPE_NONE = 0, + FAN_TYPE_AGFN, /* deprecated on newer platforms */ +}; + struct asus_wmi { int dsts_id; int spec; @@ -178,9 +185,9 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; - bool asus_hwmon_fan_manual_mode; - int asus_hwmon_num_fans; - int asus_hwmon_pwm; + enum fan_type fan_type; + int fan_pwm_mode; + int agfn_pwm; bool fan_boost_mode_available; u8 fan_boost_mode_mask; @@ -1125,10 +1132,10 @@ static void asus_wmi_set_als(void) /* Hwmon device ***************************************************************/ -static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, +static int asus_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { - struct fan_args args = { + struct agfn_fan_args args = { .agfn.len = sizeof(args), .agfn.mfun = ASUS_FAN_MFUN, .agfn.sfun = ASUS_FAN_SFUN_READ, @@ -1152,10 +1159,10 @@ static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, return 0; } -static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, +static int asus_agfn_fan_speed_write(struct asus_wmi *asus, int fan, int *speed) { - struct fan_args args = { + struct agfn_fan_args args = { .agfn.len = sizeof(args), .agfn.mfun = ASUS_FAN_MFUN, .agfn.sfun = ASUS_FAN_SFUN_WRITE, @@ -1175,7 +1182,7 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, return -ENXIO; if (speed && fan == 1) - asus->asus_hwmon_pwm = *speed; + asus->agfn_pwm = *speed; return 0; } @@ -1184,87 +1191,75 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, * Check if we can read the speed of one fan. If true we assume we can also * control it. */ -static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans) +static bool asus_wmi_has_agfn_fan(struct asus_wmi *asus) { int status; - int speed = 0; + int speed; + u32 value; - *num_fans = 0; + status = asus_agfn_fan_speed_read(asus, 1, &speed); + if (status != 0) + return false; - status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed); - if (!status) - *num_fans = 1; + status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); + if (status != 0) + return false; - return 0; + /* + * We need to find a better way, probably using sfun, + * bits or spec ... + * Currently we disable it if: + * - ASUS_WMI_UNSUPPORTED_METHOD is returned + * - reverved bits are non-zero + * - sfun and presence bit are not set + */ + return !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000 + || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))); } -static int asus_hwmon_fan_set_auto(struct asus_wmi *asus) +static int asus_fan_set_auto(struct asus_wmi *asus) { int status; - status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL); + status = asus_agfn_fan_speed_write(asus, 0, NULL); if (status) return -ENXIO; - asus->asus_hwmon_fan_manual_mode = false; - return 0; } -static int asus_hwmon_fan_rpm_show(struct device *dev, int fan) -{ - struct asus_wmi *asus = dev_get_drvdata(dev); - int value; - int ret; - - /* no speed readable on manual mode */ - if (asus->asus_hwmon_fan_manual_mode) - return -ENXIO; - - ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value); - if (ret) { - pr_warn("reading fan speed failed: %d\n", ret); - return -ENXIO; - } - - return value; -} - -static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value) -{ - int err; - - if (asus->asus_hwmon_pwm >= 0) { - *value = asus->asus_hwmon_pwm; - return; - } - - err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, value); - if (err < 0) - return; - - *value &= 0xFF; - - if (*value == 1) /* Low Speed */ - *value = 85; - else if (*value == 2) - *value = 170; - else if (*value == 3) - *value = 255; - else if (*value) { - pr_err("Unknown fan speed %#x\n", *value); - *value = -1; - } -} - static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); + int err; int value; - asus_hwmon_pwm_show(asus, 0, &value); + /* If we already set a value then just return it */ + if (asus->agfn_pwm >= 0) + return sprintf(buf, "%d\n", asus->agfn_pwm); + + /* + * If we haven't set already set a value through the AGFN interface, + * we read a current value through the (now-deprecated) FAN_CTRL device. + */ + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); + if (err < 0) + return err; + + value &= 0xFF; + + if (value == 1) /* Low Speed */ + value = 85; + else if (value == 2) + value = 170; + else if (value == 3) + value = 255; + else if (value) { + pr_err("Unknown fan speed %#x\n", value); + value = -1; + } return sprintf(buf, "%d\n", value); } @@ -1284,11 +1279,11 @@ static ssize_t pwm1_store(struct device *dev, value = clamp(value, 0, 255); - state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value); + state = asus_agfn_fan_speed_write(asus, 1, &value); if (state) pr_warn("Setting fan speed failed: %d\n", state); else - asus->asus_hwmon_fan_manual_mode = true; + asus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL; return count; } @@ -1297,10 +1292,21 @@ static ssize_t fan1_input_show(struct device *dev, struct device_attribute *attr, char *buf) { - int value = asus_hwmon_fan_rpm_show(dev, 0); + struct asus_wmi *asus = dev_get_drvdata(dev); + int value; + int ret; + + /* no speed readable on manual mode */ + if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL) + return -ENXIO; + + ret = asus_agfn_fan_speed_read(asus, 1, &value); + if (ret) { + pr_warn("reading fan speed failed: %d\n", ret); + return -ENXIO; + } return sprintf(buf, "%d\n", value < 0 ? -1 : value*100); - } static ssize_t pwm1_enable_show(struct device *dev, @@ -1309,10 +1315,7 @@ static ssize_t pwm1_enable_show(struct device *dev, { struct asus_wmi *asus = dev_get_drvdata(dev); - if (asus->asus_hwmon_fan_manual_mode) - return sprintf(buf, "%d\n", ASUS_FAN_CTRL_MANUAL); - - return sprintf(buf, "%d\n", ASUS_FAN_CTRL_AUTO); + return sprintf(buf, "%d\n", asus->fan_pwm_mode); } static ssize_t pwm1_enable_store(struct device *dev, @@ -1329,14 +1332,21 @@ static ssize_t pwm1_enable_store(struct device *dev, if (ret) return ret; - if (state == ASUS_FAN_CTRL_MANUAL) - asus->asus_hwmon_fan_manual_mode = true; - else - status = asus_hwmon_fan_set_auto(asus); + switch (state) { + case ASUS_FAN_CTRL_MANUAL: + break; - if (status) - return status; + case ASUS_FAN_CTRL_AUTO: + status = asus_fan_set_auto(asus); + if (status) + return status; + break; + default: + return -EINVAL; + } + + asus->fan_pwm_mode = state; return count; } @@ -1389,59 +1399,31 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); struct asus_wmi *asus = dev_get_drvdata(dev->parent); - int dev_id = -1; - int fan_attr = -1; u32 value = ASUS_WMI_UNSUPPORTED_METHOD; - bool ok = true; - - if (attr == &dev_attr_pwm1.attr) - dev_id = ASUS_WMI_DEVID_FAN_CTRL; - else if (attr == &dev_attr_temp1_input.attr) - dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; - if (attr == &dev_attr_fan1_input.attr || attr == &dev_attr_fan1_label.attr || attr == &dev_attr_pwm1.attr || attr == &dev_attr_pwm1_enable.attr) { - fan_attr = 1; - } + if (asus->fan_type == FAN_TYPE_NONE) + return 0; + } else if (attr == &dev_attr_temp1_input.attr) { + int err = asus_wmi_get_devstate(asus, + ASUS_WMI_DEVID_THERMAL_CTRL, + &value); - if (dev_id != -1) { - int err = asus_wmi_get_devstate(asus, dev_id, &value); - - if (err < 0 && fan_attr == -1) + if (err < 0) return 0; /* can't return negative here */ - } - if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) { - /* - * We need to find a better way, probably using sfun, - * bits or spec ... - * Currently we disable it if: - * - ASUS_WMI_UNSUPPORTED_METHOD is returned - * - reverved bits are non-zero - * - sfun and presence bit are not set - */ - if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000 - || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) - ok = false; - else - ok = fan_attr <= asus->asus_hwmon_num_fans; - } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { /* * If the temperature value in deci-Kelvin is near the absolute * zero temperature, something is clearly wrong */ if (value == 0 || value == 1) - ok = false; - } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { - ok = true; - } else { - ok = false; + return 0; } - return ok ? attr->mode : 0; + return attr->mode; } static const struct attribute_group hwmon_attribute_group = { @@ -1467,21 +1449,16 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) static int asus_wmi_fan_init(struct asus_wmi *asus) { - int status; + asus->fan_type = FAN_TYPE_NONE; + asus->agfn_pwm = -1; - asus->asus_hwmon_pwm = -1; - asus->asus_hwmon_num_fans = -1; - asus->asus_hwmon_fan_manual_mode = false; - - status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans); - if (status) { - asus->asus_hwmon_num_fans = 0; - pr_warn("Could not determine number of fans: %d\n", status); - return -ENXIO; + if (asus_wmi_has_agfn_fan(asus)) { + asus->fan_type = FAN_TYPE_AGFN; + asus_fan_set_auto(asus); + asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO; } - pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); - return 0; + return asus->fan_type != FAN_TYPE_NONE; } /* Fan mode *******************************************************************/ @@ -2333,7 +2310,6 @@ static int asus_wmi_add(struct platform_device *pdev) goto fail_input; err = asus_wmi_fan_init(asus); /* probably no problems on error */ - asus_hwmon_fan_set_auto(asus); err = asus_wmi_hwmon_init(asus); if (err) @@ -2425,7 +2401,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); asus_wmi_sysfs_exit(asus->platform_device); - asus_hwmon_fan_set_auto(asus); + asus_fan_set_auto(asus); kfree(asus); return 0; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 4802cd2c7309..5ae9c062a1f6 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -12,7 +12,7 @@ #define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ #define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ #define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ -#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* FaN? */ +#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* Atk Generic FuNction */ #define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ #define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ @@ -72,7 +72,7 @@ /* Fan, Thermal */ #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 -#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 +#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ /* Power */ #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 From f1fc032192d2872caef69fb899e939bf7cc24fc7 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 29 Jul 2019 16:27:38 +0800 Subject: [PATCH 14/53] platform/x86: asus-wmi: add a helper for device presence Factor out the WLAN LED and lightbar LED presence checks into a helper function, which will also be used by the upcoming CPU fan device support. Signed-off-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index fc2939ac1cfe..25f1e256c442 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -358,6 +358,14 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } +static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) +{ + u32 retval; + int status = asus_wmi_get_devstate(asus, dev_id, &retval); + + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); +} + /* LEDs ***********************************************************************/ /* @@ -503,15 +511,6 @@ static int wlan_led_unknown_state(struct asus_wmi *asus) return result & ASUS_WMI_DSTS_UNKNOWN_BIT; } -static int wlan_led_presence(struct asus_wmi *asus) -{ - u32 result; - - asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result); - - return result & ASUS_WMI_DSTS_PRESENCE_BIT; -} - static void wlan_led_update(struct work_struct *work) { int ctrl_param; @@ -578,15 +577,6 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; } -static int lightbar_led_presence(struct asus_wmi *asus) -{ - u32 result; - - asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); - - return result & ASUS_WMI_DSTS_PRESENCE_BIT; -} - static void asus_wmi_led_exit(struct asus_wmi *asus) { if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) @@ -637,7 +627,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (wlan_led_presence(asus) && (asus->driver->quirks->wapf > 0)) { + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED) + && (asus->driver->quirks->wapf > 0)) { INIT_WORK(&asus->wlan_led_work, wlan_led_update); asus->wlan_led.name = "asus::wlan"; @@ -654,7 +645,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (lightbar_led_presence(asus)) { + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_LIGHTBAR)) { INIT_WORK(&asus->lightbar_led_work, lightbar_led_update); asus->lightbar_led.name = "asus::lightbar"; From e3168b874321d04c160c9eb937919eb926ae232f Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 29 Jul 2019 16:27:39 +0800 Subject: [PATCH 15/53] platform/x86: asus-wmi: fix CPU fan control on recent products Previously, asus-wmi was using the AGFN interface and FAN_CTRL device for CPU fan control. However, this code has been found to be not fully working on some recent products, and having checked the spec, these interfaces are marked as being removed from future products currently in development. The replacement appears to be the CPU_FAN device, added in spec version 8.3 (March 2014) and present on many modern Asus laptops. Add support for this device, and use it whenever it is detected. The older approach based on AGFN and FAN_CTRL is used as a fallback on products that do not have such device. Other than switching between automatic and full speed, there is no fan speed control through this new interface. Signed-off-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 125 ++++++++++++++++----- include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 101 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 25f1e256c442..34dfbed65332 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -67,6 +67,7 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_SFUN_WRITE 0x07 /* Based on standard hwmon pwmX_enable values */ +#define ASUS_FAN_CTRL_FULLSPEED 0 #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 @@ -153,6 +154,7 @@ struct asus_rfkill { enum fan_type { FAN_TYPE_NONE = 0, FAN_TYPE_AGFN, /* deprecated on newer platforms */ + FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */ }; struct asus_wmi { @@ -1211,10 +1213,29 @@ static bool asus_wmi_has_agfn_fan(struct asus_wmi *asus) static int asus_fan_set_auto(struct asus_wmi *asus) { int status; + u32 retval; - status = asus_agfn_fan_speed_write(asus, 0, NULL); - if (status) + switch (asus->fan_type) { + case FAN_TYPE_SPEC83: + status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL, + 0, &retval); + if (status) + return status; + + if (retval != 1) + return -EIO; + break; + + case FAN_TYPE_AGFN: + status = asus_agfn_fan_speed_write(asus, 0, NULL); + if (status) + return -ENXIO; + break; + + default: return -ENXIO; + } + return 0; } @@ -1287,13 +1308,29 @@ static ssize_t fan1_input_show(struct device *dev, int value; int ret; - /* no speed readable on manual mode */ - if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL) - return -ENXIO; + switch (asus->fan_type) { + case FAN_TYPE_SPEC83: + ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL, + &value); + if (ret < 0) + return ret; - ret = asus_agfn_fan_speed_read(asus, 1, &value); - if (ret) { - pr_warn("reading fan speed failed: %d\n", ret); + value &= 0xffff; + break; + + case FAN_TYPE_AGFN: + /* no speed readable on manual mode */ + if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL) + return -ENXIO; + + ret = asus_agfn_fan_speed_read(asus, 1, &value); + if (ret) { + pr_warn("reading fan speed failed: %d\n", ret); + return -ENXIO; + } + break; + + default: return -ENXIO; } @@ -1306,6 +1343,15 @@ static ssize_t pwm1_enable_show(struct device *dev, { struct asus_wmi *asus = dev_get_drvdata(dev); + /* + * Just read back the cached pwm mode. + * + * For the CPU_FAN device, the spec indicates that we should be + * able to read the device status and consult bit 19 to see if we + * are in Full On or Automatic mode. However, this does not work + * in practice on X532FL at least (the bit is always 0) and there's + * also nothing in the DSDT to indicate that this behaviour exists. + */ return sprintf(buf, "%d\n", asus->fan_pwm_mode); } @@ -1316,25 +1362,48 @@ static ssize_t pwm1_enable_store(struct device *dev, struct asus_wmi *asus = dev_get_drvdata(dev); int status = 0; int state; + int value; int ret; + u32 retval; ret = kstrtouint(buf, 10, &state); if (ret) return ret; - switch (state) { - case ASUS_FAN_CTRL_MANUAL: - break; + if (asus->fan_type == FAN_TYPE_SPEC83) { + switch (state) { /* standard documented hwmon values */ + case ASUS_FAN_CTRL_FULLSPEED: + value = 1; + break; + case ASUS_FAN_CTRL_AUTO: + value = 0; + break; + default: + return -EINVAL; + } - case ASUS_FAN_CTRL_AUTO: - status = asus_fan_set_auto(asus); - if (status) - return status; - break; + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL, + value, &retval); + if (ret) + return ret; - default: - return -EINVAL; + if (retval != 1) + return -EIO; + } else if (asus->fan_type == FAN_TYPE_AGFN) { + switch (state) { + case ASUS_FAN_CTRL_MANUAL: + break; + + case ASUS_FAN_CTRL_AUTO: + status = asus_fan_set_auto(asus); + if (status) + return status; + break; + + default: + return -EINVAL; + } } asus->fan_pwm_mode = state; @@ -1392,9 +1461,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, struct asus_wmi *asus = dev_get_drvdata(dev->parent); u32 value = ASUS_WMI_UNSUPPORTED_METHOD; - if (attr == &dev_attr_fan1_input.attr + if (attr == &dev_attr_pwm1.attr) { + if (asus->fan_type != FAN_TYPE_AGFN) + return 0; + } else if (attr == &dev_attr_fan1_input.attr || attr == &dev_attr_fan1_label.attr - || attr == &dev_attr_pwm1.attr || attr == &dev_attr_pwm1_enable.attr) { if (asus->fan_type == FAN_TYPE_NONE) return 0; @@ -1443,13 +1514,17 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) asus->fan_type = FAN_TYPE_NONE; asus->agfn_pwm = -1; - if (asus_wmi_has_agfn_fan(asus)) { + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL)) + asus->fan_type = FAN_TYPE_SPEC83; + else if (asus_wmi_has_agfn_fan(asus)) asus->fan_type = FAN_TYPE_AGFN; - asus_fan_set_auto(asus); - asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO; - } - return asus->fan_type != FAN_TYPE_NONE; + if (asus->fan_type == FAN_TYPE_NONE) + return -ENODEV; + + asus_fan_set_auto(asus); + asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO; + return 0; } /* Fan mode *******************************************************************/ diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 5ae9c062a1f6..409e16064f4b 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -73,6 +73,7 @@ /* Fan, Thermal */ #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ +#define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013 /* Power */ #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 From 50944213062f7605ef79604418c7e3468d97b109 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:52 -0700 Subject: [PATCH 16/53] platform/x86: intel_pmc_ipc: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Andy Shevchenko Cc: Greg Kroah-Hartman Cc: Enric Balletbo i Serra Cc: Hans de Goede Cc: "Darren Hart (VMware)" Cc: Roman Kiryanov Cc: Vadim Pasternak Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_ipc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 55037ff258f8..5c1da2bb1435 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -936,10 +936,8 @@ static int ipc_plat_probe(struct platform_device *pdev) spin_lock_init(&ipcdev.gcr_lock); ipcdev.irq = platform_get_irq(pdev, 0); - if (ipcdev.irq < 0) { - dev_err(&pdev->dev, "Failed to get irq\n"); + if (ipcdev.irq < 0) return -EINVAL; - } ret = ipc_plat_get_res(pdev); if (ret) { From eaae882c4a77c8aa2ca20d4447aeb81d9d5eec97 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:53 -0700 Subject: [PATCH 17/53] platform/mellanox: mlxreg-hotplug: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Andy Shevchenko Cc: Greg Kroah-Hartman Cc: Enric Balletbo i Serra Cc: Hans de Goede Cc: "Darren Hart (VMware)" Cc: Roman Kiryanov Cc: Vadim Pasternak Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Andy Shevchenko --- drivers/platform/mellanox/mlxreg-hotplug.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index f85a1b9d129b..706207d192ae 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -642,11 +642,8 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) priv->irq = pdata->irq; } else { priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(&pdev->dev, "Failed to get platform irq: %d\n", - priv->irq); + if (priv->irq < 0) return priv->irq; - } } priv->regmap = pdata->regmap; From b2dd2d9a49e0a9251badde3363efc9e7be2d0bde Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:54 -0700 Subject: [PATCH 18/53] platform/x86: intel_bxtwc_tmu: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Andy Shevchenko Cc: Greg Kroah-Hartman Cc: Enric Balletbo i Serra Cc: Hans de Goede Cc: "Darren Hart (VMware)" Cc: Roman Kiryanov Cc: Vadim Pasternak Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_bxtwc_tmu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c index 951c105bafc1..7ccf583649e6 100644 --- a/drivers/platform/x86/intel_bxtwc_tmu.c +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -60,11 +60,8 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev) wctmu->regmap = pmic->regmap; irq = platform_get_irq(pdev, 0); - - if (irq < 0) { - dev_err(&pdev->dev, "invalid irq %d\n", irq); + if (irq < 0) return irq; - } regmap_irq_chip = pmic->irq_chip_data_tmu; virq = regmap_irq_get_virq(regmap_irq_chip, irq); From f839b4b549a65e1c611f911654012ac65e6d1610 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:55 -0700 Subject: [PATCH 19/53] platform/x86: intel_int0002_vgpio: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Andy Shevchenko Cc: Greg Kroah-Hartman Cc: Enric Balletbo i Serra Cc: Hans de Goede Cc: "Darren Hart (VMware)" Cc: Roman Kiryanov Cc: Vadim Pasternak Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index d9542c661ddc..4f3f30152a27 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -166,10 +166,8 @@ static int int0002_probe(struct platform_device *pdev) return -ENODEV; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "Error getting IRQ: %d\n", irq); + if (irq < 0) return irq; - } chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) From a0c809792183228a1d269d4112a2b64f2c7f8f9f Mon Sep 17 00:00:00 2001 From: Giang Le Date: Sat, 3 Aug 2019 21:12:22 +0700 Subject: [PATCH 20/53] platform/x86: touchscreen_dmi: Add info for the Chuwi Surbook Mini tablet Add touchscreen platform data for the Chuwi Surbook Mini tablet. Signed-off-by: Giang Le Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 4370e4add83a..72535b0268eb 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -136,6 +136,22 @@ static const struct ts_dmi_data chuwi_vi10_data = { .properties = chuwi_vi10_props, }; +static const struct property_entry chuwi_surbook_mini_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 88), + PROPERTY_ENTRY_U32("touchscreen-min-y", 13), + PROPERTY_ENTRY_U32("touchscreen-size-x", 2040), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1524), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-surbook-mini.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + { } +}; + +static const struct ts_dmi_data chuwi_surbook_mini_data = { + .acpi_name = "MSSL1680:00", + .properties = chuwi_surbook_mini_props, +}; + static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 9), PROPERTY_ENTRY_U32("touchscreen-min-y", 10), @@ -646,6 +662,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "S165"), }, }, + { + /* Chuwi Surbook Mini (CWI540) */ + .driver_data = (void *)&chuwi_surbook_mini_data, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "C3W6_AP108_4G"), + }, + }, { /* Connect Tablet 9 */ .driver_data = (void *)&connect_tablet9_data, From f078d053c8696d4b0884ee81d132dd22fca6a5c9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 4 Aug 2019 15:40:23 +0200 Subject: [PATCH 21/53] platform/x86: touchscreen_dmi: Add info for the Irbis TW90 tablet Add touchscreen info for the Irbis TW90 tablet. Reported-by: russianneuromancer@ya.ru Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 72535b0268eb..0ccd7990454c 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -246,6 +246,24 @@ static const struct ts_dmi_data gp_electronic_t701_data = { .properties = gp_electronic_t701_props, }; +static const struct property_entry irbis_tw90_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1720), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), + PROPERTY_ENTRY_U32("touchscreen-min-x", 8), + PROPERTY_ENTRY_U32("touchscreen-min-y", 14), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-irbis_tw90.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data irbis_tw90_data = { + .acpi_name = "MSSL1680:00", + .properties = irbis_tw90_props, +}; + static const struct property_entry itworks_tw891_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 1), PROPERTY_ENTRY_U32("touchscreen-min-y", 5), @@ -732,6 +750,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "itWORKS.G.WI71C.JGBMRB"), }, }, + { + /* Irbis TW90 */ + .driver_data = (void *)&irbis_tw90_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW90"), + }, + }, { /* I.T.Works TW891 */ .driver_data = (void *)&itworks_tw891_data, From 02a5e9bc86dee8c7698ac2cfd9e29650eb318442 Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Sun, 4 Aug 2019 17:21:43 +0200 Subject: [PATCH 22/53] platform/x86: asus-nb-wmi: Support ALS on the Zenbook UX430UNR This patch adds support for ALS on the Zenbook UX430UNR to the asus_nb_wmi driver. Signed-off-by: Kristian Klausen Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-nb-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 2ebde0174937..b361c73636a4 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -402,6 +402,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_forceals, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. UX430UNR", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "UX430UNR"), + }, + .driver_data = &quirk_asus_forceals, + }, {}, }; From d507a54f5865d8dcbdd16c66a1a2da15640878ca Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Mon, 5 Aug 2019 21:23:05 +0200 Subject: [PATCH 23/53] platform/x86: asus-wmi: Add support for charge threshold Most newer ASUS laptops supports limiting the battery charge level, which help prolonging the battery life. Tested on a Zenbook UX430UNR. Signed-off-by: Kristian Klausen Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 48 ++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 49 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 34dfbed65332..22ae350e0a96 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -195,6 +195,8 @@ struct asus_wmi { u8 fan_boost_mode_mask; u8 fan_boost_mode; + int charge_threshold; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -2075,6 +2077,43 @@ static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_WO(cpufv); + +static ssize_t charge_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int value, ret, rv; + + ret = kstrtouint(buf, 10, &value); + + if (!count || ret != 0) + return -EINVAL; + if (value < 0 || value > 100) + return -EINVAL; + + asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, value, &rv); + + if (rv != 1) + return -EIO; + + /* There isn't any method in the DSDT to read the threshold, so we + * save the threshold. + */ + asus->charge_threshold = value; + return count; +} + +static ssize_t charge_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", asus->charge_threshold); +} + +static DEVICE_ATTR_RW(charge_threshold); + static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, &dev_attr_camera.attr, @@ -2083,6 +2122,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, + &dev_attr_charge_threshold.attr, NULL }; @@ -2106,6 +2146,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_ALS_ENABLE; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; + else if (attr == &dev_attr_charge_threshold.attr) + devid = ASUS_WMI_CHARGE_THRESHOLD; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2434,6 +2476,12 @@ static int asus_wmi_add(struct platform_device *pdev) } asus_wmi_debugfs_init(asus); + /* The charge threshold is only reset when the system is power cycled, + * and we can't get the current threshold so let set it to 100% on + * module load. + */ + asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, 100, NULL); + asus->charge_threshold = 100; return 0; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 409e16064f4b..53934ef38d98 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -61,6 +61,7 @@ /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 +#define ASUS_WMI_CHARGE_THRESHOLD 0x00120057 /* Storage */ #define ASUS_WMI_DEVID_CARDREADER 0x00080013 From 84d8e80b0a3612aad746837e95d8efe324fdc5c6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Aug 2019 12:46:25 +0300 Subject: [PATCH 24/53] platform/x86: asus-wmi: Refactor charge_threshold_store() There are few issues with the current code: - the error code from kstrtouint() is shadowed - the error code from asus_wmi_set_devstate() is ignored - the extra check against 0 for count (this is guaranteed by sysfs) Fix these issues by doing a slight refactoring of charge_threshold_store(). Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 22ae350e0a96..1a76d878852f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2086,13 +2086,15 @@ static ssize_t charge_threshold_store(struct device *dev, int value, ret, rv; ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; - if (!count || ret != 0) - return -EINVAL; if (value < 0 || value > 100) return -EINVAL; - asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, value, &rv); + ret = asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, value, &rv); + if (!ret) + return ret; if (rv != 1) return -EIO; From a5556fa1107d5b9cda75632c6ece1b4612dd1607 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Aug 2019 14:15:55 +0300 Subject: [PATCH 25/53] platform/x86: asus-wmi: Replace sscanf() with kstrtoint() The use of sscanf() is an overkill here. Moreover, there is no need to check for count to be 0, since it's guaranteed by sysfs not to be. Taking above into account, replace sscanf() with kstrtoint() calls. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 1a76d878852f..6b1f661a6ce6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1989,32 +1989,25 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) /* Sysfs **********************************************************************/ -static int parse_arg(const char *buf, unsigned long count, int *val) -{ - if (!count) - return 0; - if (sscanf(buf, "%i", val) != 1) - return -EINVAL; - return count; -} - static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid, const char *buf, size_t count) { u32 retval; - int rv, err, value; + int err, value; value = asus_wmi_get_devstate_simple(asus, devid); if (value < 0) return value; - rv = parse_arg(buf, count, &value); - err = asus_wmi_set_devstate(devid, value, &retval); + err = kstrtoint(buf, 0, &value); + if (err) + return err; + err = asus_wmi_set_devstate(devid, value, &retval); if (err < 0) return err; - return rv; + return count; } static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf) @@ -2063,8 +2056,10 @@ static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, { int value, rv; - if (!count || sscanf(buf, "%i", &value) != 1) - return -EINVAL; + rv = kstrtoint(buf, 0, &value); + if (rv) + return rv; + if (value < 0 || value > 2) return -EINVAL; From 2275752004ab216c667cfdba378c5199f9068e96 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Aug 2019 14:10:03 +0300 Subject: [PATCH 26/53] platform/x86: asus-wmi: Use clamp_val() instead of open coded variant There is no need to open code clamp_val() macro implementation. Replace the corresponding lines with direct call to clamp_val(). Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6b1f661a6ce6..42dc2db775d1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -464,12 +464,7 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) asus = container_of(led_cdev, struct asus_wmi, kbd_led); max_level = asus->kbd_led.max_brightness; - if (value > max_level) - value = max_level; - else if (value < 0) - value = 0; - - asus->kbd_led_wk = value; + asus->kbd_led_wk = clamp_val(value, 0, max_level); kbd_led_update(asus); } From 127e1dfc66cd0ea9ebd0fbf95c817b1b8d1d408b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Aug 2019 14:07:17 +0300 Subject: [PATCH 27/53] platform/x86: asus-wmi: Drop indentation level by inverting conditionals We have extra indentation level where it can be avoided by changing conditional to the inverted one. Do it here for three such locations in the driver. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 43 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 42dc2db775d1..d538b8ce1a3a 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -445,15 +445,14 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) if (retval == 0x8000) retval = 0; - if (retval >= 0) { - if (level) - *level = retval & 0x7F; - if (env) - *env = (retval >> 8) & 0x7F; - retval = 0; - } + if (retval < 0) + return retval; - return retval; + if (level) + *level = retval & 0x7F; + if (env) + *env = (retval >> 8) & 0x7F; + return 0; } static void do_kbd_led_set(struct led_classdev *led_cdev, int value) @@ -767,16 +766,14 @@ static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node) acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); - - if (ACPI_SUCCESS(status)) { - status = acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - asus_rfkill_notify, asus); - if (ACPI_FAILURE(status)) - pr_warn("Failed to register notify on %s\n", node); - } else + if (ACPI_FAILURE(status)) return -ENODEV; + status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + asus_rfkill_notify, asus); + if (ACPI_FAILURE(status)) + pr_warn("Failed to register notify on %s\n", node); + return 0; } @@ -786,15 +783,13 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); + if (ACPI_FAILURE(status)) + return; - if (ACPI_SUCCESS(status)) { - status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - asus_rfkill_notify); - if (ACPI_FAILURE(status)) - pr_err("Error removing rfkill notify handler %s\n", - node); - } + status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + asus_rfkill_notify); + if (ACPI_FAILURE(status)) + pr_err("Error removing rfkill notify handler %s\n", node); } static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, From 109e8adfbc665a3e463043cefd5d17e552dc020c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Aug 2019 14:17:34 +0300 Subject: [PATCH 28/53] platform/x86: asus-wmi: Remove unnecessary blank lines Remove blank lines where they are not needed. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index d538b8ce1a3a..14467748b343 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -341,7 +341,6 @@ static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, int err; err = asus_wmi_get_devstate(asus, dev_id, &retval); - if (err < 0) return err; @@ -493,7 +492,6 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); retval = kbd_led_read(asus, &value, NULL); - if (retval < 0) return retval; @@ -1277,7 +1275,6 @@ static ssize_t pwm1_store(struct device *dev, int ret; ret = kstrtouint(buf, 10, &value); - if (ret) return ret; @@ -1359,7 +1356,6 @@ static ssize_t pwm1_enable_store(struct device *dev, u32 retval; ret = kstrtouint(buf, 10, &state); - if (ret) return ret; @@ -1418,7 +1414,6 @@ static ssize_t asus_hwmon_temp1(struct device *dev, int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value); - if (err < 0) return err; @@ -1557,7 +1552,6 @@ static int fan_boost_mode_write(struct asus_wmi *asus) pr_info("Set fan boost mode: %u\n", value); err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value, &retval); - if (err) { pr_warn("Failed to set fan boost mode: %d\n", err); return err; @@ -1640,6 +1634,7 @@ static DEVICE_ATTR_RW(fan_boost_mode); static int read_backlight_power(struct asus_wmi *asus) { int ret; + if (asus->driver->quirks->store_backlight_power) ret = !asus->driver->panel_power; else @@ -1658,7 +1653,6 @@ static int read_brightness_max(struct asus_wmi *asus) int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); - if (err < 0) return err; @@ -1678,7 +1672,6 @@ static int read_brightness(struct backlight_device *bd) int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); - if (err < 0) return err; @@ -1768,7 +1761,6 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) return max; power = read_backlight_power(asus); - if (power == -ENODEV) power = FB_BLANK_UNBLANK; else if (power < 0) @@ -1934,7 +1926,6 @@ static void asus_wmi_notify(u32 value, void *context) for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { code = asus_wmi_get_event_code(value); - if (code < 0) { pr_warn("Failed to get notify code: %d\n", code); return; @@ -1963,7 +1954,6 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK); - if (code < 0) { pr_warn("Failed to get event during flush: %d\n", code); return code; @@ -2252,7 +2242,6 @@ static int show_dsts(struct seq_file *m, void *data) u32 retval = -1; err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); - if (err < 0) return err; @@ -2269,7 +2258,6 @@ static int show_devs(struct seq_file *m, void *data) err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, &retval); - if (err < 0) return err; From 07779c33a7282366f979e384de263e42aa422ada Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 2 Sep 2019 17:38:50 +0300 Subject: [PATCH 29/53] platform/x86: asus-wmi: Fix condition in charge_threshold_store() This error handling is reversed so we return early. Fixes: 84d8e80b0a36 ("platform/x86: asus-wmi: Refactor charge_threshold_store()") Signed-off-by: Dan Carpenter Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 14467748b343..848b23764fc3 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2068,7 +2068,7 @@ static ssize_t charge_threshold_store(struct device *dev, return -EINVAL; ret = asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, value, &rv); - if (!ret) + if (ret) return ret; if (rv != 1) From 7d505758b1e556cdf65a5e451744fe0ae8063d17 Mon Sep 17 00:00:00 2001 From: "M. Vefa Bicakci" Date: Thu, 15 Aug 2019 21:41:39 -0400 Subject: [PATCH 30/53] platform/x86: intel_pmc_core: Do not ioremap RAM On a Xen-based PVH virtual machine with more than 4 GiB of RAM, intel_pmc_core fails initialization with the following warning message from the kernel, indicating that the driver is attempting to ioremap RAM: ioremap on RAM at 0x00000000fe000000 - 0x00000000fe001fff WARNING: CPU: 1 PID: 434 at arch/x86/mm/ioremap.c:186 __ioremap_caller.constprop.0+0x2aa/0x2c0 ... Call Trace: ? pmc_core_probe+0x87/0x2d0 [intel_pmc_core] pmc_core_probe+0x87/0x2d0 [intel_pmc_core] This issue appears to manifest itself because of the following fallback mechanism in the driver: if (lpit_read_residency_count_address(&slp_s0_addr)) pmcdev->base_addr = PMC_BASE_ADDR_DEFAULT; The validity of address PMC_BASE_ADDR_DEFAULT (i.e., 0xFE000000) is not verified by the driver, which is what this patch introduces. With this patch, if address PMC_BASE_ADDR_DEFAULT is in RAM, then the driver will not attempt to ioremap the aforementioned address. Signed-off-by: M. Vefa Bicakci Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index c510d0d72475..3b6b8dcc4767 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -878,10 +878,14 @@ static int pmc_core_probe(struct platform_device *pdev) if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids)) pmcdev->map = &cnp_reg_map; - if (lpit_read_residency_count_address(&slp_s0_addr)) + if (lpit_read_residency_count_address(&slp_s0_addr)) { pmcdev->base_addr = PMC_BASE_ADDR_DEFAULT; - else + + if (page_is_ram(PHYS_PFN(pmcdev->base_addr))) + return -ENODEV; + } else { pmcdev->base_addr = slp_s0_addr - pmcdev->map->slp_s0_offset; + } pmcdev->regbase = ioremap(pmcdev->base_addr, pmcdev->map->regmap_length); From 0b43e41e93815ecd9616759cf5d64d3a7be8e6fb Mon Sep 17 00:00:00 2001 From: "M. Vefa Bicakci" Date: Thu, 15 Aug 2019 21:41:40 -0400 Subject: [PATCH 31/53] platform/x86: intel_pmc_core_pltdrv: Module removal warning fix Prior to this commit, removing the intel_pmc_core_pltdrv module would cause the following warning: Device 'intel_pmc_core.0' does not have a release() function, it is broken and must be fixed. See Documentation/kobject.txt. WARNING: CPU: 0 PID: 2202 at drivers/base/core.c:1238 device_release+0x6f/0x80 This commit hence adds an empty release function for the driver. Signed-off-by: M. Vefa Bicakci Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core_pltdrv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/intel_pmc_core_pltdrv.c b/drivers/platform/x86/intel_pmc_core_pltdrv.c index a8754a6db1b8..186540014c48 100644 --- a/drivers/platform/x86/intel_pmc_core_pltdrv.c +++ b/drivers/platform/x86/intel_pmc_core_pltdrv.c @@ -18,8 +18,16 @@ #include #include +static void intel_pmc_core_release(struct device *dev) +{ + /* Nothing to do. */ +} + static struct platform_device pmc_core_device = { .name = "intel_pmc_core", + .dev = { + .release = intel_pmc_core_release, + }, }; /* From ad0d315b4d4e7138f43acf03308192ec00e9614d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 4 Sep 2019 08:42:30 +0200 Subject: [PATCH 32/53] platform/x86: pmc_atom: Add Siemens SIMATIC IPC227E to critclk_systems DMI table The SIMATIC IPC227E uses the PMC clock for on-board components and gets stuck during boot if the clock is disabled. Therefore, add this device to the critical systems list. Fixes: 648e921888ad ("clk: x86: Stop marking clocks as CLK_IS_CRITICAL") Signed-off-by: Jan Kiszka Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pmc_atom.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index aa53648a2214..9aca5e7ce6d0 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -415,6 +415,13 @@ static const struct dmi_system_id critclk_systems[] = { DMI_MATCH(DMI_BOARD_NAME, "CB6363"), }, }, + { + .ident = "SIMATIC IPC227E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), + DMI_MATCH(DMI_PRODUCT_VERSION, "6ES7647-8B"), + }, + }, { /*sentinel*/ } }; From 110ea1d833ad291272d52e0a40a06157a3d9ba17 Mon Sep 17 00:00:00 2001 From: Alexander Schremmer Date: Thu, 22 Aug 2019 13:48:33 +0200 Subject: [PATCH 33/53] platform/x86: thinkpad_acpi: Add ThinkPad PrivacyGuard This feature is found optionally in T480s, T490, T490s. The feature is called lcdshadow and visible via /proc/acpi/ibm/lcdshadow. The ACPI methods \_SB.PCI0.LPCB.EC.HKEY.{GSSS,SSSS,TSSS,CSSS} are available in these machines. They get, set, toggle or change the state apparently. The patch was tested on a 5.0 series kernel on a T480s. Signed-off-by: Alexander Schremmer Signed-off-by: Andy Shevchenko --- .../admin-guide/laptops/thinkpad-acpi.rst | 23 ++++ drivers/platform/x86/thinkpad_acpi.c | 105 ++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index adea0bf2acc5..822907dcc845 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -49,6 +49,7 @@ detailed description): - Fan control and monitoring: fan speed, fan enable/disable - WAN enable and disable - UWB enable and disable + - LCD Shadow (PrivacyGuard) enable and disable A compatibility table by model and feature is maintained on the web site, http://ibm-acpi.sf.net/. I appreciate any success or failure @@ -1409,6 +1410,28 @@ Sysfs notes Documentation/driver-api/rfkill.rst for details. +LCD Shadow control +------------------ + +procfs: /proc/acpi/ibm/lcdshadow + +Some newer T480s and T490s ThinkPads provide a feature called +PrivacyGuard. By turning this feature on, the usable vertical and +horizontal viewing angles of the LCD can be limited (as if some privacy +screen was applied manually in front of the display). + +procfs notes +^^^^^^^^^^^^ + +The available commands are:: + + echo '0' >/proc/acpi/ibm/lcdshadow + echo '1' >/proc/acpi/ibm/lcdshadow + +The first command ensures the best viewing angle and the latter one turns +on the feature, restricting the viewing angles. + + EXPERIMENTAL: UWB ----------------- diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d379bdf98a0f..da794dcfdd92 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9711,6 +9711,107 @@ static struct ibm_struct battery_driver_data = { .exit = tpacpi_battery_exit, }; +/************************************************************************* + * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature + */ + +static int lcdshadow_state; + +static int lcdshadow_on_off(bool state) +{ + acpi_handle set_shadow_handle; + int output; + + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); + return -EIO; + } + + if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) + return -EIO; + + lcdshadow_state = state; + return 0; +} + +static int lcdshadow_set(bool on) +{ + if (lcdshadow_state < 0) + return lcdshadow_state; + if (lcdshadow_state == on) + return 0; + return lcdshadow_on_off(on); +} + +static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) +{ + acpi_handle get_shadow_handle; + int output; + + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) { + lcdshadow_state = -ENODEV; + return 0; + } + + if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) { + lcdshadow_state = -EIO; + return -EIO; + } + if (!(output & 0x10000)) { + lcdshadow_state = -ENODEV; + return 0; + } + lcdshadow_state = output & 0x1; + + return 0; +} + +static void lcdshadow_resume(void) +{ + if (lcdshadow_state >= 0) + lcdshadow_on_off(lcdshadow_state); +} + +static int lcdshadow_read(struct seq_file *m) +{ + if (lcdshadow_state < 0) { + seq_puts(m, "status:\t\tnot supported\n"); + } else { + seq_printf(m, "status:\t\t%d\n", lcdshadow_state); + seq_puts(m, "commands:\t0, 1\n"); + } + + return 0; +} + +static int lcdshadow_write(char *buf) +{ + char *cmd; + int state = -1; + + if (lcdshadow_state < 0) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "0") == 0) + state = 0; + else if (strlencmp(cmd, "1") == 0) + state = 1; + } + + if (state == -1) + return -EINVAL; + + return lcdshadow_set(state); +} + +static struct ibm_struct lcdshadow_driver_data = { + .name = "lcdshadow", + .resume = lcdshadow_resume, + .read = lcdshadow_read, + .write = lcdshadow_write, +}; + /**************************************************************************** **************************************************************************** * @@ -10192,6 +10293,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = tpacpi_battery_init, .data = &battery_driver_data, }, + { + .init = tpacpi_lcdshadow_init, + .data = &lcdshadow_driver_data, + }, }; static int __init set_ibm_param(const char *val, const struct kernel_param *kp) From 37d960a4d753605ca248baea577eaaa96f5dccf0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 18 Aug 2019 13:04:51 +0200 Subject: [PATCH 34/53] platform/x86: touchscreen_dmi: Add info for the Trekstor Primebook C11B 2-in-1 Add touchscreen info for the Trekstor Primebook C11B 2-in-1, note the C11B used the same touchscreen as the regular C11, so we only add a new DMI match. Cc: Thomas Hiller Reported-and-tested-by: Thomas Hiller Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 0ccd7990454c..1c7d8324ff5c 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -933,6 +933,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Primebook C11"), }, }, + { + /* Trekstor Primebook C11B (same touchscreen as the C11) */ + .driver_data = (void *)&trekstor_primebook_c11_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "PRIMEBOOK C11B"), + }, + }, { /* Trekstor Primebook C13 */ .driver_data = (void *)&trekstor_primebook_c13_data, From 09e7f2805c352dc38d13f663613663574a0584ad Mon Sep 17 00:00:00 2001 From: Yizhuo Date: Mon, 2 Sep 2019 12:52:49 -0700 Subject: [PATCH 35/53] platform/x86: compal-laptop: Initialize "value" in ec_read_u8() In function ec_read_u8(), variable "value" could be uninitialized if ec_read() fails. However, "value" is returned directly and used in its callers. This is potentially unsafe. Signed-off-by: Yizhuo Signed-off-by: Andy Shevchenko --- drivers/platform/x86/compal-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 09dfa6f48a1a..ab610376fdad 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -226,7 +226,7 @@ static const unsigned char pwm_lookup_table[256] = { /* General access */ static u8 ec_read_u8(u8 addr) { - u8 value; + u8 value = 0; ec_read(addr, &value); return value; } From 1bd43d0077b9a32a8b8059036471f3fc82dae342 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 23 Aug 2019 19:48:14 +0200 Subject: [PATCH 36/53] platform/x86: intel_int0002_vgpio: Fix wakeups not working on Cherry Trail Commit 871f1f2bcb01 ("platform/x86: intel_int0002_vgpio: Only implement irq_set_wake on Bay Trail") removed the irq_set_wake method from the struct irq_chip used on Cherry Trail, but it did not set IRQCHIP_SKIP_SET_WAKE causing kernel/irq/manage.c: set_irq_wake_real() to return -ENXIO. This causes the kernel to no longer see PME events reported through the INT0002 device as wakeup events. Which e.g. breaks wakeup by the (USB) keyboard on many Cherry Trail 2-in-1 devices. Cc: stable@vger.kernel.org Fixes: 871f1f2bcb01 ("platform/x86: intel_int0002_vgpio: Only implement irq_set_wake on Bay Trail") Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 4f3f30152a27..4df10d5b9db9 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -144,6 +144,7 @@ static struct irq_chip int0002_cht_irqchip = { * No set_wake, on CHT the IRQ is typically shared with the ACPI SCI * and we don't want to mess with the ACPI SCI irq settings. */ + .flags = IRQCHIP_SKIP_SET_WAKE, }; static const struct x86_cpu_id int0002_cpu_ids[] = { From 0ecee9e3d42231e5641671c5b6b1127d3efff1d4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 23 Aug 2019 19:48:15 +0200 Subject: [PATCH 37/53] platform/x86: intel_int0002_vgpio: Use device_init_wakeup Use device_init_wakeup and pm_wakeup_hard_event instead of directly calling pm_system_wakeup(). This is the preferred way to do this and this will allow the user to disable wakeup through INT0002 events through sysfs. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 4df10d5b9db9..d291bb482786 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -122,7 +122,7 @@ static irqreturn_t int0002_irq(int irq, void *data) generic_handle_irq(irq_find_mapping(chip->irq.domain, GPE0A_PME_B0_VIRT_GPIO_PIN)); - pm_system_wakeup(); + pm_wakeup_hard_event(chip->parent); return IRQ_HANDLED; } @@ -215,6 +215,13 @@ static int int0002_probe(struct platform_device *pdev) gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL); + device_init_wakeup(dev, true); + return 0; +} + +static int int0002_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, false); return 0; } @@ -230,6 +237,7 @@ static struct platform_driver int0002_driver = { .acpi_match_table = int0002_acpi_ids, }, .probe = int0002_probe, + .remove = int0002_remove, }; module_platform_driver(int0002_driver); From 010764b8856e5ee5056113704dc4b914ebc88f1d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Aug 2019 10:14:03 +0300 Subject: [PATCH 38/53] tools/power/x86/intel-speed-select: Fix a read overflow in isst_set_tdp_level_msr() The isst_send_msr_command() function will read 8 bytes but we are passing an address to an int (4 bytes) so it results in a read overflow. Fixes: 3fb4f7cd472c ("tools/power/x86: A tool to validate Intel Speed Select commands") Signed-off-by: Dan Carpenter Acked-by: Srinivas Pandruvada Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 8de4ac39a008..f724322856ed 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -190,6 +190,7 @@ int isst_get_get_trl(int cpu, int level, int avx_level, int *trl) int isst_set_tdp_level_msr(int cpu, int tdp_level) { + unsigned long long level = tdp_level; int ret; debug_printf("cpu: tdp_level via MSR %d\n", cpu, tdp_level); @@ -202,8 +203,7 @@ int isst_set_tdp_level_msr(int cpu, int tdp_level) if (tdp_level > 2) return -1; /* invalid value */ - ret = isst_send_msr_command(cpu, 0x64b, 1, - (unsigned long long *)&tdp_level); + ret = isst_send_msr_command(cpu, 0x64b, 1, &level); if (ret) return ret; From a8dc07448177dddd3947e89fe3904cb3169935c2 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:03 -0400 Subject: [PATCH 39/53] tools/power/x86/intel-speed-select: Fix package typo packag_ should be package_. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index f368b8323742..0d9a53a5da2d 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -133,7 +133,7 @@ static void format_and_print(FILE *outf, int level, char *header, char *value) last_level = level; } -static void print_packag_info(int cpu, FILE *outf) +static void print_package_info(int cpu, FILE *outf) { char header[256]; @@ -261,7 +261,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, char value[256]; int i, base_level = 1; - print_packag_info(cpu, outf); + print_package_info(cpu, outf); for (i = 0; i <= pkg_dev->levels; ++i) { struct isst_pkg_ctdp_level_info *ctdp_level; @@ -397,7 +397,7 @@ void isst_ctdp_display_information_end(FILE *outf) void isst_pbf_display_information(int cpu, FILE *outf, int level, struct isst_pbf_info *pbf_info) { - print_packag_info(cpu, outf); + print_package_info(cpu, outf); _isst_pbf_display_information(cpu, outf, level, pbf_info, 4); format_and_print(outf, 1, NULL, NULL); } @@ -406,7 +406,7 @@ void isst_fact_display_information(int cpu, FILE *outf, int level, int fact_bucket, int fact_avx, struct isst_fact_info *fact_info) { - print_packag_info(cpu, outf); + print_package_info(cpu, outf); _isst_fact_display_information(cpu, outf, level, fact_bucket, fact_avx, fact_info, 4); format_and_print(outf, 1, NULL, NULL); From 43774c0dccb405a0b63a10829d45db4b74c29e40 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:04 -0400 Subject: [PATCH 40/53] tools/power/x86/intel-speed-select: Fix help option typo Help is -h, not --h. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 91c5ad1685a1..d32af8210427 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1491,7 +1491,7 @@ static void usage(void) printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n"); printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features,\n"); printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power]\n"); - printf("\nFor help on each feature, use --h|--help\n"); + printf("\nFor help on each feature, use -h|--help\n"); printf("\tFor example: intel-speed-select perf-profile -h\n"); printf("\nFor additional help on each command for a feature, use --h|--help\n"); From 3ec2aef1b03eefeffe93817bd04068d3805d4a98 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:05 -0400 Subject: [PATCH 41/53] tools/power/x86/intel-speed-select: Fix cpu-count output I have a system with 28 threads/socket but intel-speed-select reports a cpu-count of 29. Fix an off-by-one error in the cpu_count() function. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index d32af8210427..f81a28c6b586 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -304,7 +304,7 @@ static void set_cpu_present_cpu_mask(void) int get_cpu_count(int pkg_id, int die_id) { if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE) - return cpu_cnt[pkg_id][die_id] + 1; + return cpu_cnt[pkg_id][die_id]; return 0; } From dece22a2d5e37cf7c4cda9a7d5f036c9b3fdaccb Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:06 -0400 Subject: [PATCH 42/53] tools/power/x86/intel-speed-select: Simplify output for turbo-freq and base-freq The current output of 'intel-speed-select -c 53 perf-profile info -l 0' shows speed-select-turbo-freq-support:1 speed-select-base-freq-support:1 speed-select-base-freq-enabled:0 speed-select-turbo-freq-enabled:0 Simplify the output to single lines displaying status of disabled, enabled, and unsupported. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- .../x86/intel-speed-select/isst-display.c | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 0d9a53a5da2d..3ae693efceaa 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -297,23 +297,25 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, format_and_print(outf, base_level + 4, header, value); snprintf(header, sizeof(header), - "speed-select-turbo-freq-support"); - snprintf(value, sizeof(value), "%d", ctdp_level->fact_support); + "speed-select-turbo-freq"); + if (ctdp_level->fact_support) { + if (ctdp_level->fact_enabled) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + } else + snprintf(value, sizeof(value), "unsupported"); format_and_print(outf, base_level + 4, header, value); snprintf(header, sizeof(header), - "speed-select-base-freq-support"); - snprintf(value, sizeof(value), "%d", ctdp_level->pbf_support); - format_and_print(outf, base_level + 4, header, value); - - snprintf(header, sizeof(header), - "speed-select-base-freq-enabled"); - snprintf(value, sizeof(value), "%d", ctdp_level->pbf_enabled); - format_and_print(outf, base_level + 4, header, value); - - snprintf(header, sizeof(header), - "speed-select-turbo-freq-enabled"); - snprintf(value, sizeof(value), "%d", ctdp_level->fact_enabled); + "speed-select-base-freq"); + if (ctdp_level->pbf_support) { + if (ctdp_level->pbf_enabled) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + } else + snprintf(value, sizeof(value), "unsupported"); format_and_print(outf, base_level + 4, header, value); snprintf(header, sizeof(header), "thermal-design-power(W)"); From 808088e4a1fb2d27a347db2890d43ae73c75a72f Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:07 -0400 Subject: [PATCH 43/53] tools/power/x86/intel-speed-select: Switch output to MHz These features are introduced on new processors that will never operate in the KHz range. Save some zeros and switch the output to MHz. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- .../x86/intel-speed-select/isst-display.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 3ae693efceaa..4ec1924ff7a9 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -6,7 +6,7 @@ #include "isst.h" -#define DISP_FREQ_MULTIPLIER 100000 +#define DISP_FREQ_MULTIPLIER 100 static void printcpumask(int str_len, char *str, int mask_size, cpu_set_t *cpu_mask) @@ -156,7 +156,7 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level, snprintf(header, sizeof(header), "speed-select-base-freq"); format_and_print(outf, disp_level, header, NULL); - snprintf(header, sizeof(header), "high-priority-base-frequency(KHz)"); + snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", pbf_info->p1_high * DISP_FREQ_MULTIPLIER); format_and_print(outf, disp_level + 1, header, value); @@ -166,7 +166,7 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level, pbf_info->core_cpumask); format_and_print(outf, disp_level + 1, header, value); - snprintf(header, sizeof(header), "low-priority-base-frequency(KHz)"); + snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", pbf_info->p1_low * DISP_FREQ_MULTIPLIER); format_and_print(outf, disp_level + 1, header, value); @@ -209,7 +209,7 @@ static void _isst_fact_display_information(int cpu, FILE *outf, int level, if (fact_avx & 0x01) { snprintf(header, sizeof(header), - "high-priority-max-frequency(KHz)"); + "high-priority-max-frequency(MHz)"); snprintf(value, sizeof(value), "%d", bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 2, header, value); @@ -217,7 +217,7 @@ static void _isst_fact_display_information(int cpu, FILE *outf, int level, if (fact_avx & 0x02) { snprintf(header, sizeof(header), - "high-priority-max-avx2-frequency(KHz)"); + "high-priority-max-avx2-frequency(MHz)"); snprintf(value, sizeof(value), "%d", bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 2, header, value); @@ -225,7 +225,7 @@ static void _isst_fact_display_information(int cpu, FILE *outf, int level, if (fact_avx & 0x04) { snprintf(header, sizeof(header), - "high-priority-max-avx512-frequency(KHz)"); + "high-priority-max-avx512-frequency(MHz)"); snprintf(value, sizeof(value), "%d", bucket_info[j].avx512_trl * DISP_FREQ_MULTIPLIER); @@ -235,19 +235,19 @@ static void _isst_fact_display_information(int cpu, FILE *outf, int level, snprintf(header, sizeof(header), "speed-select-turbo-freq-clip-frequencies"); format_and_print(outf, base_level + 1, header, NULL); - snprintf(header, sizeof(header), "low-priority-max-frequency(KHz)"); + snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); snprintf(value, sizeof(value), "%d", fact_info->lp_clipping_ratio_license_sse * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 2, header, value); snprintf(header, sizeof(header), - "low-priority-max-avx2-frequency(KHz)"); + "low-priority-max-avx2-frequency(MHz)"); snprintf(value, sizeof(value), "%d", fact_info->lp_clipping_ratio_license_avx2 * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 2, header, value); snprintf(header, sizeof(header), - "low-priority-max-avx512-frequency(KHz)"); + "low-priority-max-avx512-frequency(MHz)"); snprintf(value, sizeof(value), "%d", fact_info->lp_clipping_ratio_license_avx512 * DISP_FREQ_MULTIPLIER); @@ -291,7 +291,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio); format_and_print(outf, base_level + 4, header, value); - snprintf(header, sizeof(header), "base-frequency(KHz)"); + snprintf(header, sizeof(header), "base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 4, header, value); From 76c2ef35f73913380f32d473a181d68decfca4f4 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:08 -0400 Subject: [PATCH 44/53] tools/power/x86/intel-speed-select: Change turbo ratio output to maximum turbo frequency The intel-speed-select tool currently outputs the turbo ratio for every bucket. Make the output more user-friendly by changing the output to the maximum turbo frequency. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- .../x86/intel-speed-select/isst-display.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 4ec1924ff7a9..cfeee0beb78d 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -336,9 +336,11 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "%d", j); format_and_print(outf, base_level + 6, header, value); - snprintf(header, sizeof(header), "turbo-ratio"); + snprintf(header, sizeof(header), + "max-turbo-frequency(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->trl_sse_active_cores[j]); + ctdp_level->trl_sse_active_cores[j] * + DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 6, header, value); } snprintf(header, sizeof(header), "turbo-ratio-limits-avx"); @@ -351,9 +353,11 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "%d", j); format_and_print(outf, base_level + 6, header, value); - snprintf(header, sizeof(header), "turbo-ratio"); + snprintf(header, sizeof(header), + "max-turbo-frequency(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->trl_avx_active_cores[j]); + ctdp_level->trl_avx_active_cores[j] * + DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 6, header, value); } @@ -367,9 +371,11 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "%d", j); format_and_print(outf, base_level + 6, header, value); - snprintf(header, sizeof(header), "turbo-ratio"); + snprintf(header, sizeof(header), + "max-turbo-frequency(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->trl_avx_512_active_cores[j]); + ctdp_level->trl_avx_512_active_cores[j] * + DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 6, header, value); } if (ctdp_level->pbf_support) From 49aed155ec1b07f8820cceec7317c597a6f69c6e Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:09 -0400 Subject: [PATCH 45/53] tools/power/x86/intel-speed-select: Output human readable CPU list The intel-speed-select tool currently only outputs a hexidecimal CPU mask, which requires translation for use with kernel parameters such as isolcpus. Along with the CPU mask, output a human readable CPU list. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- .../x86/intel-speed-select/isst-display.c | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index cfeee0beb78d..890a01bfee4b 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -8,6 +8,33 @@ #define DISP_FREQ_MULTIPLIER 100 +static void printcpulist(int str_len, char *str, int mask_size, + cpu_set_t *cpu_mask) +{ + int i, first, curr_index, index; + + if (!CPU_COUNT_S(mask_size, cpu_mask)) { + snprintf(str, str_len, "none"); + return; + } + + curr_index = 0; + first = 1; + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!CPU_ISSET_S(i, mask_size, cpu_mask)) + continue; + if (!first) { + index = snprintf(&str[curr_index], + str_len - curr_index, ","); + curr_index += index; + } + index = snprintf(&str[curr_index], str_len - curr_index, "%d", + i); + curr_index += index; + first = 0; + } +} + static void printcpumask(int str_len, char *str, int mask_size, cpu_set_t *cpu_mask) { @@ -166,6 +193,12 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level, pbf_info->core_cpumask); format_and_print(outf, disp_level + 1, header, value); + snprintf(header, sizeof(header), "high-priority-cpu-list"); + printcpulist(sizeof(value), value, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask); + format_and_print(outf, disp_level + 1, header, value); + snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", pbf_info->p1_low * DISP_FREQ_MULTIPLIER); @@ -287,6 +320,12 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, ctdp_level->core_cpumask); format_and_print(outf, base_level + 4, header, value); + snprintf(header, sizeof(header), "enable-cpu-list"); + printcpulist(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, base_level + 4, header, value); + snprintf(header, sizeof(header), "thermal-design-power-ratio"); snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio); format_and_print(outf, base_level + 4, header, value); From 522586a9d5c32b5d7569237eebbfd8241f196419 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:10 -0400 Subject: [PATCH 46/53] tools/power/x86/intel-speed-select: Output success/failed for command output Command output has confusing data, returning "0" on success. For example |# ./intel-speed-select -c 14 turbo-freq enable Intel(R) Speed Select Technology Executing on CPU model:106[0x6a] package-1 die-0 cpu-14 turbo-freq enable:0 To avoid confusion change the command output to 'success' or 'failed'. v2: Remove help output line. Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-config.c | 1 - tools/power/x86/intel-speed-select/isst-display.c | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index f81a28c6b586..78f0cebda1da 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1514,7 +1514,6 @@ static void usage(void) printf("\tResult display uses a common format for each command:\n"); printf("\tResults are formatted in text/JSON with\n"); printf("\t\tPackage, Die, CPU, and command specific results.\n"); - printf("\t\t\tFor Set commands, status is 0 for success and rest for failures\n"); exit(1); } diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 890a01bfee4b..8500cf2997a6 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -519,7 +519,10 @@ void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd, snprintf(header, sizeof(header), "%s", feature); format_and_print(outf, 4, header, NULL); snprintf(header, sizeof(header), "%s", cmd); - snprintf(value, sizeof(value), "%d", result); + if (!result) + snprintf(value, sizeof(value), "success"); + else + snprintf(value, sizeof(value), "failed(error %d)", result); format_and_print(outf, 5, header, value); format_and_print(outf, 1, NULL, NULL); From 3bc3d30ca324bfc3045a1a7fe1f5fe5ad5d92fd9 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 5 Sep 2019 08:03:11 -0400 Subject: [PATCH 47/53] tools/power/x86/intel-speed-select: Fix memory leak cpumasks are allocated by calling the alloc_cpu_mask() function and are never free'd. They should be free'd after the commands have run. Fix the memory leaks by calling free_cpu_set(). Signed-off-by: Prarit Bhargava Acked-by: Srinivas Pandruvada Cc: Srinivas Pandruvada Cc: David Arcari Cc: linux-kernel@vger.kernel.org Signed-off-by: Andy Shevchenko --- tools/power/x86/intel-speed-select/isst-config.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 78f0cebda1da..59753b3917bb 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -603,6 +603,10 @@ static int isst_fill_platform_info(void) close(fd); + if (isst_platform_info.api_version > supported_api_ver) { + printf("Incompatible API versions; Upgrade of tool is required\n"); + return -1; + } return 0; } @@ -1528,6 +1532,7 @@ static void cmdline(int argc, char **argv) { int opt; int option_index = 0; + int ret; static struct option long_options[] = { { "cpu", required_argument, 0, 'c' }, @@ -1589,13 +1594,14 @@ static void cmdline(int argc, char **argv) set_max_cpu_num(); set_cpu_present_cpu_mask(); set_cpu_target_cpu_mask(); - isst_fill_platform_info(); - if (isst_platform_info.api_version > supported_api_ver) { - printf("Incompatible API versions; Upgrade of tool is required\n"); - exit(0); - } + ret = isst_fill_platform_info(); + if (ret) + goto out; process_command(argc, argv); +out: + free_cpu_set(present_cpumask); + free_cpu_set(target_cpumask); } int main(int argc, char **argv) From 92e0e87d0be5eb192fab1edb9b44e724c63416ce Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 5 Sep 2019 16:37:47 -0700 Subject: [PATCH 48/53] platform/x86: ISST: Allow additional TRL MSRs Additional Turbo Ratio Limit (TRL) MSRs are required to get bucket vs core count relationship. So add them to the list of allowed MSRs. Signed-off-by: Srinivas Pandruvada Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_speed_select_if/isst_if_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c index 68d75391db57..3de5a3c66529 100644 --- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c @@ -29,6 +29,8 @@ static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX]; static int punit_msr_white_list[] = { MSR_TURBO_RATIO_LIMIT, MSR_CONFIG_TDP_CONTROL, + MSR_TURBO_RATIO_LIMIT1, + MSR_TURBO_RATIO_LIMIT2, }; struct isst_valid_cmd_ranges { From 1233c7b95c7045905e40c11484493f20ab521d21 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sun, 8 Sep 2019 07:42:25 -0700 Subject: [PATCH 49/53] tools/power/x86/intel-speed-select: Display core count for bucket Read the bucket and core count relationship via MSR and display when displaying turbo ratio limits. Signed-off-by: Srinivas Pandruvada Signed-off-by: Andy Shevchenko --- .../power/x86/intel-speed-select/isst-core.c | 22 +++++++++++++++++++ .../x86/intel-speed-select/isst-display.c | 6 ++--- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index f724322856ed..0bf341ad9697 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -188,6 +188,24 @@ int isst_get_get_trl(int cpu, int level, int avx_level, int *trl) return 0; } +int isst_get_trl_bucket_info(int cpu, unsigned long long *buckets_info) +{ + int ret; + + debug_printf("cpu:%d bucket info via MSR\n", cpu); + + *buckets_info = 0; + + ret = isst_send_msr_command(cpu, 0x1ae, 0, buckets_info); + if (ret) + return ret; + + debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", cpu, + *buckets_info); + + return 0; +} + int isst_set_tdp_level_msr(int cpu, int tdp_level) { unsigned long long level = tdp_level; @@ -563,6 +581,10 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev) if (ret) return ret; + ret = isst_get_trl_bucket_info(cpu, &ctdp_level->buckets_info); + if (ret) + return ret; + ret = isst_get_get_trl(cpu, i, 0, ctdp_level->trl_sse_active_cores); if (ret) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 8500cf2997a6..df4aa99c4e92 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -372,7 +372,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, format_and_print(outf, base_level + 5, header, NULL); snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%d", j); + snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); format_and_print(outf, base_level + 6, header, value); snprintf(header, sizeof(header), @@ -389,7 +389,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, format_and_print(outf, base_level + 5, header, NULL); snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%d", j); + snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); format_and_print(outf, base_level + 6, header, value); snprintf(header, sizeof(header), @@ -407,7 +407,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, format_and_print(outf, base_level + 5, header, NULL); snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%d", j); + snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); format_and_print(outf, base_level + 6, header, value); snprintf(header, sizeof(header), diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 221881761609..2f7f62765eb6 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -134,6 +134,7 @@ struct isst_pkg_ctdp_level_info { size_t core_cpumask_size; cpu_set_t *core_cpumask; int cpu_count; + unsigned long long buckets_info; int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; From 7c28503db19cfa28e394a394aca61c79fbf3f969 Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Mon, 9 Sep 2019 19:31:26 +0200 Subject: [PATCH 50/53] platform/x86: asus-wmi: Reorder ASUS_WMI_CHARGE_THRESHOLD At the same time add a comment explaining what it is used for. Signed-off-by: Kristian Klausen Signed-off-by: Andy Shevchenko --- include/linux/platform_data/x86/asus-wmi.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 53934ef38d98..21f0426c8272 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -61,7 +61,6 @@ /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 -#define ASUS_WMI_CHARGE_THRESHOLD 0x00120057 /* Storage */ #define ASUS_WMI_DEVID_CARDREADER 0x00080013 @@ -82,6 +81,9 @@ /* Deep S3 / Resume on LID open */ #define ASUS_WMI_DEVID_LID_RESUME 0x00120031 +/* Maximum charging percentage */ +#define ASUS_WMI_CHARGE_THRESHOLD 0x00120057 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 From 0c37f44845557b5e5b91ab320f256a4fd5059648 Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Mon, 9 Sep 2019 19:31:27 +0200 Subject: [PATCH 51/53] platform/x86: asus-wmi: Rename CHARGE_THRESHOLD to RSOC The device is officially called "Relative state of charge" (RSOC). At the same time add the missing DEVID from the name. Signed-off-by: Kristian Klausen Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 6 +++--- include/linux/platform_data/x86/asus-wmi.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 848b23764fc3..92c149dc2e6e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2067,7 +2067,7 @@ static ssize_t charge_threshold_store(struct device *dev, if (value < 0 || value > 100) return -EINVAL; - ret = asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, value, &rv); + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv); if (ret) return ret; @@ -2124,7 +2124,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_charge_threshold.attr) - devid = ASUS_WMI_CHARGE_THRESHOLD; + devid = ASUS_WMI_DEVID_RSOC; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2455,7 +2455,7 @@ static int asus_wmi_add(struct platform_device *pdev) * and we can't get the current threshold so let set it to 100% on * module load. */ - asus_wmi_set_devstate(ASUS_WMI_CHARGE_THRESHOLD, 100, NULL); + asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); asus->charge_threshold = 100; return 0; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 21f0426c8272..60249e22e844 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -82,7 +82,7 @@ #define ASUS_WMI_DEVID_LID_RESUME 0x00120031 /* Maximum charging percentage */ -#define ASUS_WMI_CHARGE_THRESHOLD 0x00120057 +#define ASUS_WMI_DEVID_RSOC 0x00120057 /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 From 7973353e92ee1e7ca3b2eb361a4b7cb66c92abee Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Mon, 9 Sep 2019 19:31:28 +0200 Subject: [PATCH 52/53] platform/x86: asus-wmi: Refactor charge threshold to use the battery hooking API At the same time use the official naming for the knobs. Tested on a Zenbook UX430UNR. Signed-off-by: Kristian Klausen Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 148 +++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 49 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 92c149dc2e6e..821b08e01635 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #include #include #include + +#include #include #include "asus-wmi.h" @@ -195,7 +198,8 @@ struct asus_wmi { u8 fan_boost_mode_mask; u8 fan_boost_mode; - int charge_threshold; + // The RSOC controls the maximum charging percentage. + bool battery_rsoc_available; struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; @@ -369,6 +373,97 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); } +/* Battery ********************************************************************/ + +/* The battery maximum charging percentage */ +static int charge_end_threshold; + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value, ret, rv; + + ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value < 0 || value > 100) + return -EINVAL; + + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv); + if (ret) + return ret; + + if (rv != 1) + return -EIO; + + /* There isn't any method in the DSDT to read the threshold, so we + * save the threshold. + */ + charge_end_threshold = value; + return count; +} + +static ssize_t charge_control_end_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", charge_end_threshold); +} + +static DEVICE_ATTR_RW(charge_control_end_threshold); + +static int asus_wmi_battery_add(struct power_supply *battery) +{ + /* The WMI method does not provide a way to specific a battery, so we + * just assume it is the first battery. + */ + if (strcmp(battery->desc->name, "BAT0") != 0) + return -ENODEV; + + if (device_create_file(&battery->dev, + &dev_attr_charge_control_end_threshold)) + return -ENODEV; + + /* The charge threshold is only reset when the system is power cycled, + * and we can't get the current threshold so let set it to 100% when + * a battery is added. + */ + asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); + charge_end_threshold = 100; + + return 0; +} + +static int asus_wmi_battery_remove(struct power_supply *battery) +{ + device_remove_file(&battery->dev, + &dev_attr_charge_control_end_threshold); + return 0; +} + +static struct acpi_battery_hook battery_hook = { + .add_battery = asus_wmi_battery_add, + .remove_battery = asus_wmi_battery_remove, + .name = "ASUS Battery Extension", +}; + +static void asus_wmi_battery_init(struct asus_wmi *asus) +{ + asus->battery_rsoc_available = false; + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_RSOC)) { + asus->battery_rsoc_available = true; + battery_hook_register(&battery_hook); + } +} + +static void asus_wmi_battery_exit(struct asus_wmi *asus) +{ + if (asus->battery_rsoc_available) + battery_hook_unregister(&battery_hook); +} + /* LEDs ***********************************************************************/ /* @@ -2052,45 +2147,6 @@ static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_WO(cpufv); - -static ssize_t charge_threshold_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct asus_wmi *asus = dev_get_drvdata(dev); - int value, ret, rv; - - ret = kstrtouint(buf, 10, &value); - if (ret) - return ret; - - if (value < 0 || value > 100) - return -EINVAL; - - ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv); - if (ret) - return ret; - - if (rv != 1) - return -EIO; - - /* There isn't any method in the DSDT to read the threshold, so we - * save the threshold. - */ - asus->charge_threshold = value; - return count; -} - -static ssize_t charge_threshold_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct asus_wmi *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus->charge_threshold); -} - -static DEVICE_ATTR_RW(charge_threshold); - static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, &dev_attr_camera.attr, @@ -2099,7 +2155,6 @@ static struct attribute *platform_attributes[] = { &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, - &dev_attr_charge_threshold.attr, NULL }; @@ -2123,8 +2178,6 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_ALS_ENABLE; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; - else if (attr == &dev_attr_charge_threshold.attr) - devid = ASUS_WMI_DEVID_RSOC; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2450,13 +2503,9 @@ static int asus_wmi_add(struct platform_device *pdev) goto fail_wmi_handler; } + asus_wmi_battery_init(asus); + asus_wmi_debugfs_init(asus); - /* The charge threshold is only reset when the system is power cycled, - * and we can't get the current threshold so let set it to 100% on - * module load. - */ - asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); - asus->charge_threshold = 100; return 0; @@ -2491,6 +2540,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_debugfs_exit(asus); asus_wmi_sysfs_exit(asus->platform_device); asus_fan_set_auto(asus); + asus_wmi_battery_exit(asus); kfree(asus); return 0; From f690790c9da3122dd7ee1b0d64d97973a7c34135 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Sep 2019 16:10:49 +0300 Subject: [PATCH 53/53] MAINTAINERS: Switch PDx86 subsystem status to Odd Fixes Due to shift of priorities the actual status of the subsystem is Odd Fixes. Signed-off-by: Andy Shevchenko --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 783569e3c4b4..7bce8a33bff3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17502,7 +17502,7 @@ M: Darren Hart M: Andy Shevchenko L: platform-driver-x86@vger.kernel.org T: git git://git.infradead.org/linux-platform-drivers-x86.git -S: Maintained +S: Odd Fixes F: drivers/platform/x86/ F: drivers/platform/olpc/