From 214509e5d61d294193b220f397418e76879f74c0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 6 Jan 2024 15:47:40 +0000 Subject: platform/x86: thinkpad_acpi: remove redundant assignment to variable i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable i is being initialized with the value 0 that is never read, it is being re-assigned 0 again in a for-loop statement later on. The initialization is redundant and can be removed. The initialization of variable n can also be deferred after the sanity check on pointer n and the declaration of all the int variables can be combined as a final code clear-up. Cleans up clang scan build warning: warning: Value stored to 'i' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240106154740.55202-1-colin.i.king@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c4895e9bc714..7bf91cfd3e51 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6208,17 +6208,15 @@ static int thermal_get_sensor(int idx, s32 *value) static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { - int res, i; - int n; - - n = 8; - i = 0; + int res, i, n; if (!s) return -EINVAL; if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) n = 16; + else + n = 8; for (i = 0 ; i < n; i++) { res = thermal_get_sensor(i, &s->temp[i]); -- cgit v1.2.3 From 890a48ca7b05406c538505a92148de7ab0d50591 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Jan 2024 09:56:25 +0300 Subject: platform/x86: silicom-platform: clean up a check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error message in this code can't be reached because value is either zero or non-zero. There isn't a third option. Really, it's nicer to write this as a one liner. Signed-off-by: Dan Carpenter Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/a58bffb7-0a8b-4195-b273-f65a188ace7b@moroto.mountain Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/silicom-platform.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c index 6ce43ccb3112..c0910af16a3a 100644 --- a/drivers/platform/x86/silicom-platform.c +++ b/drivers/platform/x86/silicom-platform.c @@ -256,12 +256,7 @@ static void silicom_gpio_set(struct gpio_chip *gc, if (direction == GPIO_LINE_DIRECTION_IN) return; - if (value) - silicom_mec_port_set(channel, 0); - else if (value == 0) - silicom_mec_port_set(channel, 1); - else - pr_err("Wrong argument value: %d\n", value); + silicom_mec_port_set(channel, !value); } static int silicom_gpio_direction_output(struct gpio_chip *gc, -- cgit v1.2.3 From 2cee4d0c82c023c9012a3a8814cc5c2bcc9b6db6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 14 Jan 2024 18:14:54 +0100 Subject: platform/x86: remove obsolete calls to ledtrig_audio_get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 64f67b5240db ("leds: trigger: audio: Add an activate callback to ensure the initial brightness is set") the audio triggers have an activate callback which sets the LED brightness as soon as the (default) trigger is bound to the LED device. So we can remove the call to ledtrig_audio_get. Positive side effect: There's no code dependency to ledtrig-audio any longer, what allows to remove some Kconfig dependencies. Signed-off-by: Heiner Kallweit Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/daef7331-dcb4-4b3a-802e-656629486b4c@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 6 ------ drivers/platform/x86/asus-wmi.c | 1 - drivers/platform/x86/dell/Kconfig | 3 --- drivers/platform/x86/dell/dell-laptop.c | 2 -- drivers/platform/x86/dell/dell-wmi-privacy.c | 1 - drivers/platform/x86/huawei-wmi.c | 1 - drivers/platform/x86/thinkpad_acpi.c | 1 - 7 files changed, 15 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index bdd302274b9a..6dbd40e2aeda 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -56,8 +56,6 @@ config HUAWEI_WMI depends on INPUT select INPUT_SPARSEKMAP select LEDS_CLASS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO select NEW_LEDS help This driver provides support for Huawei WMI hotkeys, battery charge @@ -269,8 +267,6 @@ config ASUS_WMI select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO select ACPI_PLATFORM_PROFILE help Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new @@ -507,8 +503,6 @@ config THINKPAD_ACPI select NVRAM select NEW_LEDS select LEDS_CLASS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO help This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 18be35fdb381..21dee425ea6f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1620,7 +1620,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus) if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) { asus->micmute_led.name = "platform::micmute"; asus->micmute_led.max_brightness = 1; - asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); asus->micmute_led.brightness_set_blocking = micmute_led_set; asus->micmute_led.default_trigger = "audio-micmute"; diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index e712df67fa6b..bd9f445974cc 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -57,8 +57,6 @@ config DELL_LAPTOP select POWER_SUPPLY select LEDS_CLASS select NEW_LEDS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO help This driver adds support for rfkill and backlight control to Dell laptops (except for some models covered by the Compal driver). @@ -165,7 +163,6 @@ config DELL_WMI config DELL_WMI_PRIVACY bool "Dell WMI Hardware Privacy Support" - depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO depends on DELL_WMI help This option adds integration with the "Dell Hardware Privacy" diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 6586438356de..42f7de2b4522 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2252,7 +2252,6 @@ static int __init dell_init(void) if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) && dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) && !dell_privacy_has_mic_mute()) { - micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev); if (ret < 0) goto fail_led; @@ -2261,7 +2260,6 @@ static int __init dell_init(void) if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) && dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) { - mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE); ret = led_classdev_register(&platform_device->dev, &mute_led_cdev); if (ret < 0) goto fail_backlight; diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c index c517bd45dd32..4d94603f7785 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.c +++ b/drivers/platform/x86/dell/dell-wmi-privacy.c @@ -288,7 +288,6 @@ static int dell_privacy_leds_setup(struct device *dev) priv->cdev.max_brightness = 1; priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set; priv->cdev.default_trigger = "audio-micmute"; - priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); return devm_led_classdev_register(dev, &priv->cdev); } diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 0ef1c46b617b..dde139c69945 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -310,7 +310,6 @@ static void huawei_wmi_leds_setup(struct device *dev) huawei->cdev.max_brightness = 1; huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set; huawei->cdev.default_trigger = "audio-micmute"; - huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); huawei->cdev.dev = dev; huawei->cdev.flags = LED_CORE_SUSPENDRESUME; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7bf91cfd3e51..b769d9d60432 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9283,7 +9283,6 @@ static int mute_led_init(struct ibm_init_struct *iibm) continue; } - mute_led_cdev[i].brightness = ledtrig_audio_get(i); err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]); if (err < 0) { while (i--) -- cgit v1.2.3 From 0959afbafaf8791a9810fba2c55a64dfdcc3b66e Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Sat, 20 Jan 2024 18:29:34 -0500 Subject: platform/x86: Support for mode FN key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Thinkpads have added a 'Mode' Function key that on Windows allows you to choose the active profile (low-power, balanced, performance) Added suppoort for this hotkey (F8), and have it cycle through the options available. Tested on X1 Carbon G12. Signed-off-by: Mark Pearson Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240120232949.317337-1-mpearson-lenovo@squebb.ca Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/admin-guide/laptops/thinkpad-acpi.rst | 7 ++++++- drivers/platform/x86/thinkpad_acpi.c | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index 98d304010170..7f674a6cfa8a 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -444,7 +444,9 @@ event code Key Notes 0x1008 0x07 FN+F8 IBM: toggle screen expand Lenovo: configure UltraNav, - or toggle screen expand + or toggle screen expand. + On newer platforms (2024+) + replaced by 0x131f (see below) 0x1009 0x08 FN+F9 - @@ -504,6 +506,9 @@ event code Key Notes 0x1019 0x18 unknown +0x131f ... FN+F8 Platform Mode change. + Implemented in driver. + ... ... ... 0x1020 0x1F unknown diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b769d9d60432..897d4cd9e5f4 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -166,6 +166,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ + TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -3731,6 +3732,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey) switch (hkey) { case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: case TP_HKEY_EV_AMT_TOGGLE: + case TP_HKEY_EV_PROFILE_TOGGLE: tpacpi_driver_event(hkey); return true; } @@ -11115,7 +11117,23 @@ static void tpacpi_driver_event(const unsigned int hkey_event) else dytc_control_amt(!dytc_amt_active); } - + if (hkey_event == TP_HKEY_EV_PROFILE_TOGGLE) { + switch (dytc_current_profile) { + case PLATFORM_PROFILE_LOW_POWER: + dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); + break; + case PLATFORM_PROFILE_BALANCED: + dytc_profile_set(NULL, PLATFORM_PROFILE_PERFORMANCE); + break; + case PLATFORM_PROFILE_PERFORMANCE: + dytc_profile_set(NULL, PLATFORM_PROFILE_LOW_POWER); + break; + default: + pr_warn("Profile HKEY unexpected profile %d", dytc_current_profile); + } + /* Notify user space the profile changed */ + platform_profile_notify(); + } } static void hotkey_driver_event(const unsigned int scancode) -- cgit v1.2.3 From e5afa3d7a91bb673018a2a9540c12c82e64e652c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 16 Jan 2024 12:51:23 +0800 Subject: drivers/platform/mellanox: Convert snprintf to sysfs_emit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per filesystems/sysfs.rst, show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. coccinelle complains that there are still a couple of functions that use snprintf(). Convert them to sysfs_emit(). > ./drivers/platform/mellanox/mlxbf-bootctl.c:466:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:584:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:635:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:686:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:737:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:788:8-16: WARNING: please use sysfs_emit > ./drivers/platform/mellanox/mlxbf-bootctl.c:839:8-16: WARNING: please use sysfs_emit No functional change intended Signed-off-by: Li Zhijian Link: https://lore.kernel.org/r/20240116045151.3940401-12-lizhijian@fujitsu.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-bootctl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index c1aef3a8fb2d..dd5f370c3168 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -463,7 +463,7 @@ static ssize_t large_icm_show(struct device *dev, if (res.a0) return -EPERM; - return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1); + return sysfs_emit(buf, "0x%lx", res.a1); } static ssize_t large_icm_store(struct device *dev, @@ -581,7 +581,7 @@ static ssize_t opn_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data); + return sysfs_emit(buf, "%s", (char *)opn_data); } static ssize_t opn_store(struct device *dev, @@ -632,7 +632,7 @@ static ssize_t sku_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data); + return sysfs_emit(buf, "%s", (char *)sku_data); } static ssize_t sku_store(struct device *dev, @@ -683,7 +683,7 @@ static ssize_t modl_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data); + return sysfs_emit(buf, "%s", (char *)modl_data); } static ssize_t modl_store(struct device *dev, @@ -734,7 +734,7 @@ static ssize_t sn_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data); + return sysfs_emit(buf, "%s", (char *)sn_data); } static ssize_t sn_store(struct device *dev, @@ -785,7 +785,7 @@ static ssize_t uuid_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data); + return sysfs_emit(buf, "%s", (char *)uuid_data); } static ssize_t uuid_store(struct device *dev, @@ -836,7 +836,7 @@ static ssize_t rev_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data); + return sysfs_emit(buf, "%s", (char *)rev_data); } static ssize_t rev_store(struct device *dev, -- cgit v1.2.3 From 3f399b5d7189bcb608c75abc85fe39f7a5509cfa Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 21 Jan 2024 21:08:24 +0100 Subject: platform/x86: wmi: Use ACPI device name in netlink event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device name inside the ACPI netlink event is limited to 15 characters, so the WMI device name will get truncated. This can be observed with kacpimon when receiving an event from WMI device "9DBB5994-A997-11DA-B012-B622A1EF5492": netlink: 9DBB5994-A997- 000000d0 00000000 Fix this by using the shorter device name from the ACPI bus device instead. This still allows users to uniquely identify the WMI device by using the notify id (0xd0). Signed-off-by: Armin Wolf Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240121200824.2778-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index bd271a5730aa..7ef1e82dc61c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1203,7 +1203,7 @@ static int wmi_notify_device(struct device *dev, void *data) } acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class, - dev_name(&wblock->dev.dev), *event, 0); + acpi_dev_name(wblock->acpi_device), *event, 0); return -EBUSY; } -- cgit v1.2.3 From e6100ef3c0bf52e801d08d247cea93cfa73a0d31 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:22 +0000 Subject: platform/x86/amd/hsmp: Move hsmp_test to probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in advance to supporting ACPI based probe. In case of non-ACPI driver, hsmp_test() can be performed either in plat init() or in probe(). however, in case of ACPI probing, hsmp_test() cannot be called in init(), as the mailbox reg offsets and base addresses are read from ACPI table in the probe(). Hence, move hsmp_test() to probe as preparation for ACPI support. Also use hsmp_send_message() directly in hsmp_test() as the semaphore is already initialized in probe. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-1-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index b55d80e29139..3c17b488f4f8 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -244,12 +244,7 @@ EXPORT_SYMBOL_GPL(hsmp_send_message); static int hsmp_test(u16 sock_ind, u32 value) { struct hsmp_message msg = { 0 }; - struct amd_northbridge *nb; - int ret = -ENODEV; - - nb = node_to_amd_nb(sock_ind); - if (!nb || !nb->root) - return ret; + int ret; /* * Test the hsmp port by performing TEST command. The test message @@ -261,7 +256,7 @@ static int hsmp_test(u16 sock_ind, u32 value) msg.args[0] = value; msg.sock_ind = sock_ind; - ret = __hsmp_send_message(nb->root, &msg); + ret = hsmp_send_message(&msg); if (ret) return ret; @@ -504,6 +499,15 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) for (i = 0; i < plat_dev.num_sockets; i++) { sema_init(&plat_dev.sock[i].hsmp_sem, 1); plat_dev.sock[i].sock_ind = i; + + /* Test the hsmp interface on each socket */ + ret = hsmp_test(i, 0xDEADBEEF); + if (ret) { + pr_err("HSMP test message failed on Fam:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + pr_err("Is HSMP disabled in BIOS ?\n"); + return ret; + } } plat_dev.hsmp_device.name = HSMP_CDEV_NAME; @@ -544,7 +548,6 @@ static struct platform_device *amd_hsmp_platdev; static int __init hsmp_plt_init(void) { int ret = -ENODEV; - int i; if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { pr_err("HSMP is not supported on Family:%x model:%x\n", @@ -560,17 +563,6 @@ static int __init hsmp_plt_init(void) if (plat_dev.num_sockets == 0) return ret; - /* Test the hsmp interface on each socket */ - for (i = 0; i < plat_dev.num_sockets; i++) { - ret = hsmp_test(i, 0xDEADBEEF); - if (ret) { - pr_err("HSMP test message failed on Fam:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - pr_err("Is HSMP disabled in BIOS ?\n"); - return ret; - } - } - ret = platform_driver_register(&amd_hsmp_driver); if (ret) return ret; -- cgit v1.2.3 From 287a821c76be80d69d9a82a94147d0daa5232a4b Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:23 +0000 Subject: platform/x86/amd/hsmp: Cache pci_dev in struct hsmp_socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cache pci_dev obj during probe as part of struct hsmp_socket and use in amd_hsmp_rdwr(). This change will make it easier to support both non-ACPI and ACPI devices. Also add a check for sock_index agsint number of sockets in the hsmp_send_message(). Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-2-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 3c17b488f4f8..1a2abe4460f9 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -58,6 +58,7 @@ struct hsmp_socket { void __iomem *metric_tbl_addr; struct semaphore hsmp_sem; char name[HSMP_ATTR_GRP_NAME_SIZE]; + struct pci_dev *root; u16 sock_ind; }; @@ -71,17 +72,20 @@ struct hsmp_plat_device { static struct hsmp_plat_device plat_dev; -static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, +static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 address, u32 *value, bool write) { int ret; - ret = pci_write_config_dword(root, HSMP_INDEX_REG, address); + if (!sock->root) + return -ENODEV; + + ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG, address); if (ret) return ret; - ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value) - : pci_read_config_dword(root, HSMP_DATA_REG, value)); + ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value) + : pci_read_config_dword(sock->root, HSMP_DATA_REG, value)); return ret; } @@ -95,7 +99,7 @@ static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, * Returns 0 for success and populates the requested number of arguments. * Returns a negative error code for failure. */ -static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) +static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg) { unsigned long timeout, short_sleep; u32 mbox_status; @@ -104,7 +108,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) /* Clear the status register */ mbox_status = HSMP_STATUS_NOT_READY; - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR); + ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR); if (ret) { pr_err("Error %d clearing mailbox status register\n", ret); return ret; @@ -113,7 +117,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) index = 0; /* Write any message arguments */ while (index < msg->num_args) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_DATA + (index << 2), &msg->args[index], HSMP_WR); if (ret) { pr_err("Error %d writing message argument %d\n", ret, index); @@ -123,7 +127,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) } /* Write the message ID which starts the operation */ - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR); + ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR); if (ret) { pr_err("Error %d writing message ID %u\n", ret, msg->msg_id); return ret; @@ -140,7 +144,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); while (time_before(jiffies, timeout)) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD); + ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD); if (ret) { pr_err("Error %d reading mailbox status\n", ret); return ret; @@ -175,7 +179,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) */ index = 0; while (index < msg->response_sz) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_DATA + (index << 2), &msg->args[index], HSMP_RD); if (ret) { pr_err("Error %d reading response %u for message ID:%u\n", @@ -208,21 +212,19 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { - struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind]; - struct amd_northbridge *nb; + struct hsmp_socket *sock; int ret; if (!msg) return -EINVAL; - - nb = node_to_amd_nb(msg->sock_ind); - if (!nb || !nb->root) - return -ENODEV; - ret = validate_message(msg); if (ret) return ret; + if (!plat_dev.sock || msg->sock_ind >= plat_dev.num_sockets) + return -ENODEV; + sock = &plat_dev.sock[msg->sock_ind]; + /* * The time taken by smu operation to complete is between * 10us to 1ms. Sometime it may take more time. @@ -233,7 +235,7 @@ int hsmp_send_message(struct hsmp_message *msg) if (ret < 0) return ret; - ret = __hsmp_send_message(nb->root, msg); + ret = __hsmp_send_message(sock, msg); up(&sock->hsmp_sem); @@ -500,6 +502,10 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) sema_init(&plat_dev.sock[i].hsmp_sem, 1); plat_dev.sock[i].sock_ind = i; + if (!node_to_amd_nb(i)) + return -ENODEV; + plat_dev.sock[i].root = node_to_amd_nb(i)->root; + /* Test the hsmp interface on each socket */ ret = hsmp_test(i, 0xDEADBEEF); if (ret) { -- cgit v1.2.3 From e76064e34a371731e3f0da13ffbda16f6e51a202 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:24 +0000 Subject: platform/x86/amd/hsmp: Create static func to handle platdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a static function and call platform device alloc and add device, which will simplify handling acpi and plat device probing. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-3-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 1a2abe4460f9..e3354683b138 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -551,6 +551,21 @@ static struct platform_driver amd_hsmp_driver = { static struct platform_device *amd_hsmp_platdev; +static int hsmp_plat_dev_register(void) +{ + int ret; + + amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE); + if (!amd_hsmp_platdev) + return -ENOMEM; + + ret = platform_device_add(amd_hsmp_platdev); + if (ret) + platform_device_put(amd_hsmp_platdev); + + return ret; +} + static int __init hsmp_plt_init(void) { int ret = -ENODEV; @@ -573,22 +588,10 @@ static int __init hsmp_plt_init(void) if (ret) return ret; - amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE); - if (!amd_hsmp_platdev) { - ret = -ENOMEM; - goto drv_unregister; - } - - ret = platform_device_add(amd_hsmp_platdev); - if (ret) { - platform_device_put(amd_hsmp_platdev); - goto drv_unregister; - } - - return 0; + ret = hsmp_plat_dev_register(); + if (ret) + platform_driver_unregister(&amd_hsmp_driver); -drv_unregister: - platform_driver_unregister(&amd_hsmp_driver); return ret; } -- cgit v1.2.3 From 17998b3e529bf161bb6ab52c7aafb8a9e2270a7f Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:25 +0000 Subject: platform/x86/amd/hsmp: Define a struct to hold mailbox regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define struct hsmp_mbaddr_info with register offsets and populate them during probe, which avoids the usage of macros in core functions. During ACPI probe, the same fields can be populated from ACPI table. Also move plat dev init to a static function. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-4-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 74 +++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index e3354683b138..287eaa9b0dda 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -40,9 +40,10 @@ * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. * Below are required SMN address for HSMP Mailbox register offsets in SMU address space */ -#define SMN_HSMP_MSG_ID 0x3B10534 -#define SMN_HSMP_MSG_RESP 0x3B10980 -#define SMN_HSMP_MSG_DATA 0x3B109E0 +#define SMN_HSMP_BASE 0x3B00000 +#define SMN_HSMP_MSG_ID 0x0010534 +#define SMN_HSMP_MSG_RESP 0x0010980 +#define SMN_HSMP_MSG_DATA 0x00109E0 #define HSMP_INDEX_REG 0xc4 #define HSMP_DATA_REG 0xc8 @@ -53,8 +54,17 @@ #define HSMP_ATTR_GRP_NAME_SIZE 10 +struct hsmp_mbaddr_info { + u32 base_addr; + u32 msg_id_off; + u32 msg_resp_off; + u32 msg_arg_off; + u32 size; +}; + struct hsmp_socket { struct bin_attribute hsmp_attr; + struct hsmp_mbaddr_info mbinfo; void __iomem *metric_tbl_addr; struct semaphore hsmp_sem; char name[HSMP_ATTR_GRP_NAME_SIZE]; @@ -72,7 +82,7 @@ struct hsmp_plat_device { static struct hsmp_plat_device plat_dev; -static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 address, +static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset, u32 *value, bool write) { int ret; @@ -80,7 +90,8 @@ static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 address, if (!sock->root) return -ENODEV; - ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG, address); + ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG, + sock->mbinfo.base_addr + offset); if (ret) return ret; @@ -101,14 +112,17 @@ static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 address, */ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg) { + struct hsmp_mbaddr_info *mbinfo; unsigned long timeout, short_sleep; u32 mbox_status; u32 index; int ret; + mbinfo = &sock->mbinfo; + /* Clear the status register */ mbox_status = HSMP_STATUS_NOT_READY; - ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR); if (ret) { pr_err("Error %d clearing mailbox status register\n", ret); return ret; @@ -117,7 +131,7 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms index = 0; /* Write any message arguments */ while (index < msg->num_args) { - ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), &msg->args[index], HSMP_WR); if (ret) { pr_err("Error %d writing message argument %d\n", ret, index); @@ -127,7 +141,7 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms } /* Write the message ID which starts the operation */ - ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR); if (ret) { pr_err("Error %d writing message ID %u\n", ret, msg->msg_id); return ret; @@ -144,7 +158,7 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); while (time_before(jiffies, timeout)) { - ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD); if (ret) { pr_err("Error %d reading mailbox status\n", ret); return ret; @@ -179,7 +193,7 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms */ index = 0; while (index < msg->response_sz) { - ret = amd_hsmp_rdwr(sock, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), &msg->args[index], HSMP_RD); if (ret) { pr_err("Error %d reading response %u for message ID:%u\n", @@ -487,24 +501,22 @@ static int hsmp_cache_proto_ver(void) return ret; } -static int hsmp_pltdrv_probe(struct platform_device *pdev) +static int init_platform_device(void) { + struct hsmp_socket *sock; int ret, i; - plat_dev.sock = devm_kzalloc(&pdev->dev, - (plat_dev.num_sockets * sizeof(struct hsmp_socket)), - GFP_KERNEL); - if (!plat_dev.sock) - return -ENOMEM; - plat_dev.dev = &pdev->dev; - for (i = 0; i < plat_dev.num_sockets; i++) { - sema_init(&plat_dev.sock[i].hsmp_sem, 1); - plat_dev.sock[i].sock_ind = i; - if (!node_to_amd_nb(i)) return -ENODEV; - plat_dev.sock[i].root = node_to_amd_nb(i)->root; + sock = &plat_dev.sock[i]; + sock->root = node_to_amd_nb(i)->root; + sock->sock_ind = i; + sock->mbinfo.base_addr = SMN_HSMP_BASE; + sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; + sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; + sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; + sema_init(&sock->hsmp_sem, 1); /* Test the hsmp interface on each socket */ ret = hsmp_test(i, 0xDEADBEEF); @@ -516,6 +528,24 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) } } + return 0; +} + +static int hsmp_pltdrv_probe(struct platform_device *pdev) +{ + int ret; + + plat_dev.sock = devm_kzalloc(&pdev->dev, + (plat_dev.num_sockets * sizeof(struct hsmp_socket)), + GFP_KERNEL); + if (!plat_dev.sock) + return -ENOMEM; + plat_dev.dev = &pdev->dev; + + ret = init_platform_device(); + if (ret) + return ret; + plat_dev.hsmp_device.name = HSMP_CDEV_NAME; plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; plat_dev.hsmp_device.fops = &hsmp_fops; -- cgit v1.2.3 From ce08d3570ae0ef3e8b011d8bd4722b7b413c476b Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:26 +0000 Subject: platform/x86/amd/hsmp: Move dev from platdev to hsmp_socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On an ACPI enabled platforms the probe is called for each socket and the struct dev is different for each socket. This change will help in handling both ACPI and non-ACPI platforms. Also change pr_err() to dev_err() API. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-5-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 287eaa9b0dda..6347aafb81e7 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -69,13 +69,13 @@ struct hsmp_socket { struct semaphore hsmp_sem; char name[HSMP_ATTR_GRP_NAME_SIZE]; struct pci_dev *root; + struct device *dev; u16 sock_ind; }; struct hsmp_plat_device { struct miscdevice hsmp_device; struct hsmp_socket *sock; - struct device *dev; u32 proto_ver; u16 num_sockets; }; @@ -278,8 +278,9 @@ static int hsmp_test(u16 sock_ind, u32 value) /* Check the response value */ if (msg.args[0] != (value + 1)) { - pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n", - sock_ind, (value + 1), msg.args[0]); + dev_err(plat_dev.sock[sock_ind].dev, + "Socket %d test message failed, Expected 0x%08X, received 0x%08X\n", + sock_ind, (value + 1), msg.args[0]); return -EBADE; } @@ -356,14 +357,12 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, struct hsmp_message msg = { 0 }; int ret; - /* Do not support lseek(), reads entire metric table */ - if (count < bin_attr->size) { - dev_err(plat_dev.dev, "Wrong buffer size\n"); + if (!sock) return -EINVAL; - } - if (!sock) { - dev_err(plat_dev.dev, "Failed to read attribute private data\n"); + /* Do not support lseek(), reads entire metric table */ + if (count < bin_attr->size) { + dev_err(sock->dev, "Wrong buffer size\n"); return -EINVAL; } @@ -399,13 +398,13 @@ static int hsmp_get_tbl_dram_base(u16 sock_ind) */ dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); if (!dram_addr) { - dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n"); + dev_err(sock->dev, "Invalid DRAM address for metric table\n"); return -ENOMEM; } - sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr, + sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr, sizeof(struct hsmp_metric_table)); if (!sock->metric_tbl_addr) { - dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n"); + dev_err(sock->dev, "Failed to ioremap metric table addr\n"); return -ENOMEM; } return 0; @@ -453,14 +452,15 @@ static int hsmp_create_sysfs_interface(void) if (WARN_ON(plat_dev.num_sockets > U8_MAX)) return -ERANGE; - hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) * + hsmp_attr_grps = devm_kzalloc(plat_dev.sock[0].dev, sizeof(struct attribute_group *) * (plat_dev.num_sockets + 1), GFP_KERNEL); if (!hsmp_attr_grps) return -ENOMEM; /* Create a sysfs directory for each socket */ for (i = 0; i < plat_dev.num_sockets; i++) { - attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL); + attr_grp = devm_kzalloc(plat_dev.sock[i].dev, sizeof(struct attribute_group), + GFP_KERNEL); if (!attr_grp) return -ENOMEM; @@ -468,7 +468,7 @@ static int hsmp_create_sysfs_interface(void) attr_grp->name = plat_dev.sock[i].name; /* Null terminated list of attributes */ - hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) * + hsmp_bin_attrs = devm_kzalloc(plat_dev.sock[i].dev, sizeof(struct bin_attribute *) * (NUM_HSMP_ATTRS + 1), GFP_KERNEL); if (!hsmp_bin_attrs) return -ENOMEM; @@ -482,7 +482,7 @@ static int hsmp_create_sysfs_interface(void) if (ret) return ret; } - return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps); + return devm_device_add_groups(plat_dev.sock[0].dev, hsmp_attr_grps); } static int hsmp_cache_proto_ver(void) @@ -501,7 +501,7 @@ static int hsmp_cache_proto_ver(void) return ret; } -static int init_platform_device(void) +static int init_platform_device(struct device *dev) { struct hsmp_socket *sock; int ret, i; @@ -512,6 +512,7 @@ static int init_platform_device(void) sock = &plat_dev.sock[i]; sock->root = node_to_amd_nb(i)->root; sock->sock_ind = i; + sock->dev = dev; sock->mbinfo.base_addr = SMN_HSMP_BASE; sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; @@ -521,9 +522,9 @@ static int init_platform_device(void) /* Test the hsmp interface on each socket */ ret = hsmp_test(i, 0xDEADBEEF); if (ret) { - pr_err("HSMP test message failed on Fam:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - pr_err("Is HSMP disabled in BIOS ?\n"); + dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + dev_err(dev, "Is HSMP disabled in BIOS ?\n"); return ret; } } @@ -540,9 +541,8 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) GFP_KERNEL); if (!plat_dev.sock) return -ENOMEM; - plat_dev.dev = &pdev->dev; - ret = init_platform_device(); + ret = init_platform_device(&pdev->dev); if (ret) return ret; @@ -555,13 +555,13 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) ret = hsmp_cache_proto_ver(); if (ret) { - dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n"); + dev_err(&pdev->dev, "Failed to read HSMP protocol version\n"); return ret; } ret = hsmp_create_sysfs_interface(); if (ret) - dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n"); + dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n"); return misc_register(&plat_dev.hsmp_device); } -- cgit v1.2.3 From ca511e7631e76e02c95cce6cf934e165e0e93a21 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:27 +0000 Subject: platform/x86/amd/hsmp: Restructure sysfs group creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the creation of array of attribute groups and array of attributes into different functions. This will ease the ACPI support. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-6-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 6347aafb81e7..1245f65f41c0 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -440,49 +440,53 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock /* One bin sysfs for metrics table*/ #define NUM_HSMP_ATTRS 1 -static int hsmp_create_sysfs_interface(void) +static int hsmp_create_attr_list(struct attribute_group *attr_grp, + struct device *dev, u16 sock_ind) { - const struct attribute_group **hsmp_attr_grps; struct bin_attribute **hsmp_bin_attrs; + + /* Null terminated list of attributes */ + hsmp_bin_attrs = devm_kzalloc(dev, sizeof(struct bin_attribute *) * + (NUM_HSMP_ATTRS + 1), GFP_KERNEL); + if (!hsmp_bin_attrs) + return -ENOMEM; + + attr_grp->bin_attrs = hsmp_bin_attrs; + + return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind); +} + +static int hsmp_create_sysfs_interface(struct device *dev) +{ + const struct attribute_group **hsmp_attr_grps; struct attribute_group *attr_grp; - int ret; u16 i; /* String formatting is currently limited to u8 sockets */ if (WARN_ON(plat_dev.num_sockets > U8_MAX)) return -ERANGE; - hsmp_attr_grps = devm_kzalloc(plat_dev.sock[0].dev, sizeof(struct attribute_group *) * + hsmp_attr_grps = devm_kzalloc(dev, sizeof(struct attribute_group *) * (plat_dev.num_sockets + 1), GFP_KERNEL); if (!hsmp_attr_grps) return -ENOMEM; /* Create a sysfs directory for each socket */ for (i = 0; i < plat_dev.num_sockets; i++) { - attr_grp = devm_kzalloc(plat_dev.sock[i].dev, sizeof(struct attribute_group), + attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL); if (!attr_grp) return -ENOMEM; snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); - attr_grp->name = plat_dev.sock[i].name; - - /* Null terminated list of attributes */ - hsmp_bin_attrs = devm_kzalloc(plat_dev.sock[i].dev, sizeof(struct bin_attribute *) * - (NUM_HSMP_ATTRS + 1), GFP_KERNEL); - if (!hsmp_bin_attrs) - return -ENOMEM; - - attr_grp->bin_attrs = hsmp_bin_attrs; + attr_grp->name = plat_dev.sock[i].name; attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; hsmp_attr_grps[i] = attr_grp; - /* Now create the leaf nodes */ - ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i); - if (ret) - return ret; + hsmp_create_attr_list(attr_grp, dev, i); } - return devm_device_add_groups(plat_dev.sock[0].dev, hsmp_attr_grps); + + return devm_device_add_groups(dev, hsmp_attr_grps); } static int hsmp_cache_proto_ver(void) @@ -559,7 +563,7 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) return ret; } - ret = hsmp_create_sysfs_interface(); + ret = hsmp_create_sysfs_interface(&pdev->dev); if (ret) dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n"); -- cgit v1.2.3 From ba8dcff0e9c4f8fa6a46315126fb837acb0f98fc Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:28 +0000 Subject: platform/x86/amd/hsmp: Add support for ACPI based probing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ACPI table provides mailbox base address and register offset information. The base address is provided as part of CRS method and mailbox offsets are provided through DSD table. Sockets are differentiated by UIDs. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-7-suma.hegde@amd.com [ij: Removed extra parenthesis.] Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/Kconfig | 2 +- drivers/platform/x86/amd/hsmp.c | 351 +++++++++++++++++++++++++++++++++++---- 2 files changed, 322 insertions(+), 31 deletions(-) diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index 54753213cc61..f88682d36447 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -8,7 +8,7 @@ source "drivers/platform/x86/amd/pmc/Kconfig" config AMD_HSMP tristate "AMD HSMP Driver" - depends on AMD_NB && X86_64 + depends on AMD_NB && X86_64 && ACPI help The driver provides a way for user space tools to monitor and manage system management functionality on EPYC server CPUs from AMD. diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 1245f65f41c0..9dc8e036ed72 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -18,9 +18,11 @@ #include #include #include +#include #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "2.0" +#define DRIVER_VERSION "2.2" +#define ACPI_HSMP_DEVICE_HID "AMDI0097" /* HSMP Status / Error codes */ #define HSMP_STATUS_NOT_READY 0x00 @@ -54,6 +56,11 @@ #define HSMP_ATTR_GRP_NAME_SIZE 10 +/* These are the strings specified in ACPI table */ +#define MSG_IDOFF_STR "MsgIdOffset" +#define MSG_ARGOFF_STR "MsgArgOffset" +#define MSG_RESPOFF_STR "MsgRspOffset" + struct hsmp_mbaddr_info { u32 base_addr; u32 msg_id_off; @@ -66,6 +73,7 @@ struct hsmp_socket { struct bin_attribute hsmp_attr; struct hsmp_mbaddr_info mbinfo; void __iomem *metric_tbl_addr; + void __iomem *virt_base_addr; struct semaphore hsmp_sem; char name[HSMP_ATTR_GRP_NAME_SIZE]; struct pci_dev *root; @@ -78,12 +86,14 @@ struct hsmp_plat_device { struct hsmp_socket *sock; u32 proto_ver; u16 num_sockets; + bool is_acpi_device; + bool is_probed; }; static struct hsmp_plat_device plat_dev; -static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset, - u32 *value, bool write) +static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) { int ret; @@ -101,8 +111,29 @@ static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset, return ret; } +static void amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + if (write) + iowrite32(*value, sock->virt_base_addr + offset); + else + *value = ioread32(sock->virt_base_addr + offset); +} + +static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + if (plat_dev.is_acpi_device) + amd_hsmp_acpi_rdwr(sock, offset, value, write); + else + return amd_hsmp_pci_rdwr(sock, offset, value, write); + + return 0; +} + /* - * Send a message to the HSMP port via PCI-e config space registers. + * Send a message to the HSMP port via PCI-e config space registers + * or by writing to MMIO space. * * The caller is expected to zero out any unused arguments. * If a response is expected, the number of response words should be greater than 0. @@ -349,6 +380,181 @@ static const struct file_operations hsmp_fops = { .compat_ioctl = hsmp_ioctl, }; +/* This is the UUID used for HSMP */ +static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd, + 0xa6, 0x9f, 0x4e, 0xa2, + 0x87, 0x1f, 0xc2, 0xf6); + +static inline bool is_acpi_hsmp_uuid(union acpi_object *obj) +{ + if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE) + return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid); + + return false; +} + +static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind) +{ + char *uid; + + /* + * UID (ID00, ID01..IDXX) is used for differentiating sockets, + * read it and strip the "ID" part of it and convert the remaining + * bytes to integer. + */ + uid = acpi_device_uid(ACPI_COMPANION(dev)); + + return kstrtou16(uid + 2, 10, sock_ind); +} + +static acpi_status hsmp_resource(struct acpi_resource *res, void *data) +{ + struct hsmp_socket *sock = data; + struct resource r; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + if (!acpi_dev_resource_memory(res, &r)) + return AE_ERROR; + if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE)) + return AE_ERROR; + sock->mbinfo.base_addr = r.start; + sock->mbinfo.size = resource_size(&r); + break; + case ACPI_RESOURCE_TYPE_END_TAG: + break; + default: + return AE_ERROR; + } + + return AE_OK; +} + +static int hsmp_read_acpi_dsd(struct hsmp_socket *sock) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *guid, *mailbox_package; + union acpi_object *dsd; + acpi_status status; + int ret = 0; + int j; + + status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL, + &buf, ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) { + dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + + dsd = buf.pointer; + + /* HSMP _DSD property should contain 2 objects. + * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER + * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE + * This mailbox object contains 3 more acpi objects of type + * ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets + * these packages inturn contain 2 acpi objects of type + * ACPI_TYPE_STRING and ACPI_TYPE_INTEGER + */ + if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) { + ret = -EINVAL; + goto free_buf; + } + + guid = &dsd->package.elements[0]; + mailbox_package = &dsd->package.elements[1]; + if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) { + dev_err(sock->dev, "Invalid hsmp _DSD table data\n"); + ret = -EINVAL; + goto free_buf; + } + + for (j = 0; j < mailbox_package->package.count; j++) { + union acpi_object *msgobj, *msgstr, *msgint; + + msgobj = &mailbox_package->package.elements[j]; + msgstr = &msgobj->package.elements[0]; + msgint = &msgobj->package.elements[1]; + + /* package should have 1 string and 1 integer object */ + if (msgobj->type != ACPI_TYPE_PACKAGE || + msgstr->type != ACPI_TYPE_STRING || + msgint->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto free_buf; + } + + if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_id_off = msgint->integer.value; + } else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_resp_off = msgint->integer.value; + } else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_arg_off = msgint->integer.value; + } else { + ret = -ENOENT; + goto free_buf; + } + } + + if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off || + !sock->mbinfo.msg_arg_off) + ret = -EINVAL; + +free_buf: + ACPI_FREE(buf.pointer); + return ret; +} + +static int hsmp_read_acpi_crs(struct hsmp_socket *sock) +{ + acpi_status status; + + status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS, + hsmp_resource, sock); + if (ACPI_FAILURE(status)) { + dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n", + acpi_format_exception(status)); + return -EINVAL; + } + if (!sock->mbinfo.base_addr || !sock->mbinfo.size) + return -EINVAL; + + /* The mapped region should be un cached */ + sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr, + sock->mbinfo.size); + if (!sock->virt_base_addr) { + dev_err(sock->dev, "Failed to ioremap MP1 base address\n"); + return -ENOMEM; + } + + return 0; +} + +/* Parse the ACPI table to read the data */ +static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) +{ + struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; + int ret; + + sock->sock_ind = sock_ind; + sock->dev = dev; + plat_dev.is_acpi_device = true; + + sema_init(&sock->hsmp_sem, 1); + + /* Read MP1 base address from CRS method */ + ret = hsmp_read_acpi_crs(sock); + if (ret) + return ret; + + /* Read mailbox offsets from DSD table */ + return hsmp_read_acpi_dsd(sock); +} + static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -456,7 +662,7 @@ static int hsmp_create_attr_list(struct attribute_group *attr_grp, return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind); } -static int hsmp_create_sysfs_interface(struct device *dev) +static int hsmp_create_non_acpi_sysfs_if(struct device *dev) { const struct attribute_group **hsmp_attr_grps; struct attribute_group *attr_grp; @@ -489,13 +695,36 @@ static int hsmp_create_sysfs_interface(struct device *dev) return devm_device_add_groups(dev, hsmp_attr_grps); } -static int hsmp_cache_proto_ver(void) +static int hsmp_create_acpi_sysfs_if(struct device *dev) +{ + struct attribute_group *attr_grp; + u16 sock_ind; + int ret; + + attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL); + if (!attr_grp) + return -ENOMEM; + + attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; + + ret = hsmp_get_uid(dev, &sock_ind); + if (ret) + return ret; + + ret = hsmp_create_attr_list(attr_grp, dev, sock_ind); + if (ret) + return ret; + + return devm_device_add_group(dev, attr_grp); +} + +static int hsmp_cache_proto_ver(u16 sock_ind) { struct hsmp_message msg = { 0 }; int ret; msg.msg_id = HSMP_GET_PROTO_VER; - msg.sock_ind = 0; + msg.sock_ind = sock_ind; msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; ret = hsmp_send_message(&msg); @@ -536,43 +765,102 @@ static int init_platform_device(struct device *dev) return 0; } +static const struct acpi_device_id amd_hsmp_acpi_ids[] = { + {ACPI_HSMP_DEVICE_HID, 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids); + static int hsmp_pltdrv_probe(struct platform_device *pdev) { + struct acpi_device *adev; + u16 sock_ind = 0; int ret; - plat_dev.sock = devm_kzalloc(&pdev->dev, - (plat_dev.num_sockets * sizeof(struct hsmp_socket)), - GFP_KERNEL); - if (!plat_dev.sock) - return -ENOMEM; - - ret = init_platform_device(&pdev->dev); - if (ret) - return ret; - - plat_dev.hsmp_device.name = HSMP_CDEV_NAME; - plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; - plat_dev.hsmp_device.fops = &hsmp_fops; - plat_dev.hsmp_device.parent = &pdev->dev; - plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; - plat_dev.hsmp_device.mode = 0644; + /* + * On ACPI supported BIOS, there is an ACPI HSMP device added for + * each socket, so the per socket probing, but the memory allocated for + * sockets should be contiguous to access it as an array, + * Hence allocate memory for all the sockets at once instead of allocating + * on each probe. + */ + if (!plat_dev.is_probed) { + plat_dev.sock = devm_kzalloc(&pdev->dev, + (plat_dev.num_sockets * sizeof(struct hsmp_socket)), + GFP_KERNEL); + if (!plat_dev.sock) + return -ENOMEM; + } + adev = ACPI_COMPANION(&pdev->dev); + if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids)) { + ret = hsmp_get_uid(&pdev->dev, &sock_ind); + if (ret) + return ret; + if (sock_ind >= plat_dev.num_sockets) + return -EINVAL; + ret = hsmp_parse_acpi_table(&pdev->dev, sock_ind); + if (ret) { + dev_err(&pdev->dev, "Failed to parse ACPI table\n"); + return ret; + } + /* Test the hsmp interface */ + ret = hsmp_test(sock_ind, 0xDEADBEEF); + if (ret) { + dev_err(&pdev->dev, "HSMP test message failed on Fam:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + dev_err(&pdev->dev, "Is HSMP disabled in BIOS ?\n"); + return ret; + } + } else { + ret = init_platform_device(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to init HSMP mailbox\n"); + return ret; + } + } - ret = hsmp_cache_proto_ver(); + ret = hsmp_cache_proto_ver(sock_ind); if (ret) { dev_err(&pdev->dev, "Failed to read HSMP protocol version\n"); return ret; } - ret = hsmp_create_sysfs_interface(&pdev->dev); + if (plat_dev.is_acpi_device) + ret = hsmp_create_acpi_sysfs_if(&pdev->dev); + else + ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev); if (ret) dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n"); - return misc_register(&plat_dev.hsmp_device); + if (!plat_dev.is_probed) { + plat_dev.hsmp_device.name = HSMP_CDEV_NAME; + plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; + plat_dev.hsmp_device.fops = &hsmp_fops; + plat_dev.hsmp_device.parent = &pdev->dev; + plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; + plat_dev.hsmp_device.mode = 0644; + + ret = misc_register(&plat_dev.hsmp_device); + if (ret) + return ret; + + plat_dev.is_probed = true; + } + + return 0; + } static void hsmp_pltdrv_remove(struct platform_device *pdev) { - misc_deregister(&plat_dev.hsmp_device); + /* + * We register only one misc_device even on multi socket system. + * So, deregister should happen only once. + */ + if (plat_dev.is_probed) { + misc_deregister(&plat_dev.hsmp_device); + plat_dev.is_probed = false; + } } static struct platform_driver amd_hsmp_driver = { @@ -580,6 +868,7 @@ static struct platform_driver amd_hsmp_driver = { .remove_new = hsmp_pltdrv_remove, .driver = { .name = DRIVER_NAME, + .acpi_match_table = amd_hsmp_acpi_ids, }, }; @@ -622,9 +911,11 @@ static int __init hsmp_plt_init(void) if (ret) return ret; - ret = hsmp_plat_dev_register(); - if (ret) - platform_driver_unregister(&amd_hsmp_driver); + if (!plat_dev.is_acpi_device) { + ret = hsmp_plat_dev_register(); + if (ret) + platform_driver_unregister(&amd_hsmp_driver); + } return ret; } -- cgit v1.2.3 From d2bf115115d556a5a50cec9dd63cb5f64f10de87 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:29 +0000 Subject: platform/x86/amd/hsmp: Non-ACPI support for AMD F1A_M00~0Fh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMD EPYC family 0x1A and Model 0x0-0xF are having different mailbox message ID offset compared to previous platforms. In case of ACPI based BIOS, this information will be read from ACPI table, for non-ACPI BIOS, this needs to be #defined. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-8-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 9dc8e036ed72..aecc64a72d20 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -44,6 +44,7 @@ */ #define SMN_HSMP_BASE 0x3B00000 #define SMN_HSMP_MSG_ID 0x0010534 +#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 #define SMN_HSMP_MSG_RESP 0x0010980 #define SMN_HSMP_MSG_DATA 0x00109E0 @@ -734,6 +735,14 @@ static int hsmp_cache_proto_ver(u16 sock_ind) return ret; } +static inline bool is_f1a_m0h(void) +{ + if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F) + return true; + + return false; +} + static int init_platform_device(struct device *dev) { struct hsmp_socket *sock; @@ -747,7 +756,16 @@ static int init_platform_device(struct device *dev) sock->sock_ind = i; sock->dev = dev; sock->mbinfo.base_addr = SMN_HSMP_BASE; - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; + + /* + * This is a transitional change from non-ACPI to ACPI, only + * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. + */ + if (is_f1a_m0h()) + sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; + else + sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; + sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; sema_init(&sock->hsmp_sem, 1); -- cgit v1.2.3 From ef6e98177f8d331cdcbe18125a1e2da82f6ede34 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:30 +0000 Subject: platform/x86/amd/hsmp: Check num_sockets against MAX_AMD_SOCKETS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMD supports connecting up to 8 AMD EPYCs in a system. Hence, verify the num_sockets returned from amd_nb_num(). Also remove the WARN_ON() since the num_sockets is already verified. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-9-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index aecc64a72d20..1b446d36221f 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -62,6 +62,8 @@ #define MSG_ARGOFF_STR "MsgArgOffset" #define MSG_RESPOFF_STR "MsgRspOffset" +#define MAX_AMD_SOCKETS 8 + struct hsmp_mbaddr_info { u32 base_addr; u32 msg_id_off; @@ -669,10 +671,6 @@ static int hsmp_create_non_acpi_sysfs_if(struct device *dev) struct attribute_group *attr_grp; u16 i; - /* String formatting is currently limited to u8 sockets */ - if (WARN_ON(plat_dev.num_sockets > U8_MAX)) - return -ERANGE; - hsmp_attr_grps = devm_kzalloc(dev, sizeof(struct attribute_group *) * (plat_dev.num_sockets + 1), GFP_KERNEL); if (!hsmp_attr_grps) @@ -922,7 +920,7 @@ static int __init hsmp_plt_init(void) * if we have N SMN/DF interfaces that ideally means N sockets */ plat_dev.num_sockets = amd_nb_num(); - if (plat_dev.num_sockets == 0) + if (plat_dev.num_sockets == 0 || plat_dev.num_sockets > MAX_AMD_SOCKETS) return ret; ret = platform_driver_register(&amd_hsmp_driver); -- cgit v1.2.3 From 202574971d81f26e7a966a6fdcb75913040923f6 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Sat, 6 Jan 2024 02:25:32 +0000 Subject: platform/x86/amd/hsmp: Remove extra parenthesis and add a space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary parenthesis around hsmp_get_tbl_dram_base(). Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240106022532.1746932-11-suma.hegde@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 1b446d36221f..4a12d7ab36c1 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -641,12 +641,12 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock hattrs[0] = hattr; if (plat_dev.proto_ver == HSMP_PROTO_VER6) - return (hsmp_get_tbl_dram_base(sock_ind)); + return hsmp_get_tbl_dram_base(sock_ind); else return 0; } -/* One bin sysfs for metrics table*/ +/* One bin sysfs for metrics table */ #define NUM_HSMP_ATTRS 1 static int hsmp_create_attr_list(struct attribute_group *attr_grp, -- cgit v1.2.3 From 2b703fbe4e3d4356195d604405825db1fcd08379 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Thu, 25 Jan 2024 12:54:01 +0000 Subject: platform/x86/amd/hsmp: Change devm_kzalloc() to devm_kcalloc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the standard array allocation variant of devm memory allocation APIs. Signed-off-by: Suma Hegde Signed-off-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20240125125401.597617-1-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 4a12d7ab36c1..1927be901108 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -655,8 +655,9 @@ static int hsmp_create_attr_list(struct attribute_group *attr_grp, struct bin_attribute **hsmp_bin_attrs; /* Null terminated list of attributes */ - hsmp_bin_attrs = devm_kzalloc(dev, sizeof(struct bin_attribute *) * - (NUM_HSMP_ATTRS + 1), GFP_KERNEL); + hsmp_bin_attrs = devm_kcalloc(dev, NUM_HSMP_ATTRS + 1, + sizeof(*hsmp_bin_attrs), + GFP_KERNEL); if (!hsmp_bin_attrs) return -ENOMEM; @@ -671,8 +672,9 @@ static int hsmp_create_non_acpi_sysfs_if(struct device *dev) struct attribute_group *attr_grp; u16 i; - hsmp_attr_grps = devm_kzalloc(dev, sizeof(struct attribute_group *) * - (plat_dev.num_sockets + 1), GFP_KERNEL); + hsmp_attr_grps = devm_kcalloc(dev, plat_dev.num_sockets + 1, + sizeof(*hsmp_attr_grps), + GFP_KERNEL); if (!hsmp_attr_grps) return -ENOMEM; @@ -801,8 +803,8 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) * on each probe. */ if (!plat_dev.is_probed) { - plat_dev.sock = devm_kzalloc(&pdev->dev, - (plat_dev.num_sockets * sizeof(struct hsmp_socket)), + plat_dev.sock = devm_kcalloc(&pdev->dev, plat_dev.num_sockets, + sizeof(*plat_dev.sock), GFP_KERNEL); if (!plat_dev.sock) return -ENOMEM; -- cgit v1.2.3 From def1ed0db2a66eed5de593748ffe131615edb45e Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 25 Jan 2024 00:22:51 -0800 Subject: platform/x86/intel/ifs: Trace on all HT threads when executing a test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the trace function on all HT threads. Currently, the trace is called from some arbitrary CPU where the test was invoked. This change gives visibility to the exact errors as seen by each participating HT threads, and not just what was seen from the primary thread. Sample output below. # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | migration/0-18 [000] d..1. 527287.084668: start: 0000, stop: 007f, status: 0000000000007f80 migration/128-785 [128] d..1. 527287.084669: start: 0000, stop: 007f, status: 0000000000007f80 Signed-off-by: Ashok Raj Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20240125082254.424859-3-ashok.raj@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/runtest.c | 46 +++++++++++++++++++++++--------- include/trace/events/intel_ifs.h | 9 +++---- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 13ecd55c6668..c8352ffb9195 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -23,6 +23,12 @@ /* Max retries on the same chunk */ #define MAX_IFS_RETRIES 5 +struct run_params { + struct ifs_data *ifsd; + union ifs_scan *activate; + union ifs_status status; +}; + /* * Number of TSC cycles that a logical CPU will wait for the other * logical CPU on the core in the WRMSR(ACTIVATE_SCAN). @@ -140,10 +146,22 @@ static bool can_restart(union ifs_status status) */ static int doscan(void *data) { - int cpu = smp_processor_id(); - u64 *msrs = data; + int cpu = smp_processor_id(), start, stop; + struct run_params *params = data; + union ifs_status status; + struct ifs_data *ifsd; int first; + ifsd = params->ifsd; + + if (ifsd->generation) { + start = params->activate->gen2.start; + stop = params->activate->gen2.stop; + } else { + start = params->activate->gen0.start; + stop = params->activate->gen0.stop; + } + /* Only the first logical CPU on a core reports result */ first = cpumask_first(cpu_smt_mask(cpu)); @@ -155,12 +173,14 @@ static int doscan(void *data) * take up to 200 milliseconds (in the case where all chunks * are processed in a single pass) before it retires. */ - wrmsrl(MSR_ACTIVATE_SCAN, msrs[0]); + wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data); + rdmsrl(MSR_SCAN_STATUS, status.data); - if (cpu == first) { - /* Pass back the result of the scan */ - rdmsrl(MSR_SCAN_STATUS, msrs[1]); - } + trace_ifs_status(start, stop, status.data); + + /* Pass back the result of the scan */ + if (cpu == first) + params->status = status; return 0; } @@ -179,7 +199,7 @@ static void ifs_test_core(int cpu, struct device *dev) struct ifs_data *ifsd; int to_start, to_stop; int status_chunk; - u64 msrvals[2]; + struct run_params params; int retries; ifsd = ifs_get_data(dev); @@ -190,6 +210,8 @@ static void ifs_test_core(int cpu, struct device *dev) to_start = 0; to_stop = ifsd->valid_chunks - 1; + params.ifsd = ifs_get_data(dev); + if (ifsd->generation) { activate.gen2.start = to_start; activate.gen2.stop = to_stop; @@ -207,12 +229,10 @@ static void ifs_test_core(int cpu, struct device *dev) break; } - msrvals[0] = activate.data; - stop_core_cpuslocked(cpu, doscan, msrvals); - - status.data = msrvals[1]; + params.activate = &activate; + stop_core_cpuslocked(cpu, doscan, ¶ms); - trace_ifs_status(cpu, to_start, to_stop, status.data); + status = params.status; /* Some cases can be retried, give up for others */ if (!can_restart(status)) diff --git a/include/trace/events/intel_ifs.h b/include/trace/events/intel_ifs.h index af0af3f1d9b7..8462dfb7a020 100644 --- a/include/trace/events/intel_ifs.h +++ b/include/trace/events/intel_ifs.h @@ -10,26 +10,23 @@ TRACE_EVENT(ifs_status, - TP_PROTO(int cpu, int start, int stop, u64 status), + TP_PROTO(int start, int stop, u64 status), - TP_ARGS(cpu, start, stop, status), + TP_ARGS(start, stop, status), TP_STRUCT__entry( __field( u64, status ) - __field( int, cpu ) __field( u16, start ) __field( u16, stop ) ), TP_fast_assign( - __entry->cpu = cpu; __entry->start = start; __entry->stop = stop; __entry->status = status; ), - TP_printk("cpu: %d, start: %.4x, stop: %.4x, status: %.16llx", - __entry->cpu, + TP_printk("start: %.4x, stop: %.4x, status: %.16llx", __entry->start, __entry->stop, __entry->status) -- cgit v1.2.3 From e272d1e1188e55259dd0e3ba2f8f744a531fdd59 Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 25 Jan 2024 00:22:52 -0800 Subject: platform/x86/intel/ifs: Add current batch number to trace output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the current batch number in the trace output. When there are failures, it's important to know which test content resulted in failure. # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | migration/0-18 [000] d..1. 527287.084668: ifs_status: batch: 02, start: 0000, stop: 007f, status: 0000000000007f80 migration/128-785 [128] d..1. 527287.084669: ifs_status: batch: 02, start: 0000, stop: 007f, status: 0000000000007f80 Signed-off-by: Ashok Raj Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20240125082254.424859-4-ashok.raj@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/runtest.c | 2 +- include/trace/events/intel_ifs.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index c8352ffb9195..21dc0046fd9b 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -176,7 +176,7 @@ static int doscan(void *data) wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data); rdmsrl(MSR_SCAN_STATUS, status.data); - trace_ifs_status(start, stop, status.data); + trace_ifs_status(ifsd->cur_batch, start, stop, status.data); /* Pass back the result of the scan */ if (cpu == first) diff --git a/include/trace/events/intel_ifs.h b/include/trace/events/intel_ifs.h index 8462dfb7a020..8ce2de120f2d 100644 --- a/include/trace/events/intel_ifs.h +++ b/include/trace/events/intel_ifs.h @@ -10,23 +10,26 @@ TRACE_EVENT(ifs_status, - TP_PROTO(int start, int stop, u64 status), + TP_PROTO(int batch, int start, int stop, u64 status), - TP_ARGS(start, stop, status), + TP_ARGS(batch, start, stop, status), TP_STRUCT__entry( + __field( int, batch ) __field( u64, status ) __field( u16, start ) __field( u16, stop ) ), TP_fast_assign( + __entry->batch = batch; __entry->start = start; __entry->stop = stop; __entry->status = status; ), - TP_printk("start: %.4x, stop: %.4x, status: %.16llx", + TP_printk("batch: %.2d, start: %.4x, stop: %.4x, status: %.16llx", + __entry->batch, __entry->start, __entry->stop, __entry->status) -- cgit v1.2.3 From ea15f34d5fb77a0db0dd9f983b647fe5b613cf73 Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 25 Jan 2024 00:22:53 -0800 Subject: platform/x86/intel/ifs: Replace the exit rendezvous with an entry rendezvous for ARRAY_BIST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARRAY_BIST requires the test to be invoked only from one of the HT siblings of a core. If the other sibling was in mwait(), that didn't permit the test to complete and resulted in several retries before the test could finish. The exit rendezvous was introduced to keep the HT sibling busy until the primary CPU completed the test to avoid those retries. What is actually needed is to ensure that both the threads rendezvous *before* the wrmsr to trigger the test to give good chance to complete the test. The `stop_machine()` function returns only after all the CPUs complete running the function, and provides an exit rendezvous implicitly. In kernel/stop_machine.c::multi_cpu_stop(), every CPU in the mask needs to complete reaching MULTI_STOP_RUN. When all CPUs complete, the state machine moves to next state, i.e MULTI_STOP_EXIT. Thus the underlying API stop_core_cpuslocked() already provides an exit rendezvous. Add the rendezvous earlier in order to ensure the wrmsr is triggered after all CPUs reach the do_array_test(). Remove the exit rendezvous since stop_core_cpuslocked() already guarantees that. Signed-off-by: Ashok Raj Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20240125082254.424859-5-ashok.raj@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/runtest.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 21dc0046fd9b..e3307dd8e3c4 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -271,7 +271,7 @@ static void ifs_test_core(int cpu, struct device *dev) } #define SPINUNIT 100 /* 100 nsec */ -static atomic_t array_cpus_out; +static atomic_t array_cpus_in; /* * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() @@ -298,6 +298,8 @@ static int do_array_test(void *data) int cpu = smp_processor_id(); int first; + wait_for_sibling_cpu(&array_cpus_in, NSEC_PER_SEC); + /* * Only one logical CPU on a core needs to trigger the Array test via MSR write. */ @@ -309,9 +311,6 @@ static int do_array_test(void *data) rdmsrl(MSR_ARRAY_BIST, command->data); } - /* Tests complete faster if the sibling is spinning here */ - wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC); - return 0; } @@ -332,7 +331,7 @@ static void ifs_array_test_core(int cpu, struct device *dev) timed_out = true; break; } - atomic_set(&array_cpus_out, 0); + atomic_set(&array_cpus_in, 0); stop_core_cpuslocked(cpu, do_array_test, &command); if (command.ctrl_result) -- cgit v1.2.3 From ad630f5d92717632a15e1d0b92e5421e6eac7c8d Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 25 Jan 2024 00:22:54 -0800 Subject: platform/x86/intel/ifs: Add an entry rendezvous for SAF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The activation for Scan at Field (SAF) includes a parameter to make microcode wait for both threads to join. It's preferable to perform an entry rendezvous before the activation to ensure that they start the `wrmsr` close enough to each other. In some cases it has been observed that one of the threads might be just a bit late to arrive. An entry rendezvous reduces the likelihood of these cases occurring. Add an entry rendezvous to ensure the activation on both threads happen close enough to each other. Signed-off-by: Ashok Raj Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20240125082254.424859-6-ashok.raj@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/runtest.c | 48 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index e3307dd8e3c4..95b4b71fab53 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -140,6 +140,29 @@ static bool can_restart(union ifs_status status) return false; } +#define SPINUNIT 100 /* 100 nsec */ +static atomic_t array_cpus_in; +static atomic_t scan_cpus_in; + +/* + * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() + */ +static void wait_for_sibling_cpu(atomic_t *t, long long timeout) +{ + int cpu = smp_processor_id(); + const struct cpumask *smt_mask = cpu_smt_mask(cpu); + int all_cpus = cpumask_weight(smt_mask); + + atomic_inc(t); + while (atomic_read(t) < all_cpus) { + if (timeout < SPINUNIT) + return; + ndelay(SPINUNIT); + timeout -= SPINUNIT; + touch_nmi_watchdog(); + } +} + /* * Execute the scan. Called "simultaneously" on all threads of a core * at high priority using the stop_cpus mechanism. @@ -165,6 +188,8 @@ static int doscan(void *data) /* Only the first logical CPU on a core reports result */ first = cpumask_first(cpu_smt_mask(cpu)); + wait_for_sibling_cpu(&scan_cpus_in, NSEC_PER_SEC); + /* * This WRMSR will wait for other HT threads to also write * to this MSR (at most for activate.delay cycles). Then it @@ -230,6 +255,7 @@ static void ifs_test_core(int cpu, struct device *dev) } params.activate = &activate; + atomic_set(&scan_cpus_in, 0); stop_core_cpuslocked(cpu, doscan, ¶ms); status = params.status; @@ -270,28 +296,6 @@ static void ifs_test_core(int cpu, struct device *dev) } } -#define SPINUNIT 100 /* 100 nsec */ -static atomic_t array_cpus_in; - -/* - * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() - */ -static void wait_for_sibling_cpu(atomic_t *t, long long timeout) -{ - int cpu = smp_processor_id(); - const struct cpumask *smt_mask = cpu_smt_mask(cpu); - int all_cpus = cpumask_weight(smt_mask); - - atomic_inc(t); - while (atomic_read(t) < all_cpus) { - if (timeout < SPINUNIT) - return; - ndelay(SPINUNIT); - timeout -= SPINUNIT; - touch_nmi_watchdog(); - } -} - static int do_array_test(void *data) { union ifs_array *command = data; -- cgit v1.2.3 From 682c259a849610c7864cc75d52415c782c78653a Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Thu, 25 Jan 2024 15:03:28 +0200 Subject: platform/x86/intel/ifs: Remove unnecessary initialization of 'ret' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ret variable is unconditionally assigned in ifs_load_firmware(). Therefore, remove its unnecessary initialization. Reviewed-by: Ashok Raj Link: https://lore.kernel.org/r/20240125130328.11253-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/load.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index a1ee1a74fc3c..03e49b836a6b 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -383,7 +383,7 @@ int ifs_load_firmware(struct device *dev) unsigned int expected_size; const struct firmware *fw; char scan_path[64]; - int ret = -EINVAL; + int ret; snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, -- cgit v1.2.3 From 6468e64ee380514b331ccb743fa491b23cd8b4dd Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 30 Jan 2024 23:19:42 +0100 Subject: platform/x86: wmi: Stop using ACPI device class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an ACPI netlink event is received by acpid, the ACPI device class is passed as its first argument. But since the class string is not initialized during probe, an empty string is being passed: netlink: PNP0C14:01 000000d0 00000000 Fix this by passing a static string instead. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240130221942.2770-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7ef1e82dc61c..3335de4e32b2 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1202,8 +1202,7 @@ static int wmi_notify_device(struct device *dev, void *data) wblock->handler(*event, wblock->handler_data); } - acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class, - acpi_dev_name(wblock->acpi_device), *event, 0); + acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0); return -EBUSY; } -- cgit v1.2.3 From 24b10e5f8e0d2bee1a10fc67011ea5d936c1a389 Mon Sep 17 00:00:00 2001 From: Alexis Belmonte Date: Thu, 1 Feb 2024 14:29:16 +0100 Subject: platform/x86: hp-wmi: Tidy up module source code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit performs four things: - fix up the GUID string inconsistency (lower case 'e') from the WMI module alias declaration/macro definition - separate GUID macros from the embedded controller offset macros - rename the description of the module to better represent what it actually achieves as a whole - add a space right before the '*' pointer qualifier to match the other array declarations This also prepares the terrain for integrating support work for boards identified as '8BAD', which corresponds to HP's Omen 17 ck2xxx models. Signed-off-by: Alexis Belmonte Link: https://lore.kernel.org/r/ZbucrKh36sNxeyfX@alexis-pc Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index e536604225c5..b5955b6eb50b 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -29,15 +29,17 @@ #include MODULE_AUTHOR("Matthew Garrett "); -MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_DESCRIPTION("HP laptop WMI driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); -MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" -#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" + #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 + #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required /* DMI board names of devices that should use the omen specific path for @@ -62,7 +64,7 @@ static const char * const omen_thermal_profile_boards[] = { * profile version 0 by the Omen Command Center app, regardless of what * the get system design information WMI call returns */ -static const char *const omen_thermal_profile_force_v0_boards[] = { +static const char * const omen_thermal_profile_force_v0_boards[] = { "8607", "8746", "8747", "8749", "874A", "8748" }; -- cgit v1.2.3 From 3a057bf30e044a51af8e6a8fe8cbfac49e2b9bc5 Mon Sep 17 00:00:00 2001 From: Alexis Belmonte Date: Thu, 1 Feb 2024 14:29:33 +0100 Subject: platform/x86: hp-wmi: Add thermal profile support for 8BAD boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 8BAD to the list of boards which have thermal profile selection available. This allows the CPU to draw more power than the default TDP barrier defined by the 'balanced' thermal profile (around 50W), hence allowing it to perform better without being throttled by the embedded controller (around 130W). We first need to set the HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET to zero. This prevents the timer countdown from reaching zero, making the embedded controller "force-switch" the system's thermal profile back to 'balanced' automatically. We also need to put a number of specific flags in HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET when we're switching to another thermal profile: - for 'performance', we need to set both HP_OMEN_EC_FLAGS_TURBO and HP_OMEN_EC_FLAGS_NOTIMER; - for 'balanced' and 'powersave', we clear out the register to notify the system that we want to lower the TDP barrier as soon as possible. The third flag defined in the hp_thermal_profile_omen_flags enum, HP_OMEN_EC_FLAGS_JUSTSET, is present for completeness. To prevent potential behaviour breakage with other Omen models, a separate omen_timed_thermal_profile_boards array has been added to list which boards expose this behaviour. Performance benchmarking was done with the help of silver.urih.com and Google Chrome 120.0.6099.129, on Gnome 45.2, with the 'performance' thermal profile set: | | Performance | Stress | TDP | |------------------|-------------|------------|-------| | with my patch | P84549 | S0.1891 | 131W | | without my patch | P44084 | S0.1359 | 47W | The TDP measurements were done with the help of the s-tui utility, during the load. There is still work to be done: - tune the CPU and GPU fans to better cool down and enhance performance at the right time; right now, it seems that the fans are not properly reacting to thermal/performance events, which in turn either causes thermal throttling OR makes the fans spin way too long, even though the temperatures have lowered down - expose the CPU and GPU fan curves to user-land so that they can be controlled just like what the Omen Gaming Hub utility proposes to its users; Signed-off-by: Alexis Belmonte Link: https://lore.kernel.org/r/ZbucvX2rRdqRgtcu@alexis-pc Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 61 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index b5955b6eb50b..630519c08617 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -38,6 +38,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" #define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" +#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x62 +#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63 #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required @@ -57,7 +59,7 @@ static const char * const omen_thermal_profile_boards[] = { "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", - "8917", "8918", "8949", "894A", "89EB" + "8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42" }; /* DMI Board names of Omen laptops that are specifically set to be thermal @@ -68,6 +70,14 @@ static const char * const omen_thermal_profile_force_v0_boards[] = { "8607", "8746", "8747", "8749", "874A", "8748" }; +/* DMI board names of Omen laptops that have a thermal profile timer which will + * cause the embedded controller to set the thermal profile back to + * "balanced" when reaching zero. + */ +static const char * const omen_timed_thermal_profile_boards[] = { + "8BAD", "8A42" +}; + /* DMI Board names of Victus laptops */ static const char * const victus_thermal_profile_boards[] = { "8A25" @@ -184,6 +194,12 @@ enum hp_thermal_profile_omen_v1 { HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, }; +enum hp_thermal_profile_omen_flags { + HP_OMEN_EC_FLAGS_TURBO = 0x04, + HP_OMEN_EC_FLAGS_NOTIMER = 0x02, + HP_OMEN_EC_FLAGS_JUSTSET = 0x01, +}; + enum hp_thermal_profile_victus { HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00, HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01, @@ -451,7 +467,11 @@ static int hp_wmi_get_tablet_mode(void) static int omen_thermal_profile_set(int mode) { - char buffer[2] = {0, mode}; + /* The Omen Control Center actively sets the first byte of the buffer to + * 255, so let's mimic this behaviour to be as close as possible to + * the original software. + */ + char buffer[2] = {-1, mode}; int ret; ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, @@ -1203,10 +1223,33 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof, return 0; } +static bool has_omen_thermal_profile_ec_timer(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_timed_thermal_profile_boards, + ARRAY_SIZE(omen_timed_thermal_profile_boards), + board_name) >= 0; +} + +inline int omen_thermal_profile_ec_flags_set(enum hp_thermal_profile_omen_flags flags) +{ + return ec_write(HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET, flags); +} + +inline int omen_thermal_profile_ec_timer_set(u8 value) +{ + return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value); +} + static int platform_profile_omen_set(struct platform_profile_handler *pprof, enum platform_profile_option profile) { int err, tp, tp_version; + enum hp_thermal_profile_omen_flags flags = 0; tp_version = omen_get_thermal_policy_version(); @@ -1240,6 +1283,20 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof, if (err < 0) return err; + if (has_omen_thermal_profile_ec_timer()) { + err = omen_thermal_profile_ec_timer_set(0); + if (err < 0) + return err; + + if (profile == PLATFORM_PROFILE_PERFORMANCE) + flags = HP_OMEN_EC_FLAGS_NOTIMER | + HP_OMEN_EC_FLAGS_TURBO; + + err = omen_thermal_profile_ec_flags_set(flags); + if (err < 0) + return err; + } + return 0; } -- cgit v1.2.3 From 10fdfd13a35994ac1dcc3003d4046cb1955e66a8 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 11:40:16 -0300 Subject: platform: x86: wmi: make wmi_bus_type const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the driver core can properly handle constant struct bus_type, move the wmi_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20240204-bus_cleanup-platform-drivers-x86-v1-1-1f0839b385c6@marliere.net Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 3335de4e32b2..5682c7de0394 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -219,7 +219,7 @@ static int wmidev_match_guid(struct device *dev, const void *data) return 0; } -static struct bus_type wmi_bus_type; +static const struct bus_type wmi_bus_type; static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) { @@ -899,7 +899,7 @@ static struct class wmi_bus_class = { .name = "wmi_bus", }; -static struct bus_type wmi_bus_type = { +static const struct bus_type wmi_bus_type = { .name = "wmi", .dev_groups = wmi_groups, .match = wmi_dev_match, -- cgit v1.2.3 From 41b43c75121208a9e4e84fd148f918bddb3f5d1f Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 11:40:17 -0300 Subject: platform: x86: ibm_rtl: make rtl_subsys const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the driver core can properly handle constant struct bus_type, move the rtl_subsys variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Link: https://lore.kernel.org/r/20240204-bus_cleanup-platform-drivers-x86-v1-2-1f0839b385c6@marliere.net Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/ibm_rtl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 2ab7d9ac542d..1d4bbae115f1 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -179,7 +179,7 @@ static ssize_t rtl_set_state(struct device *dev, return ret; } -static struct bus_type rtl_subsys = { +static const struct bus_type rtl_subsys = { .name = "ibm_rtl", .dev_name = "ibm_rtl", }; -- cgit v1.2.3 From 197b980644eae1184844d451d10fe3ea51c7597d Mon Sep 17 00:00:00 2001 From: Ivor Wanders Date: Tue, 30 Jan 2024 19:58:56 -0500 Subject: platform/surface: aggregator_registry: add entry for fan speed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an entry for the fan speed function. Add this new entry to the Surface Pro 9 group. Signed-off-by: Ivor Wanders Link: https://github.com/linux-surface/kernel/pull/144 Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20240131005856.10180-3-ivor@iwanders.net Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/surface/surface_aggregator_registry.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index aeb3feae40ff..035d6b4105cd 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = { .parent = &ssam_node_root, }; +/* Fan speed function. */ +static const struct software_node ssam_node_fan_speed = { + .name = "ssam:01:05:01:01:01", + .parent = &ssam_node_root, +}; + /* Tablet-mode switch via KIP subsystem. */ static const struct software_node ssam_node_kip_tablet_switch = { .name = "ssam:01:0e:01:00:01", @@ -305,6 +311,7 @@ static const struct software_node *ssam_node_group_sp9[] = { &ssam_node_bat_ac, &ssam_node_bat_main, &ssam_node_tmp_pprof, + &ssam_node_fan_speed, &ssam_node_pos_tablet_switch, &ssam_node_hid_kip_keyboard, &ssam_node_hid_kip_penstash, -- cgit v1.2.3 From 7f1b998a9108f7fd465039323d5fc2599b8cae77 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 6 Feb 2024 23:04:44 +0100 Subject: platform/x86: wmi: Check if WMxx control method exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices like the MSI GF63-12VF contain WMI method blocks without providing the necessary WMxx ACPI control methods. Avoid creating WMI devices for such WMI method blocks since the resulting WMI device is going to be unusable. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240206220447.3102-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 5682c7de0394..af273623bc5b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -958,6 +958,15 @@ static int wmi_create_device(struct device *wmi_bus_dev, } if (wblock->gblock.flags & ACPI_WMI_METHOD) { + get_acpi_method_name(wblock, 'M', method); + if (!acpi_has_method(device->handle, method)) { + dev_warn(wmi_bus_dev, + FW_BUG "%s method block execution control method not found\n", + method); + + return -ENXIO; + } + wblock->dev.dev.type = &wmi_type_method; goto out_init; } -- cgit v1.2.3 From d0c595a11785573aad3b9e32ae293c48757eceff Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 6 Feb 2024 23:04:45 +0100 Subject: platform/x86: wmi: Use FW_BUG when warning about missing control methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A missing WQxx control method is a firmware bug and should be marked as such using FW_BUG so that users know that the issue is not a kernel issue. Since get_subobj_info() might fail even if the control method is present, we need to print the warning only if acpi_get_handle() fails. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240206220447.3102-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index af273623bc5b..43d750ff65cf 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -130,26 +130,6 @@ static const void *find_guid_context(struct wmi_block *wblock, return NULL; } -static int get_subobj_info(acpi_handle handle, const char *pathname, - struct acpi_device_info **info) -{ - acpi_handle subobj_handle; - acpi_status status; - - status = acpi_get_handle(handle, pathname, &subobj_handle); - if (status == AE_NOT_FOUND) - return -ENOENT; - - if (ACPI_FAILURE(status)) - return -EIO; - - status = acpi_get_object_info(subobj_handle, info); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; -} - static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) { struct guid_block *block; @@ -947,9 +927,10 @@ static int wmi_create_device(struct device *wmi_bus_dev, struct wmi_block *wblock, struct acpi_device *device) { - struct acpi_device_info *info; char method[WMI_ACPI_METHOD_NAME_SIZE]; - int result; + struct acpi_device_info *info; + acpi_handle method_handle; + acpi_status status; uint count; if (wblock->gblock.flags & ACPI_WMI_EVENT) { @@ -977,15 +958,19 @@ static int wmi_create_device(struct device *wmi_bus_dev, * we ignore this data block. */ get_acpi_method_name(wblock, 'Q', method); - result = get_subobj_info(device->handle, method, &info); - - if (result) { + status = acpi_get_handle(device->handle, method, &method_handle); + if (ACPI_FAILURE(status)) { dev_warn(wmi_bus_dev, - "%s data block query control method not found\n", + FW_BUG "%s data block query control method not found\n", method); - return result; + + return -ENXIO; } + status = acpi_get_object_info(method_handle, &info); + if (ACPI_FAILURE(status)) + return -EIO; + wblock->dev.dev.type = &wmi_type_data; /* -- cgit v1.2.3 From 49c67cd5b6a4b611ac775de3831f5e739dd580f2 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 6 Feb 2024 23:04:46 +0100 Subject: platform/x86: wmi: Remove unnecessary out-of-memory message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If kzalloc() fails, an out-of-memory message is already printed. Remove the unnecessary second warning message. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240206220447.3102-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- 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 43d750ff65cf..be3e35a90703 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1093,10 +1093,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) continue; wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); - if (!wblock) { - dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); + if (!wblock) continue; - } wblock->acpi_device = device; wblock->gblock = gblock[i]; -- cgit v1.2.3 From fde7da1072f3c0239a80b590e7b75c9411e8b630 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 6 Feb 2024 23:04:47 +0100 Subject: platform/x86: wmi: Replace pr_err() with dev_err() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using dev_err() allows users to find out from which device the error message came from. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240206220447.3102-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index be3e35a90703..57026f91c396 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1295,7 +1295,7 @@ static int acpi_wmi_probe(struct platform_device *device) error = parse_wdg(wmi_bus_dev, device); if (error) { - pr_err("Failed to parse WDG method\n"); + dev_err(&device->dev, "Failed to parse _WDG method\n"); return error; } -- cgit v1.2.3 From 5b559e8ab01c8d7a92478f8143ba844161292203 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 12 Feb 2024 19:50:16 +0100 Subject: platform/x86: wmi: Make input buffer mandatory when evaluating methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACPI-WMI specification declares in the section "ACPI Control Method Naming Conventions and Functionality for Windows 2000 Instrumentation" that a WMxx control method takes 3 arguments: instance, method id and argument buffer. This is also the case even when the underlying WMI method does not have any input arguments. So if a WMI driver evaluates a WMI method without passing an input buffer, ACPICA will log a warning complaining that the third argument is missing. Prevent this by checking that a input buffer was passed, and return an error if this was not the case. Tested on a Asus PRIME B650-Plus. Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240212185016.5494-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b83c0f0ddd5c..5a613b06b269 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -296,7 +296,7 @@ EXPORT_SYMBOL_GPL(wmidev_instance_count); * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @instance: Instance index * @method_id: Method ID to call - * @in: Buffer containing input for the method call + * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. @@ -326,7 +326,7 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method); * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call - * @in: Buffer containing input for the method call + * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. @@ -347,26 +347,25 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met block = &wblock->gblock; handle = wblock->acpi_device->handle; + if (!in) + return AE_BAD_DATA; + if (!(block->flags & ACPI_WMI_METHOD)) return AE_BAD_DATA; if (block->instance_count <= instance) return AE_BAD_PARAMETER; - input.count = 2; + input.count = 3; input.pointer = params; + params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = instance; params[1].type = ACPI_TYPE_INTEGER; params[1].integer.value = method_id; - - if (in) { - input.count = 3; - - params[2].type = get_param_acpi_type(wblock); - params[2].buffer.length = in->length; - params[2].buffer.pointer = in->pointer; - } + params[2].type = get_param_acpi_type(wblock); + params[2].buffer.length = in->length; + params[2].buffer.pointer = in->pointer; get_acpi_method_name(wblock, 'M', method); -- cgit v1.2.3 From fd23023e2aaa78320243801666690deb751143c2 Mon Sep 17 00:00:00 2001 From: Shravan Kumar Ramani Date: Tue, 13 Feb 2024 06:15:25 -0500 Subject: platform/mellanox: mlxbf-pmc: Replace uintN_t with kernel-style types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use u8, u32 and u64 instead of respective uintN_t types. Remove unnecessary newlines for function argument lists. Signed-off-by: Shravan Kumar Ramani Link: https://lore.kernel.org/r/39be055af3506ce6f843d11e45d71620f2a96e26.1707808180.git.shravankr@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-pmc.c | 109 +++++++++++++++------------------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index b1995ac268d7..86044d1b8fa5 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -149,17 +149,17 @@ struct mlxbf_pmc_block_info { */ struct mlxbf_pmc_context { struct platform_device *pdev; - uint32_t total_blocks; - uint32_t tile_count; - uint8_t llt_enable; - uint8_t mss_enable; - uint32_t group_num; + u32 total_blocks; + u32 tile_count; + u8 llt_enable; + u8 mss_enable; + u32 group_num; struct device *hwmon_dev; const char *block_name[MLXBF_PMC_MAX_BLOCKS]; struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS]; const struct attribute_group *groups[MLXBF_PMC_MAX_BLOCKS]; bool svc_sreg_support; - uint32_t sreg_tbl_perf; + u32 sreg_tbl_perf; unsigned int event_set; }; @@ -865,8 +865,7 @@ static struct mlxbf_pmc_context *pmc; static const char *mlxbf_pmc_svc_uuid_str = "89c036b4-e7d7-11e6-8797-001aca00bfc4"; /* Calls an SMC to access a performance register */ -static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command, - uint64_t *result) +static int mlxbf_pmc_secure_read(void __iomem *addr, u32 command, u64 *result) { struct arm_smccc_res res; int status, err = 0; @@ -892,8 +891,7 @@ static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command, } /* Read from a performance counter */ -static int mlxbf_pmc_read(void __iomem *addr, uint32_t command, - uint64_t *result) +static int mlxbf_pmc_read(void __iomem *addr, u32 command, u64 *result) { if (pmc->svc_sreg_support) return mlxbf_pmc_secure_read(addr, command, result); @@ -907,22 +905,21 @@ static int mlxbf_pmc_read(void __iomem *addr, uint32_t command, } /* Convenience function for 32-bit reads */ -static int mlxbf_pmc_readl(void __iomem *addr, uint32_t *result) +static int mlxbf_pmc_readl(void __iomem *addr, u32 *result) { - uint64_t read_out; + u64 read_out; int status; status = mlxbf_pmc_read(addr, MLXBF_PMC_READ_REG_32, &read_out); if (status) return status; - *result = (uint32_t)read_out; + *result = (u32)read_out; return 0; } /* Calls an SMC to access a performance register */ -static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command, - uint64_t value) +static int mlxbf_pmc_secure_write(void __iomem *addr, u32 command, u64 value) { struct arm_smccc_res res; int status, err = 0; @@ -945,7 +942,7 @@ static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command, } /* Write to a performance counter */ -static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value) +static int mlxbf_pmc_write(void __iomem *addr, int command, u64 value) { if (pmc->svc_sreg_support) return mlxbf_pmc_secure_write(addr, command, value); @@ -959,7 +956,7 @@ static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value) } /* Check if the register offset is within the mapped region for the block */ -static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset) +static bool mlxbf_pmc_valid_range(int blk_num, u32 offset) { if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) && (offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size)) @@ -1082,7 +1079,7 @@ static char *mlxbf_pmc_get_event_name(const char *blk, int evt) /* Method to enable/disable/reset l3cache counters */ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset) { - uint32_t perfcnt_cfg = 0; + u32 perfcnt_cfg = 0; if (enable) perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_EN; @@ -1095,12 +1092,9 @@ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset) } /* Method to handle l3cache counter programming */ -static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num, - uint32_t evt) +static int mlxbf_pmc_program_l3_counter(int blk_num, u32 cnt_num, u32 evt) { - uint32_t perfcnt_sel_1 = 0; - uint32_t perfcnt_sel = 0; - uint32_t *wordaddr; + u32 perfcnt_sel_1 = 0, perfcnt_sel = 0, *wordaddr; void __iomem *pmcaddr; int ret; @@ -1162,11 +1156,10 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num, } /* Method to handle crspace counter programming */ -static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num, - uint32_t evt) +static int mlxbf_pmc_program_crspace_counter(int blk_num, u32 cnt_num, u32 evt) { - uint32_t word; void *addr; + u32 word; int ret; addr = pmc->block[blk_num].mmio_base + @@ -1187,7 +1180,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num, } /* Method to clear crspace counter value */ -static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num) +static int mlxbf_pmc_clear_crspace_counter(int blk_num, u32 cnt_num) { void *addr; @@ -1199,10 +1192,9 @@ static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num) } /* Method to program a counter to monitor an event */ -static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num, - uint32_t evt, bool is_l3) +static int mlxbf_pmc_program_counter(int blk_num, u32 cnt_num, u32 evt, bool is_l3) { - uint64_t perfctl, perfevt, perfmon_cfg; + u64 perfctl, perfevt, perfmon_cfg; if (cnt_num >= pmc->block[blk_num].counters) return -ENODEV; @@ -1263,12 +1255,11 @@ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num, } /* Method to handle l3 counter reads */ -static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num, - uint64_t *result) +static int mlxbf_pmc_read_l3_counter(int blk_num, u32 cnt_num, u64 *result) { - uint32_t perfcnt_low = 0, perfcnt_high = 0; - uint64_t value; + u32 perfcnt_low = 0, perfcnt_high = 0; int status; + u64 value; status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + MLXBF_PMC_L3C_PERF_CNT_LOW + @@ -1295,11 +1286,10 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num, } /* Method to handle crspace counter reads */ -static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num, - uint64_t *result) +static int mlxbf_pmc_read_crspace_counter(int blk_num, u32 cnt_num, u64 *result) { - uint32_t value; int status = 0; + u32 value; status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) + @@ -1313,11 +1303,10 @@ static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num, } /* Method to read the counter value */ -static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3, - uint64_t *result) +static int mlxbf_pmc_read_counter(int blk_num, u32 cnt_num, bool is_l3, u64 *result) { - uint32_t perfcfg_offset, perfval_offset; - uint64_t perfmon_cfg; + u32 perfcfg_offset, perfval_offset; + u64 perfmon_cfg; int status; if (cnt_num >= pmc->block[blk_num].counters) @@ -1351,13 +1340,11 @@ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3, } /* Method to read L3 block event */ -static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num, - uint64_t *result) +static int mlxbf_pmc_read_l3_event(int blk_num, u32 cnt_num, u64 *result) { - uint32_t perfcnt_sel = 0, perfcnt_sel_1 = 0; - uint32_t *wordaddr; + u32 perfcnt_sel = 0, perfcnt_sel_1 = 0, *wordaddr; void __iomem *pmcaddr; - uint64_t evt; + u64 evt; /* Select appropriate register information */ switch (cnt_num) { @@ -1405,10 +1392,9 @@ static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num, } /* Method to read crspace block event */ -static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num, - uint64_t *result) +static int mlxbf_pmc_read_crspace_event(int blk_num, u32 cnt_num, u64 *result) { - uint32_t word, evt; + u32 word, evt; void *addr; int ret; @@ -1429,11 +1415,10 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num, } /* Method to find the event currently being monitored by a counter */ -static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, - uint64_t *result) +static int mlxbf_pmc_read_event(int blk_num, u32 cnt_num, bool is_l3, u64 *result) { - uint32_t perfcfg_offset, perfval_offset; - uint64_t perfmon_cfg, perfevt; + u32 perfcfg_offset, perfval_offset; + u64 perfmon_cfg, perfevt; if (cnt_num >= pmc->block[blk_num].counters) return -EINVAL; @@ -1469,9 +1454,9 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, } /* Method to read a register */ -static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result) +static int mlxbf_pmc_read_reg(int blk_num, u32 offset, u64 *result) { - uint32_t ecc_out; + u32 ecc_out; if (strstr(pmc->block_name[blk_num], "ecc")) { if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset, @@ -1490,7 +1475,7 @@ static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result) } /* Method to write to a register */ -static int mlxbf_pmc_write_reg(int blk_num, uint32_t offset, uint64_t data) +static int mlxbf_pmc_write_reg(int blk_num, u32 offset, u64 data) { if (strstr(pmc->block_name[blk_num], "ecc")) { return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset, @@ -1512,7 +1497,7 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, attr, struct mlxbf_pmc_attribute, dev_attr); int blk_num, cnt_num, offset; bool is_l3 = false; - uint64_t value; + u64 value; blk_num = attr_counter->nr; cnt_num = attr_counter->index; @@ -1546,7 +1531,7 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev, attr, struct mlxbf_pmc_attribute, dev_attr); int blk_num, cnt_num, offset, err, data; bool is_l3 = false; - uint64_t evt_num; + u64 evt_num; blk_num = attr_counter->nr; cnt_num = attr_counter->index; @@ -1597,7 +1582,7 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev, attr, struct mlxbf_pmc_attribute, dev_attr); int blk_num, cnt_num, err; bool is_l3 = false; - uint64_t evt_num; + u64 evt_num; char *evt_name; blk_num = attr_event->nr; @@ -1686,7 +1671,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - uint32_t perfcnt_cfg, word; + u32 perfcnt_cfg, word; int blk_num, value; blk_num = attr_enable->nr; @@ -1718,7 +1703,7 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); int err, en, blk_num; - uint32_t word; + u32 word; blk_num = attr_enable->nr; @@ -1914,7 +1899,7 @@ static bool mlxbf_pmc_guid_match(const guid_t *guid, /* Helper to map the Performance Counters from the varios blocks */ static int mlxbf_pmc_map_counters(struct device *dev) { - uint64_t info[MLXBF_PMC_INFO_SZ]; + u64 info[MLXBF_PMC_INFO_SZ]; int i, tile_num, ret; for (i = 0; i < pmc->total_blocks; ++i) { -- cgit v1.2.3 From 1ae9ffd303c2028048be4cef6221a17442c0175d Mon Sep 17 00:00:00 2001 From: Shravan Kumar Ramani Date: Tue, 13 Feb 2024 06:15:26 -0500 Subject: platform/mellanox: mlxbf-pmc: Cleanup signed/unsigned mix-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use unsigned integer types for register values and array indices. Use %u instead of %d accordingly. Signed-off-by: Shravan Kumar Ramani Link: https://lore.kernel.org/r/d8548c70339a29258a906b2b518e5c48f669795c.1707808180.git.shravankr@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-pmc.c | 129 ++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 86044d1b8fa5..250405bb59a7 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -99,8 +99,8 @@ */ struct mlxbf_pmc_attribute { struct device_attribute dev_attr; - int index; - int nr; + unsigned int index; + unsigned int nr; }; /** @@ -121,7 +121,7 @@ struct mlxbf_pmc_block_info { void __iomem *mmio_base; size_t blk_size; size_t counters; - int type; + unsigned int type; struct mlxbf_pmc_attribute *attr_counter; struct mlxbf_pmc_attribute *attr_event; struct mlxbf_pmc_attribute attr_event_list; @@ -169,7 +169,7 @@ struct mlxbf_pmc_context { * @evt_name: Name of the event */ struct mlxbf_pmc_events { - int evt_num; + u32 evt_num; char *evt_name; }; @@ -956,7 +956,7 @@ static int mlxbf_pmc_write(void __iomem *addr, int command, u64 value) } /* Check if the register offset is within the mapped region for the block */ -static bool mlxbf_pmc_valid_range(int blk_num, u32 offset) +static bool mlxbf_pmc_valid_range(unsigned int blk_num, u32 offset) { if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) && (offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size)) @@ -966,8 +966,7 @@ static bool mlxbf_pmc_valid_range(int blk_num, u32 offset) } /* Get the event list corresponding to a certain block */ -static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, - int *size) +static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *size) { const struct mlxbf_pmc_events *events; @@ -1044,7 +1043,8 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, static int mlxbf_pmc_get_event_num(const char *blk, const char *evt) { const struct mlxbf_pmc_events *events; - int i, size; + unsigned int i; + size_t size; events = mlxbf_pmc_event_list(blk, &size); if (!events) @@ -1059,10 +1059,11 @@ static int mlxbf_pmc_get_event_num(const char *blk, const char *evt) } /* Get the event number given the name */ -static char *mlxbf_pmc_get_event_name(const char *blk, int evt) +static char *mlxbf_pmc_get_event_name(const char *blk, u32 evt) { const struct mlxbf_pmc_events *events; - int i, size; + unsigned int i; + size_t size; events = mlxbf_pmc_event_list(blk, &size); if (!events) @@ -1077,7 +1078,7 @@ static char *mlxbf_pmc_get_event_name(const char *blk, int evt) } /* Method to enable/disable/reset l3cache counters */ -static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset) +static int mlxbf_pmc_config_l3_counters(unsigned int blk_num, bool enable, bool reset) { u32 perfcnt_cfg = 0; @@ -1092,7 +1093,7 @@ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset) } /* Method to handle l3cache counter programming */ -static int mlxbf_pmc_program_l3_counter(int blk_num, u32 cnt_num, u32 evt) +static int mlxbf_pmc_program_l3_counter(unsigned int blk_num, u32 cnt_num, u32 evt) { u32 perfcnt_sel_1 = 0, perfcnt_sel = 0, *wordaddr; void __iomem *pmcaddr; @@ -1156,7 +1157,7 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, u32 cnt_num, u32 evt) } /* Method to handle crspace counter programming */ -static int mlxbf_pmc_program_crspace_counter(int blk_num, u32 cnt_num, u32 evt) +static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, u32 evt) { void *addr; u32 word; @@ -1180,7 +1181,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, u32 cnt_num, u32 evt) } /* Method to clear crspace counter value */ -static int mlxbf_pmc_clear_crspace_counter(int blk_num, u32 cnt_num) +static int mlxbf_pmc_clear_crspace_counter(unsigned int blk_num, u32 cnt_num) { void *addr; @@ -1192,7 +1193,7 @@ static int mlxbf_pmc_clear_crspace_counter(int blk_num, u32 cnt_num) } /* Method to program a counter to monitor an event */ -static int mlxbf_pmc_program_counter(int blk_num, u32 cnt_num, u32 evt, bool is_l3) +static int mlxbf_pmc_program_counter(unsigned int blk_num, u32 cnt_num, u32 evt, bool is_l3) { u64 perfctl, perfevt, perfmon_cfg; @@ -1255,7 +1256,7 @@ static int mlxbf_pmc_program_counter(int blk_num, u32 cnt_num, u32 evt, bool is_ } /* Method to handle l3 counter reads */ -static int mlxbf_pmc_read_l3_counter(int blk_num, u32 cnt_num, u64 *result) +static int mlxbf_pmc_read_l3_counter(unsigned int blk_num, u32 cnt_num, u64 *result) { u32 perfcnt_low = 0, perfcnt_high = 0; int status; @@ -1286,7 +1287,7 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, u32 cnt_num, u64 *result) } /* Method to handle crspace counter reads */ -static int mlxbf_pmc_read_crspace_counter(int blk_num, u32 cnt_num, u64 *result) +static int mlxbf_pmc_read_crspace_counter(unsigned int blk_num, u32 cnt_num, u64 *result) { int status = 0; u32 value; @@ -1303,7 +1304,7 @@ static int mlxbf_pmc_read_crspace_counter(int blk_num, u32 cnt_num, u64 *result) } /* Method to read the counter value */ -static int mlxbf_pmc_read_counter(int blk_num, u32 cnt_num, bool is_l3, u64 *result) +static int mlxbf_pmc_read_counter(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result) { u32 perfcfg_offset, perfval_offset; u64 perfmon_cfg; @@ -1340,7 +1341,7 @@ static int mlxbf_pmc_read_counter(int blk_num, u32 cnt_num, bool is_l3, u64 *res } /* Method to read L3 block event */ -static int mlxbf_pmc_read_l3_event(int blk_num, u32 cnt_num, u64 *result) +static int mlxbf_pmc_read_l3_event(unsigned int blk_num, u32 cnt_num, u64 *result) { u32 perfcnt_sel = 0, perfcnt_sel_1 = 0, *wordaddr; void __iomem *pmcaddr; @@ -1392,7 +1393,7 @@ static int mlxbf_pmc_read_l3_event(int blk_num, u32 cnt_num, u64 *result) } /* Method to read crspace block event */ -static int mlxbf_pmc_read_crspace_event(int blk_num, u32 cnt_num, u64 *result) +static int mlxbf_pmc_read_crspace_event(unsigned int blk_num, u32 cnt_num, u64 *result) { u32 word, evt; void *addr; @@ -1415,7 +1416,7 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, u32 cnt_num, u64 *result) } /* Method to find the event currently being monitored by a counter */ -static int mlxbf_pmc_read_event(int blk_num, u32 cnt_num, bool is_l3, u64 *result) +static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result) { u32 perfcfg_offset, perfval_offset; u64 perfmon_cfg, perfevt; @@ -1454,7 +1455,7 @@ static int mlxbf_pmc_read_event(int blk_num, u32 cnt_num, bool is_l3, u64 *resul } /* Method to read a register */ -static int mlxbf_pmc_read_reg(int blk_num, u32 offset, u64 *result) +static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result) { u32 ecc_out; @@ -1475,7 +1476,7 @@ static int mlxbf_pmc_read_reg(int blk_num, u32 offset, u64 *result) } /* Method to write to a register */ -static int mlxbf_pmc_write_reg(int blk_num, u32 offset, u64 data) +static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data) { if (strstr(pmc->block_name[blk_num], "ecc")) { return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset, @@ -1495,7 +1496,7 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_counter = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int blk_num, cnt_num, offset; + unsigned int blk_num, cnt_num, offset; bool is_l3 = false; u64 value; @@ -1529,14 +1530,15 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev, { struct mlxbf_pmc_attribute *attr_counter = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int blk_num, cnt_num, offset, err, data; + unsigned int blk_num, cnt_num, offset, data; bool is_l3 = false; u64 evt_num; + int err; blk_num = attr_counter->nr; cnt_num = attr_counter->index; - err = kstrtoint(buf, 0, &data); + err = kstrtouint(buf, 0, &data); if (err < 0) return err; @@ -1565,7 +1567,7 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev, if (err) return err; } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) { - if (sscanf(attr->attr.name, "counter%d", &cnt_num) != 1) + if (sscanf(attr->attr.name, "counter%u", &cnt_num) != 1) return -EINVAL; err = mlxbf_pmc_clear_crspace_counter(blk_num, cnt_num); } else @@ -1580,10 +1582,11 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_event = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int blk_num, cnt_num, err; + unsigned int blk_num, cnt_num; bool is_l3 = false; - u64 evt_num; char *evt_name; + u64 evt_num; + int err; blk_num = attr_event->nr; cnt_num = attr_event->index; @@ -1609,8 +1612,9 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev, { struct mlxbf_pmc_attribute *attr_event = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int blk_num, cnt_num, evt_num, err; + unsigned int blk_num, cnt_num, evt_num; bool is_l3 = false; + int err; blk_num = attr_event->nr; cnt_num = attr_event->index; @@ -1621,7 +1625,7 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev, if (evt_num < 0) return -EINVAL; } else { - err = kstrtoint(buf, 0, &evt_num); + err = kstrtouint(buf, 0, &evt_num); if (err < 0) return err; } @@ -1643,9 +1647,11 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_event_list = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int blk_num, i, size, len = 0, ret = 0; const struct mlxbf_pmc_events *events; char e_info[MLXBF_PMC_EVENT_INFO_LEN]; + unsigned int blk_num, i, len = 0; + size_t size; + int ret = 0; blk_num = attr_event_list->nr; @@ -1671,8 +1677,8 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); + unsigned int blk_num, value; u32 perfcnt_cfg, word; - int blk_num, value; blk_num = attr_enable->nr; @@ -1692,7 +1698,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev, value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); } - return sysfs_emit(buf, "%d\n", value); + return sysfs_emit(buf, "%u\n", value); } /* Store function for "enable" sysfs files - only for l3cache & crspace */ @@ -1702,12 +1708,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, { struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - int err, en, blk_num; + unsigned int en, blk_num; u32 word; + int err; blk_num = attr_enable->nr; - err = kstrtoint(buf, 0, &en); + err = kstrtouint(buf, 0, &en); if (err < 0) return err; @@ -1745,10 +1752,10 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, } /* Populate attributes for blocks with counters to monitor performance */ -static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) +static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num) { struct mlxbf_pmc_attribute *attr; - int i = 0, j = 0; + unsigned int i = 0, j = 0; /* "event_list" sysfs to list events supported by the block */ attr = &pmc->block[blk_num].attr_event_list; @@ -1797,8 +1804,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) attr->dev_attr.store = mlxbf_pmc_counter_store; attr->index = j; attr->nr = blk_num; - attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, - "counter%d", j); + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "counter%u", j); if (!attr->dev_attr.attr.name) return -ENOMEM; pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; @@ -1810,8 +1816,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) attr->dev_attr.store = mlxbf_pmc_event_store; attr->index = j; attr->nr = blk_num; - attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, - "event%d", j); + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "event%u", j); if (!attr->dev_attr.attr.name) return -ENOMEM; pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; @@ -1822,30 +1827,31 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) } /* Populate attributes for blocks with registers to monitor performance */ -static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num) +static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num) { - struct mlxbf_pmc_attribute *attr; const struct mlxbf_pmc_events *events; - int i = 0, j = 0; + struct mlxbf_pmc_attribute *attr; + unsigned int i = 0; + size_t count = 0; - events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &j); + events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &count); if (!events) return -EINVAL; pmc->block[blk_num].attr_event = devm_kcalloc( - dev, j, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); + dev, count, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); if (!pmc->block[blk_num].attr_event) return -ENOMEM; - while (j > 0) { - --j; - attr = &pmc->block[blk_num].attr_event[j]; + while (count > 0) { + --count; + attr = &pmc->block[blk_num].attr_event[count]; attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_counter_show; attr->dev_attr.store = mlxbf_pmc_counter_store; attr->nr = blk_num; attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, - events[j].evt_name); + events[count].evt_name); if (!attr->dev_attr.attr.name) return -ENOMEM; pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr; @@ -1857,7 +1863,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num) } /* Helper to create the bfperf sysfs sub-directories and files */ -static int mlxbf_pmc_create_groups(struct device *dev, int blk_num) +static int mlxbf_pmc_create_groups(struct device *dev, unsigned int blk_num) { int err; @@ -1900,18 +1906,19 @@ static bool mlxbf_pmc_guid_match(const guid_t *guid, static int mlxbf_pmc_map_counters(struct device *dev) { u64 info[MLXBF_PMC_INFO_SZ]; - int i, tile_num, ret; + unsigned int tile_num, i; + int ret; for (i = 0; i < pmc->total_blocks; ++i) { /* Create sysfs for tiles only if block number < tile_count */ if (strstr(pmc->block_name[i], "tilenet")) { - if (sscanf(pmc->block_name[i], "tilenet%d", &tile_num) != 1) + if (sscanf(pmc->block_name[i], "tilenet%u", &tile_num) != 1) continue; if (tile_num >= pmc->tile_count) continue; } else if (strstr(pmc->block_name[i], "tile")) { - if (sscanf(pmc->block_name[i], "tile%d", &tile_num) != 1) + if (sscanf(pmc->block_name[i], "tile%u", &tile_num) != 1) continue; if (tile_num >= pmc->tile_count) @@ -1921,9 +1928,9 @@ static int mlxbf_pmc_map_counters(struct device *dev) /* Create sysfs only for enabled MSS blocks */ if (strstr(pmc->block_name[i], "mss") && pmc->event_set == MLXBF_PMC_EVENT_SET_BF3) { - int mss_num; + unsigned int mss_num; - if (sscanf(pmc->block_name[i], "mss%d", &mss_num) != 1) + if (sscanf(pmc->block_name[i], "mss%u", &mss_num) != 1) continue; if (!((pmc->mss_enable >> mss_num) & 0x1)) @@ -1932,17 +1939,17 @@ static int mlxbf_pmc_map_counters(struct device *dev) /* Create sysfs only for enabled LLT blocks */ if (strstr(pmc->block_name[i], "llt_miss")) { - int llt_num; + unsigned int llt_num; - if (sscanf(pmc->block_name[i], "llt_miss%d", &llt_num) != 1) + if (sscanf(pmc->block_name[i], "llt_miss%u", &llt_num) != 1) continue; if (!((pmc->llt_enable >> llt_num) & 0x1)) continue; } else if (strstr(pmc->block_name[i], "llt")) { - int llt_num; + unsigned int llt_num; - if (sscanf(pmc->block_name[i], "llt%d", &llt_num) != 1) + if (sscanf(pmc->block_name[i], "llt%u", &llt_num) != 1) continue; if (!((pmc->llt_enable >> llt_num) & 0x1)) -- cgit v1.2.3 From c5211eacf3326538fbf31b612e5ea546ca8a3425 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Sat, 10 Feb 2024 23:51:57 +0100 Subject: platform/x86: ideapad-laptop: support Fn+R dual-function key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the manual, Fn+R adjusts the display refresh rate. Map Fn+R to KEY_DISPLAYTOGGLE. Signed-off-by: Gergo Koteles Link: https://lore.kernel.org/r/0cdbc0e6eb65e160384ae0ed152e7de3ded1d9d5.1707604991.git.soyer@irl.hu Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 88eefccb6ed2..4c130957f80d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1091,6 +1091,8 @@ static const struct key_entry ideapad_keymap[] = { { KE_KEY, 0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } }, { KE_KEY, 0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } }, { KE_KEY, 0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } }, + /* Refresh Rate Toggle (Fn+R) */ + { KE_KEY, 0x10 | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } }, /* Dark mode toggle */ { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } }, /* Sound profile switch */ -- cgit v1.2.3 From ee8b738e7f7adf78700afd9ab9642fe015464885 Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Thu, 15 Feb 2024 22:41:01 +0900 Subject: platform/x86: thinkpad_acpi: Simplify thermal mode checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a thermal_read_mode_check helper to make the code simpler during init. This helps particularly when the new TPACPI_THERMAL_TPEC_12 mode is added in the next patch. Suggested-by: Ilpo Järvinen Signed-off-by: Vishnu Sankar Link: https://lore.kernel.org/r/20240215134102.25118-1-vishnuocv@gmail.com [ij: Reflowed the comment to 80 cols, multiline if braces, added __init.] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 143 ++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 897d4cd9e5f4..9a49573ae378 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6149,6 +6149,78 @@ struct ibm_thermal_sensors_struct { static enum thermal_access_mode thermal_read_mode; static bool thermal_use_labels; +/* Function to check thermal read mode */ +static enum thermal_access_mode __init thermal_read_mode_check(void) +{ + u8 t, ta1, ta2, ver = 0; + int i; + int acpi_tmp7; + + acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (thinkpad_id.ec_model) { + /* + * Direct EC access mode: sensors at registers 0x78-0x7F, + * 0xC0-0xC7. Registers return 0x00 for non-implemented, + * thermal sensors return 0x80 when not available. + * + * In some special cases (when Power Supply ID is 0xC2) + * above rule causes thermal control issues. Offset 0xEF + * determines EC version. 0xC0-0xC7 are not thermal registers + * in Ver 3. + */ + if (!acpi_ec_read(TP_EC_FUNCREV, &ver)) + pr_warn("Thinkpad ACPI EC unable to access EC version\n"); + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (ver < 3) { + if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + } + + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n"); + return TPACPI_THERMAL_ACPI_TMP07; + } + pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n"); + return TPACPI_THERMAL_NONE; + } + + if (ver >= 3) { + thermal_use_labels = true; + return TPACPI_THERMAL_TPEC_8; + } + + return (ta2 != 0) ? TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; + } + + if (acpi_tmp7) { + if (tpacpi_is_ibm() && acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + return TPACPI_THERMAL_ACPI_UPDT; + } + /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ + return TPACPI_THERMAL_ACPI_TMP07; + } + + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + return TPACPI_THERMAL_NONE; +} + /* idx is zero-based */ static int thermal_get_sensor(int idx, s32 *value) { @@ -6375,78 +6447,9 @@ static const struct attribute_group temp_label_attr_group = { static int __init thermal_init(struct ibm_init_struct *iibm) { - u8 t, ta1, ta2, ver = 0; - int i; - int acpi_tmp7; - vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); - acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - if (thinkpad_id.ec_model) { - /* - * Direct EC access mode: sensors at registers - * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for - * non-implemented, thermal sensors return 0x80 when - * not available - * The above rule is unfortunately flawed. This has been seen with - * 0xC2 (power supply ID) causing thermal control problems. - * The EC version can be determined by offset 0xEF and at least for - * version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7 - * are not thermal registers. - */ - if (!acpi_ec_read(TP_EC_FUNCREV, &ver)) - pr_warn("Thinkpad ACPI EC unable to access EC version\n"); - - ta1 = ta2 = 0; - for (i = 0; i < 8; i++) { - if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { - ta1 |= t; - } else { - ta1 = 0; - break; - } - if (ver < 3) { - if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { - ta2 |= t; - } else { - ta1 = 0; - break; - } - } - } - if (ta1 == 0) { - /* This is sheer paranoia, but we handle it anyway */ - if (acpi_tmp7) { - pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n"); - thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; - } else { - pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n"); - thermal_read_mode = TPACPI_THERMAL_NONE; - } - } else { - if (ver >= 3) { - thermal_read_mode = TPACPI_THERMAL_TPEC_8; - thermal_use_labels = true; - } else { - thermal_read_mode = - (ta2 != 0) ? - TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; - } - } - } else if (acpi_tmp7) { - if (tpacpi_is_ibm() && - acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { - /* 600e/x, 770e, 770x */ - thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; - } else { - /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ - thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; - } - } else { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_read_mode = TPACPI_THERMAL_NONE; - } + thermal_read_mode = thermal_read_mode_check(); vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n", str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), -- cgit v1.2.3 From 301c1904d638ea9f9c9da34952a63faa5ceed26b Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Thu, 15 Feb 2024 22:41:02 +0900 Subject: platform/x86: thinkpad_acpi: Fix to correct wrong temp reporting on some ThinkPads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added non-standard thermal register's support for some ThinkPads. Some of the Thinkpads use a non-standard ECFW which has different thermal register addresses. This is a fix to correct the wrong temperature reporting on those systems. Tested on Lenovo ThinkPad L13 Yoga Gen2. Suggested-by: Mark Pearson Signed-off-by: Vishnu Sankar Link: https://lore.kernel.org/r/20240215134102.25118-2-vishnuocv@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 74 +++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9a49573ae378..df4ee025516c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -6128,12 +6129,15 @@ enum thermal_access_mode { TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + TPACPI_THERMAL_TPEC_12, /* Use ACPI EC regs, 12 sensors */ TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ }; enum { /* TPACPI_THERMAL_TPEC_* */ TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ + TP_EC_THERMAL_TMP0_NS = 0xA8, /* ACPI EC Non-Standard regs TMP 0..7 */ + TP_EC_THERMAL_TMP8_NS = 0xB8, /* ACPI EC Non-standard regs TMP 8..11 */ TP_EC_FUNCREV = 0xEF, /* ACPI EC Functional revision */ TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ @@ -6146,8 +6150,22 @@ struct ibm_thermal_sensors_struct { s32 temp[TPACPI_MAX_THERMAL_SENSORS]; }; +static const struct tpacpi_quirk thermal_quirk_table[] __initconst = { + /* Non-standard address for thermal registers on some ThinkPads */ + TPACPI_Q_LNV3('R', '1', 'F', true), /* L13 Yoga Gen 2 */ + TPACPI_Q_LNV3('N', '2', 'U', true), /* X13 Yoga Gen 2*/ + TPACPI_Q_LNV3('R', '0', 'R', true), /* L380 */ + TPACPI_Q_LNV3('R', '1', '5', true), /* L13 Yoga Gen 1*/ + TPACPI_Q_LNV3('R', '1', '0', true), /* L390 */ + TPACPI_Q_LNV3('N', '2', 'L', true), /* X13 Yoga Gen 1*/ + TPACPI_Q_LNV3('R', '0', 'T', true), /* 11e Gen5 GL*/ + TPACPI_Q_LNV3('R', '1', 'D', true), /* 11e Gen5 GL-R*/ + TPACPI_Q_LNV3('R', '0', 'V', true), /* 11e Gen5 KL-Y*/ +}; + static enum thermal_access_mode thermal_read_mode; static bool thermal_use_labels; +static bool thermal_with_ns_address; /* Non-standard thermal reg address */ /* Function to check thermal read mode */ static enum thermal_access_mode __init thermal_read_mode_check(void) @@ -6172,6 +6190,16 @@ static enum thermal_access_mode __init thermal_read_mode_check(void) if (!acpi_ec_read(TP_EC_FUNCREV, &ver)) pr_warn("Thinkpad ACPI EC unable to access EC version\n"); + /* Quirks to check non-standard EC */ + thermal_with_ns_address = tpacpi_check_quirks(thermal_quirk_table, + ARRAY_SIZE(thermal_quirk_table)); + + /* Support for Thinkpads with non-standard address */ + if (thermal_with_ns_address) { + pr_info("ECFW with non-standard thermal registers found\n"); + return TPACPI_THERMAL_TPEC_12; + } + ta1 = ta2 = 0; for (i = 0; i < 8; i++) { if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { @@ -6248,6 +6276,20 @@ static int thermal_get_sensor(int idx, s32 *value) } break; + /* The Non-standard EC uses 12 Thermal areas */ + case TPACPI_THERMAL_TPEC_12: + if (idx >= 12) + return -EINVAL; + + t = idx < 8 ? TP_EC_THERMAL_TMP0_NS + idx : + TP_EC_THERMAL_TMP8_NS + (idx - 8); + + if (!acpi_ec_read(t, &tmp)) + return -EIO; + + *value = tmp * MILLIDEGREE_PER_DEGREE; + return 0; + case TPACPI_THERMAL_ACPI_UPDT: if (idx <= 7) { snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); @@ -6289,6 +6331,8 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) n = 16; + else if (thermal_read_mode == TPACPI_THERMAL_TPEC_12) + n = 12; else n = 8; @@ -6389,18 +6433,36 @@ static struct attribute *thermal_temp_input_attr[] = { NULL }; +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + static umode_t thermal_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - if (thermal_read_mode == TPACPI_THERMAL_NONE) + struct device_attribute *dev_attr = to_dev_attr(attr); + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(dev_attr); + + int idx = sensor_attr->index; + + switch (thermal_read_mode) { + case TPACPI_THERMAL_NONE: return 0; - if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) || - attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) || - attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) || - attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) { - if (thermal_read_mode != TPACPI_THERMAL_TPEC_16) + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + case TPACPI_THERMAL_TPEC_8: + if (idx >= 8) return 0; + break; + + case TPACPI_THERMAL_TPEC_12: + if (idx >= 12) + return 0; + break; + + default: + break; + } return attr->mode; -- cgit v1.2.3 From 14c8a1451810cf616c959574bee4fad246f3a0cb Mon Sep 17 00:00:00 2001 From: Szilard Fabian Date: Thu, 15 Feb 2024 20:31:43 +0000 Subject: platform/x86/fujitsu-laptop: Add battery charge control support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds battery charge control support on Fujitsu notebooks via the S006 method of the FUJ02E3 ACPI device. With this method it's possible to set charge_control_end_threshold between 50 and 100%. Tested on Lifebook E5411 and Lifebook U728. Sadly I can't test this patch on a dual battery one, but I didn't find any clue about independent battery charge control on dual battery Fujitsu notebooks either. And by that I mean checking the DSDT table of various Lifebook notebooks and reverse engineering FUJ02E3.dll. Signed-off-by: Szilard Fabian Acked-by: Jonathan Woithe Link: https://lore.kernel.org/r/20240215203012.228758-2-szfabian@bluemarch.art [ij: coding style cleanups.] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/fujitsu-laptop.c | 117 ++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 6dbd40e2aeda..7e9251fc3341 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -370,6 +370,7 @@ config FUJITSU_LAPTOP depends on ACPI depends on INPUT depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_BATTERY depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP select NEW_LEDS diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 085e044e888e..94480af49467 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #define FUJITSU_DRIVER_VERSION "0.6.0" @@ -97,6 +99,10 @@ #define BACKLIGHT_OFF (BIT(0) | BIT(1)) #define BACKLIGHT_ON 0 +/* FUNC interface - battery control interface */ +#define FUNC_S006_METHOD 0x1006 +#define CHARGE_CONTROL_RW 0x21 + /* Scancodes read from the GIRB register */ #define KEY1_CODE 0x410 #define KEY2_CODE 0x411 @@ -132,6 +138,7 @@ struct fujitsu_laptop { spinlock_t fifo_lock; int flags_supported; int flags_state; + bool charge_control_supported; }; static struct acpi_device *fext; @@ -164,6 +171,110 @@ static int call_fext_func(struct acpi_device *device, return value; } +/* Battery charge control code */ +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int cc_end_value, s006_cc_return; + int value, ret; + + ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value < 50 || value > 100) + return -EINVAL; + + cc_end_value = value * 0x100 + 0x20; + s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD, + CHARGE_CONTROL_RW, cc_end_value, 0x0); + if (s006_cc_return < 0) + return s006_cc_return; + /* + * The S006 0x21 method returns 0x00 in case the provided value + * is invalid. + */ + if (s006_cc_return == 0x00) + return -EINVAL; + + return count; +} + +static ssize_t charge_control_end_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = call_fext_func(fext, FUNC_S006_METHOD, + CHARGE_CONTROL_RW, 0x21, 0x0); + if (status < 0) + return status; + + return sysfs_emit(buf, "%d\n", status); +} + +static DEVICE_ATTR_RW(charge_control_end_threshold); + +/* ACPI battery hook */ +static int fujitsu_battery_add_hook(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + return device_create_file(&battery->dev, + &dev_attr_charge_control_end_threshold); +} + +static int fujitsu_battery_remove_hook(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + device_remove_file(&battery->dev, + &dev_attr_charge_control_end_threshold); + + return 0; +} + +static struct acpi_battery_hook battery_hook = { + .add_battery = fujitsu_battery_add_hook, + .remove_battery = fujitsu_battery_remove_hook, + .name = "Fujitsu Battery Extension", +}; + +/* + * These functions are intended to be called from acpi_fujitsu_laptop_add and + * acpi_fujitsu_laptop_remove. + */ +static int fujitsu_battery_charge_control_add(struct acpi_device *device) +{ + struct fujitsu_laptop *priv = acpi_driver_data(device); + int s006_cc_return; + + priv->charge_control_supported = false; + /* + * Check if the S006 0x21 method exists by trying to get the current + * battery charge limit. + */ + s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD, + CHARGE_CONTROL_RW, 0x21, 0x0); + if (s006_cc_return < 0) + return s006_cc_return; + if (s006_cc_return == UNSUPPORTED_CMD) + return -ENODEV; + + priv->charge_control_supported = true; + battery_hook_register(&battery_hook); + + return 0; +} + +static void fujitsu_battery_charge_control_remove(struct acpi_device *device) +{ + struct fujitsu_laptop *priv = acpi_driver_data(device); + + if (priv->charge_control_supported) + battery_hook_unregister(&battery_hook); +} + /* Hardware access for LCD brightness control */ static int set_lcd_level(struct acpi_device *device, int level) @@ -839,6 +950,10 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) if (ret) goto err_free_fifo; + ret = fujitsu_battery_charge_control_add(device); + if (ret < 0) + pr_warn("Unable to register battery charge control: %d\n", ret); + return 0; err_free_fifo: @@ -851,6 +966,8 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device) { struct fujitsu_laptop *priv = acpi_driver_data(device); + fujitsu_battery_charge_control_remove(device); + fujitsu_laptop_platform_remove(device); kfifo_free(&priv->fifo); -- cgit v1.2.3 From 4f299135d5668f56be270d224d41eb83d2002038 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 19 Feb 2024 12:59:15 +0100 Subject: platform/x86: wmi: Prevent incompatible event driver from probing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a WMI event driver has no_notify_data set, then it indicates support for WMI events which provide no notify data, otherwise the notify() callback expects a valid ACPI object as notify data. However if a WMI event driver which requires notify data is bound to a WMI event device which cannot retrieve such data due to the _WED ACPI method being absent, then the driver will be dysfunctional since all WMI events will be dropped due to the missing notify data. Fix this by not allowing such WMI event drivers to bind to WMI event devices which do not support retrieving of notify data. Also reword the description of no_notify_data a bit. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240219115919.16526-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 10 ++++++++++ include/linux/wmi.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 5a613b06b269..8fb90b726f50 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -57,6 +57,7 @@ static_assert(__alignof__(struct guid_block) == 1); enum { /* wmi_block flags */ WMI_READ_TAKES_NO_ARGS, + WMI_NO_EVENT_DATA, }; struct wmi_block { @@ -869,6 +870,11 @@ static int wmi_dev_probe(struct device *dev) struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); int ret = 0; + if (wdriver->notify) { + if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data) + return -ENODEV; + } + if (ACPI_FAILURE(wmi_method_enable(wblock, true))) dev_warn(dev, "failed to enable device -- probing anyway\n"); @@ -1094,6 +1100,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; const struct guid_block *gblock; + bool event_data_available; struct wmi_block *wblock; union acpi_object *obj; acpi_status status; @@ -1113,6 +1120,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) return -ENXIO; } + event_data_available = acpi_has_method(device->handle, "_WED"); gblock = (const struct guid_block *)obj->buffer.pointer; total = obj->buffer.length / sizeof(struct guid_block); @@ -1131,6 +1139,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) wblock->acpi_device = device; wblock->gblock = gblock[i]; + if (gblock[i].flags & ACPI_WMI_EVENT && !event_data_available) + set_bit(WMI_NO_EVENT_DATA, &wblock->flags); retval = wmi_create_device(wmi_bus_dev, wblock, device); if (retval) { diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 686291b87852..781958310bfb 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -48,7 +48,7 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * struct wmi_driver - WMI driver structure * @driver: Driver model structure * @id_table: List of WMI GUIDs supported by this driver - * @no_notify_data: WMI events provide no event data + * @no_notify_data: Driver supports WMI events which provide no event data * @probe: Callback for device binding * @remove: Callback for device unbinding * @notify: Callback for receiving WMI events -- cgit v1.2.3 From 125619112deaf5f7d79b05e268254df3af916d10 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 19 Feb 2024 12:59:16 +0100 Subject: platform/x86: wmi: Check if event data is not NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WMI event drivers which do not have no_notify_data set expect that each WMI event contains valid data. Evaluating _WED however might return no data, which can cause issues with such drivers. Fix this by validating that evaluating _WED did return data. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240219115919.16526-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 8fb90b726f50..ff4742c40cc3 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1210,6 +1210,7 @@ static void wmi_notify_driver(struct wmi_block *wblock) { struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj = NULL; acpi_status status; if (!driver->no_notify_data) { @@ -1218,12 +1219,18 @@ static void wmi_notify_driver(struct wmi_block *wblock) dev_warn(&wblock->dev.dev, "Failed to get event data\n"); return; } + + obj = data.pointer; + if (!obj) { + dev_warn(&wblock->dev.dev, "Event contains no event data\n"); + return; + } } if (driver->notify) - driver->notify(&wblock->dev, data.pointer); + driver->notify(&wblock->dev, obj); - kfree(data.pointer); + kfree(obj); } static int wmi_notify_device(struct device *dev, void *data) -- cgit v1.2.3 From 56230bd733f8cb122632c80bc49ae12b1a9365a6 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 19 Feb 2024 12:59:17 +0100 Subject: platform/x86: wmi: Always evaluate _WED when receiving an event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACPI WMI specification states: "The _WED control method is evaluated by the mapper in response to receiving a notification from a control method." This means that _WED should be evaluated unconditionally even if no WMI event consumers are present. Some firmware implementations actually depend on this behavior by storing the event data inside a queue which will fill up if the WMI core stops retrieving event data items due to no consumers being present Fix this by always evaluating _WED even if no WMI event consumers are present. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240219115919.16526-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 67 +++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index ff4742c40cc3..abd0183c4107 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1206,37 +1206,46 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, } } -static void wmi_notify_driver(struct wmi_block *wblock) +static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj) { - struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj = NULL; acpi_status status; - if (!driver->no_notify_data) { - status = get_event_data(wblock, &data); - if (ACPI_FAILURE(status)) { - dev_warn(&wblock->dev.dev, "Failed to get event data\n"); - return; - } + if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { + *obj = NULL; + return 0; + } - obj = data.pointer; - if (!obj) { - dev_warn(&wblock->dev.dev, "Event contains no event data\n"); - return; - } + status = get_event_data(wblock, &data); + if (ACPI_FAILURE(status)) { + dev_warn(&wblock->dev.dev, "Failed to get event data\n"); + return -EIO; + } + + *obj = data.pointer; + + return 0; +} + +static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) +{ + struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); + + if (!obj && !driver->no_notify_data) { + dev_warn(&wblock->dev.dev, "Event contains no event data\n"); + return; } if (driver->notify) driver->notify(&wblock->dev, obj); - - kfree(obj); } static int wmi_notify_device(struct device *dev, void *data) { struct wmi_block *wblock = dev_to_wblock(dev); + union acpi_object *obj; u32 *event = data; + int ret; if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) return 0; @@ -1246,10 +1255,32 @@ static int wmi_notify_device(struct device *dev, void *data) * Because of this the WMI driver notify handler takes precedence. */ if (wblock->dev.dev.driver && wblock->driver_ready) { - wmi_notify_driver(wblock); + ret = wmi_get_notify_data(wblock, &obj); + if (ret >= 0) { + wmi_notify_driver(wblock, obj); + kfree(obj); + } } else { - if (wblock->handler) + if (wblock->handler) { wblock->handler(*event, wblock->handler_data); + } else { + /* The ACPI WMI specification says that _WED should be + * evaluated every time an notification is received, even + * if no consumers are present. + * + * Some firmware implementations actually depend on this + * by using a queue for events which will fill up if the + * WMI driver core stops evaluating _WED due to missing + * WMI event consumers. + * + * Because of this we need this seemingly useless call to + * wmi_get_notify_data() which in turn evaluates _WED. + */ + ret = wmi_get_notify_data(wblock, &obj); + if (ret >= 0) + kfree(obj); + } + } up_read(&wblock->notify_lock); -- cgit v1.2.3 From 3c4303518a118a4fd5ce879bc2ad399004f6838d Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 19 Feb 2024 12:59:18 +0100 Subject: platform/x86: wmi: Update documentation regarding _WED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the WMI ACPI interface documentation to include the fact that _WED should be evaluated every time an ACPI notification is received. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240219115919.16526-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/wmi/acpi-interface.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/wmi/acpi-interface.rst b/Documentation/wmi/acpi-interface.rst index d31af0ed9c08..06fb7fcf4413 100644 --- a/Documentation/wmi/acpi-interface.rst +++ b/Documentation/wmi/acpi-interface.rst @@ -93,4 +93,7 @@ _WED ACPI method ---------------- Used to retrieve additional WMI event data, its single parameter is a integer -holding the notification ID of the event. +holding the notification ID of the event. This method should be evaluated every +time an ACPI notification is received, since some ACPI implementations use a +queue to store WMI event data items. This queue will overflow after a couple +of WMI events are received without retrieving the associated WMI event data. -- cgit v1.2.3 From 86682be1e82b18ece4391ca4cfd58fb422058952 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 19 Feb 2024 12:59:19 +0100 Subject: Revert "platform/x86: asus-wmi: Support WMI event queue" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1a373d15e283937b51eaf5debf4fc31474c31436. The WMI core now takes care of draining the event queue if asus-wmi is not loaded, so the hacky event queue handling code is not needed anymore. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240219115919.16526-6-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 71 +++-------------------------------------- 1 file changed, 5 insertions(+), 66 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 21dee425ea6f..2865af89e95c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -101,13 +101,6 @@ module_param(fnlock_default, bool, 0444); #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" -#define ASUS_ACPI_UID_ATK "ATK" - -#define WMI_EVENT_QUEUE_SIZE 0x10 -#define WMI_EVENT_QUEUE_END 0x1 -#define WMI_EVENT_MASK 0xFFFF -/* The WMI hotkey event value is always the same. */ -#define WMI_EVENT_VALUE_ATK 0xFF #define WMI_EVENT_MASK 0xFFFF @@ -219,7 +212,6 @@ struct asus_wmi { int dsts_id; int spec; int sfun; - bool wmi_event_queue; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -4019,50 +4011,14 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) static void asus_wmi_notify(u32 value, void *context) { struct asus_wmi *asus = context; - int code; - int i; - - 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; - } - - if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK) - return; - - asus_wmi_handle_event_code(code, asus); - - /* - * Double check that queue is present: - * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2. - */ - if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK) - return; - } + int code = asus_wmi_get_event_code(value); - pr_warn("Failed to process event queue, last code: 0x%x\n", code); -} - -static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) -{ - int code; - int i; - - 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; - } - - if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK) - return 0; + if (code < 0) { + pr_warn("Failed to get notify code: %d\n", code); + return; } - pr_warn("Failed to flush event queue\n"); - return -EIO; + asus_wmi_handle_event_code(code, asus); } /* Sysfs **********************************************************************/ @@ -4302,23 +4258,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) asus->dsts_id = ASUS_WMI_METHODID_DSTS; } - /* - * Some devices can have multiple event codes stored in a queue before - * the module load if it was unloaded intermittently after calling - * the INIT method (enables event handling). The WMI notify handler is - * expected to retrieve all event codes until a retrieved code equals - * queue end marker (One or Ones). Old codes are flushed from the queue - * upon module load. Not enabling this when it should be has minimal - * visible impact so fall back if anything goes wrong. - */ - wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid); - if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) { - dev_info(dev, "Detected ATK, enable event queue\n"); - - if (!asus_wmi_notify_queue_flush(asus)) - asus->wmi_event_queue = true; - } - /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ if (asus->driver->quirks->wapf >= 0) -- cgit v1.2.3 From f9ccdb4285a61a24db7dee61f19c4252fdd153c2 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 23 Feb 2024 17:29:05 +0100 Subject: platform/x86: dell-privacy: Remove usage of wmi_has_guid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WMI driver core already takes care that the WMI driver is only bound to WMI devices with a matching GUID. Remove the unnecessary call to wmi_has_guid(), which will always be true when the driver probes. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240223162905.12416-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-privacy.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c index 4d94603f7785..4b65e1655d42 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.c +++ b/drivers/platform/x86/dell/dell-wmi-privacy.c @@ -297,10 +297,6 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context) struct key_entry *keymap; int ret, i, j; - ret = wmi_has_guid(DELL_PRIVACY_GUID); - if (!ret) - pr_debug("Unable to detect available Dell privacy devices!\n"); - priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; -- cgit v1.2.3 From 0d46439bda37e2e13f14d6a9e211c4f645c6336a Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 22 Feb 2024 15:57:29 -0500 Subject: platform/mellanox: mlxbf-pmc: mlxbf_pmc_event_list(): make size ptr optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mlxbf_pmc_event_list() function returns a pointer to an array of supported events and the array size. The array size is returned via a pointer passed as an argument, which is mandatory. However, we want to be able to use mlxbf_pmc_event_list() just to check if a block name is implemented/supported. For this usage passing the size argument is not necessary so let's make it optional. Signed-off-by: Luiz Capitulino Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/182de8ec6b9c33152f2ba6b248c35b0311abf5e4.1708635408.git.luizcap@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-pmc.c | 40 +++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 250405bb59a7..b71636eb3db1 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -966,32 +966,33 @@ static bool mlxbf_pmc_valid_range(unsigned int blk_num, u32 offset) } /* Get the event list corresponding to a certain block */ -static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *size) +static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *psize) { const struct mlxbf_pmc_events *events; + size_t size; if (strstr(blk, "tilenet")) { events = mlxbf_pmc_hnfnet_events; - *size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events); + size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events); } else if (strstr(blk, "tile")) { events = mlxbf_pmc_hnf_events; - *size = ARRAY_SIZE(mlxbf_pmc_hnf_events); + size = ARRAY_SIZE(mlxbf_pmc_hnf_events); } else if (strstr(blk, "triogen")) { events = mlxbf_pmc_smgen_events; - *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); + size = ARRAY_SIZE(mlxbf_pmc_smgen_events); } else if (strstr(blk, "trio")) { switch (pmc->event_set) { case MLXBF_PMC_EVENT_SET_BF1: events = mlxbf_pmc_trio_events_1; - *size = ARRAY_SIZE(mlxbf_pmc_trio_events_1); + size = ARRAY_SIZE(mlxbf_pmc_trio_events_1); break; case MLXBF_PMC_EVENT_SET_BF2: events = mlxbf_pmc_trio_events_2; - *size = ARRAY_SIZE(mlxbf_pmc_trio_events_2); + size = ARRAY_SIZE(mlxbf_pmc_trio_events_2); break; default: events = NULL; - *size = 0; + size = 0; break; } } else if (strstr(blk, "mss")) { @@ -999,43 +1000,46 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size case MLXBF_PMC_EVENT_SET_BF1: case MLXBF_PMC_EVENT_SET_BF2: events = mlxbf_pmc_mss_events_1; - *size = ARRAY_SIZE(mlxbf_pmc_mss_events_1); + size = ARRAY_SIZE(mlxbf_pmc_mss_events_1); break; case MLXBF_PMC_EVENT_SET_BF3: events = mlxbf_pmc_mss_events_3; - *size = ARRAY_SIZE(mlxbf_pmc_mss_events_3); + size = ARRAY_SIZE(mlxbf_pmc_mss_events_3); break; default: events = NULL; - *size = 0; + size = 0; break; } } else if (strstr(blk, "ecc")) { events = mlxbf_pmc_ecc_events; - *size = ARRAY_SIZE(mlxbf_pmc_ecc_events); + size = ARRAY_SIZE(mlxbf_pmc_ecc_events); } else if (strstr(blk, "pcie")) { events = mlxbf_pmc_pcie_events; - *size = ARRAY_SIZE(mlxbf_pmc_pcie_events); + size = ARRAY_SIZE(mlxbf_pmc_pcie_events); } else if (strstr(blk, "l3cache")) { events = mlxbf_pmc_l3c_events; - *size = ARRAY_SIZE(mlxbf_pmc_l3c_events); + size = ARRAY_SIZE(mlxbf_pmc_l3c_events); } else if (strstr(blk, "gic")) { events = mlxbf_pmc_smgen_events; - *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); + size = ARRAY_SIZE(mlxbf_pmc_smgen_events); } else if (strstr(blk, "smmu")) { events = mlxbf_pmc_smgen_events; - *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); + size = ARRAY_SIZE(mlxbf_pmc_smgen_events); } else if (strstr(blk, "llt_miss")) { events = mlxbf_pmc_llt_miss_events; - *size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events); + size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events); } else if (strstr(blk, "llt")) { events = mlxbf_pmc_llt_events; - *size = ARRAY_SIZE(mlxbf_pmc_llt_events); + size = ARRAY_SIZE(mlxbf_pmc_llt_events); } else { events = NULL; - *size = 0; + size = 0; } + if (psize) + *psize = size; + return events; } -- cgit v1.2.3 From c0459eeb64e955b8fb9dd9f73b937f750387ef9a Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 22 Feb 2024 15:57:30 -0500 Subject: platform/mellanox: mlxbf-pmc: Ignore unsupported performance blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the driver has two behaviors to deal with new & unsupported performance blocks reported by the firmware: 1. For register and unknown block types, the driver will fail to load with the following error message: [ 4510.956369] mlxbf-pmc: probe of MLNXBFD2:00 failed with error -22 2. For counter and crspace blocks, the driver will load and sysfs files will be created but getting the contents of event_list or trying to setup the counter will fail Instead, let's ignore and log unsupported blocks. This means the driver will always load and unsupported blocks will never show up in sysfs. Signed-off-by: Luiz Capitulino Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/f8e2e6210b43e825b69824b420c801cd513d401d.1708635408.git.luizcap@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-pmc.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index b71636eb3db1..746567767e5b 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1043,6 +1043,11 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size return events; } +static bool mlxbf_pmc_event_supported(const char *blk) +{ + return !!mlxbf_pmc_event_list(blk, NULL); +} + /* Get the event number given the name */ static int mlxbf_pmc_get_event_num(const char *blk, const char *evt) { @@ -1761,6 +1766,9 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ struct mlxbf_pmc_attribute *attr; unsigned int i = 0, j = 0; + if (!mlxbf_pmc_event_supported(pmc->block_name[blk_num])) + return -ENOENT; + /* "event_list" sysfs to list events supported by the block */ attr = &pmc->block[blk_num].attr_event_list; attr->dev_attr.attr.mode = 0444; @@ -1840,7 +1848,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num) events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &count); if (!events) - return -EINVAL; + return -ENOENT; pmc->block[blk_num].attr_event = devm_kcalloc( dev, count, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); @@ -1878,7 +1886,7 @@ static int mlxbf_pmc_create_groups(struct device *dev, unsigned int blk_num) else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) err = mlxbf_pmc_init_perftype_reg(dev, blk_num); else - err = -EINVAL; + err = -ENOENT; if (err) return err; @@ -1983,6 +1991,10 @@ static int mlxbf_pmc_map_counters(struct device *dev) return -ENOMEM; ret = mlxbf_pmc_create_groups(dev, i); + if (ret == -ENOENT) { + dev_warn(dev, "ignoring unsupported block: '%s'\n", pmc->block_name[i]); + continue; + } if (ret) return ret; } -- cgit v1.2.3 From cfeb98b95fff25c442f78a6f616c627bc48a26b7 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Sun, 10 Mar 2024 12:31:41 +0100 Subject: Input: allocate keycode for Display refresh rate toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer Lenovo Yogas and Legions with 60Hz/90Hz displays send a wmi event when Fn + R is pressed. This is intended for use to switch between the two refresh rates. Allocate a new KEY_REFRESH_RATE_TOGGLE keycode for it. Signed-off-by: Gergo Koteles Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/15a5d08c84cf4d7b820de34ebbcf8ae2502fb3ca.1710065750.git.soyer@irl.hu Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/uapi/linux/input-event-codes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 022a520e31fc..03edf2ccdf6c 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -602,6 +602,7 @@ #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ +#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */ #define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ #define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ -- cgit v1.2.3 From f492f5f3e4e54043baeb38bfb494b2f959a636b7 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Sun, 10 Mar 2024 12:31:42 +0100 Subject: platform/x86: ideapad-laptop: map Fn + R key to KEY_REFRESH_RATE_TOGGLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer Lenovo Yogas and Legions with 60Hz/90Hz displays send a wmi event when Fn + R is pressed. This is intended for use to switch between the two refresh rates. The Fn + R key was incorrectly assigned to KEY_DISPLAYTOGGLE because it is used to toggle the display on and off. Map Fn + R key to the KEY_REFRESH_RATE_TOGGLE event code. Signed-off-by: Gergo Koteles Link: https://lore.kernel.org/r/8fd36f0f016dde700396d8afaba1979d5dbc30a1.1710065750.git.soyer@irl.hu Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/ideapad-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 4c130957f80d..901849810ce2 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1092,7 +1092,7 @@ static const struct key_entry ideapad_keymap[] = { { KE_KEY, 0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } }, { KE_KEY, 0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } }, /* Refresh Rate Toggle (Fn+R) */ - { KE_KEY, 0x10 | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } }, + { KE_KEY, 0x10 | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } }, /* Dark mode toggle */ { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } }, /* Sound profile switch */ @@ -1102,7 +1102,7 @@ static const struct key_entry ideapad_keymap[] = { /* Lenovo Support */ { KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } }, /* Refresh Rate Toggle */ - { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } }, + { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } }, { KE_END }, }; -- cgit v1.2.3 From 928439582b3133a5e845ec15ee6eb6838651024f Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 26 Feb 2024 20:35:55 +0100 Subject: platform/x86: wmi: Ignore duplicated GUIDs in legacy matches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When matching a WMI device to a GUID used by the legacy GUID-based API, devices with a duplicated GUID should be ignored. Add an additional WMI device flag signaling that the GUID used by the WMI device is also used by another WMI device. Ignore such devices inside the match functions used by the legacy GUID-based API. Tested on a ASUS Prime B650-Plus. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240226193557.2888-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index abd0183c4107..29dfe52eb802 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -57,6 +57,7 @@ static_assert(__alignof__(struct guid_block) == 1); enum { /* wmi_block flags */ WMI_READ_TAKES_NO_ARGS, + WMI_GUID_DUPLICATED, WMI_NO_EVENT_DATA, }; @@ -196,6 +197,12 @@ static int wmidev_match_guid(struct device *dev, const void *data) struct wmi_block *wblock = dev_to_wblock(dev); const guid_t *guid = data; + /* Legacy GUID-based functions are restricted to only see + * a single WMI device for each GUID. + */ + if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags)) + return 0; + if (guid_equal(guid, &wblock->gblock.guid)) return 1; @@ -207,6 +214,12 @@ static int wmidev_match_notify_id(struct device *dev, const void *data) struct wmi_block *wblock = dev_to_wblock(dev); const u32 *notify_id = data; + /* Legacy GUID-based functions are restricted to only see + * a single WMI device for each GUID. + */ + if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags)) + return 0; + if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id) return 1; @@ -1036,10 +1049,12 @@ static int wmi_create_device(struct device *wmi_bus_dev, wblock->dev.dev.parent = wmi_bus_dev; count = guid_count(&wblock->gblock.guid); - if (count) + if (count) { dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); - else + set_bit(WMI_GUID_DUPLICATED, &wblock->flags); + } else { dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); + } device_initialize(&wblock->dev.dev); -- cgit v1.2.3 From a66ccfc2535418b536b1203b65f87c4f501f6bdd Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 26 Feb 2024 20:35:56 +0100 Subject: platform/x86: wmi: Do not instantiate older WMI drivers multiple times MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many older WMI drivers cannot be instantiated multiple times for two reasons: - they are using the legacy GUID-based WMI API - they are singletons (with global state) Prevent such WMI drivers from binding to WMI devices with a duplicated GUID, as this would mean that the WMI driver will be instantiated at least two times (one for the original GUID and one for the duplicated GUID). WMI drivers which can be instantiated multiple times can signal this by setting a flag inside struct wmi_driver. Tested on a ASUS Prime B650-Plus. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240226193557.2888-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/hwmon/dell-smm-hwmon.c | 1 + drivers/platform/x86/dell/dell-wmi-ddv.c | 1 + drivers/platform/x86/intel/wmi/sbl-fw-update.c | 1 + drivers/platform/x86/intel/wmi/thunderbolt.c | 1 + drivers/platform/x86/wmi-bmof.c | 1 + drivers/platform/x86/wmi.c | 12 ++++++++++++ include/linux/wmi.h | 2 ++ 7 files changed, 19 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 6d8c0f328b7b..168d669c4eca 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1587,6 +1587,7 @@ static struct wmi_driver dell_smm_wmi_driver = { }, .id_table = dell_smm_wmi_id_table, .probe = dell_smm_wmi_probe, + .no_singleton = true, }; /* diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index db1e9240dd02..0b2299f7a2de 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -882,6 +882,7 @@ static struct wmi_driver dell_wmi_ddv_driver = { }, .id_table = dell_wmi_ddv_id_table, .probe = dell_wmi_ddv_probe, + .no_singleton = true, }; module_wmi_driver(dell_wmi_ddv_driver); diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c index 040153ad67c1..75c82c08117f 100644 --- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c +++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -131,6 +131,7 @@ static struct wmi_driver intel_wmi_sbl_fw_update_driver = { .probe = intel_wmi_sbl_fw_update_probe, .remove = intel_wmi_sbl_fw_update_remove, .id_table = intel_wmi_sbl_id_table, + .no_singleton = true, }; module_wmi_driver(intel_wmi_sbl_fw_update_driver); diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c index e2ad3f46f356..08df560a2c7a 100644 --- a/drivers/platform/x86/intel/wmi/thunderbolt.c +++ b/drivers/platform/x86/intel/wmi/thunderbolt.c @@ -63,6 +63,7 @@ static struct wmi_driver intel_wmi_thunderbolt_driver = { .dev_groups = tbt_groups, }, .id_table = intel_wmi_thunderbolt_id_table, + .no_singleton = true, }; module_wmi_driver(intel_wmi_thunderbolt_driver); diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index 644d2fd889c0..df6f0ae6e6c7 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -94,6 +94,7 @@ static struct wmi_driver wmi_bmof_driver = { .probe = wmi_bmof_probe, .remove = wmi_bmof_remove, .id_table = wmi_bmof_id_table, + .no_singleton = true, }; module_wmi_driver(wmi_bmof_driver); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 29dfe52eb802..349deced87e8 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -883,6 +883,18 @@ static int wmi_dev_probe(struct device *dev) struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); int ret = 0; + /* Some older WMI drivers will break if instantiated multiple times, + * so they are blocked from probing WMI devices with a duplicated GUID. + * + * New WMI drivers should support being instantiated multiple times. + */ + if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags) && !wdriver->no_singleton) { + dev_warn(dev, "Legacy driver %s cannot be instantiated multiple times\n", + dev->driver->name); + + return -ENODEV; + } + if (wdriver->notify) { if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data) return -ENODEV; diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 781958310bfb..63cca3b58d6d 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -49,6 +49,7 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * @driver: Driver model structure * @id_table: List of WMI GUIDs supported by this driver * @no_notify_data: Driver supports WMI events which provide no event data + * @no_singleton: Driver can be instantiated multiple times * @probe: Callback for device binding * @remove: Callback for device unbinding * @notify: Callback for receiving WMI events @@ -59,6 +60,7 @@ struct wmi_driver { struct device_driver driver; const struct wmi_device_id *id_table; bool no_notify_data; + bool no_singleton; int (*probe)(struct wmi_device *wdev, const void *context); void (*remove)(struct wmi_device *wdev); -- cgit v1.2.3 From f86f09ad9ab234a90eef8bba6239b3ca82dc281e Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 26 Feb 2024 20:35:57 +0100 Subject: platform/x86: wmi: Remove obsolete duplicate GUID allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whitelist-based approach for preventing older WMI drivers from being instantiated multiple times has many drawbacks: - uses cannot see all available WMI devices (if not whitelisted) - whitelisting a WMI driver requires changes in the WMI driver core - maintenance burden for driver and subsystem developers Since the WMI driver core already takes care that older WMI drivers are not being instantiated multiple times, remove the now redundant whitelist. Tested on a ASUS Prime B650-Plus. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240226193557.2888-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 349deced87e8..1920e115da89 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -90,16 +90,6 @@ static const struct acpi_device_id wmi_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, wmi_device_ids); -/* allow duplicate GUIDs as these device drivers use struct wmi_driver */ -static const char * const allow_duplicates[] = { - "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ - "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ - "44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */ - "86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */ - "F1DDEE52-063C-4784-A11E-8A06684B9B01", /* dell-smm-hwmon */ - NULL -}; - #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) #define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) @@ -1093,32 +1083,6 @@ static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev) return device_add(&wdev->dev); } -static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid) -{ - struct wmi_block *wblock; - - list_for_each_entry(wblock, &wmi_block_list, list) { - /* skip warning and register if we know the driver will use struct wmi_driver */ - for (int i = 0; allow_duplicates[i] != NULL; i++) { - if (guid_parse_and_compare(allow_duplicates[i], guid)) - return false; - } - if (guid_equal(&wblock->gblock.guid, guid)) { - /* - * Because we historically didn't track the relationship - * between GUIDs and ACPI nodes, we don't know whether - * we need to suppress GUIDs that are unique on a - * given node but duplicated across nodes. - */ - dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", - guid, dev_name(&wblock->acpi_device->dev)); - return true; - } - } - - return false; -} - /* * Parse the _WDG method for the GUID data blocks */ @@ -1157,9 +1121,6 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) continue; } - if (guid_already_parsed_for_legacy(device, &gblock[i].guid)) - continue; - wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); if (!wblock) continue; -- cgit v1.2.3 From bb05226c840ce577d65d91b1fea51d6638c3c896 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 23 Feb 2024 17:38:59 +0100 Subject: platform/x86/amd/pmf: Add missing __iomem attribute to policy_base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value of policy_base is the return value of a devm_ioremap call, which returns a __iomem pointer instead of an regular pointer. Add the missing __iomem attribute. Compile-tested only. Signed-off-by: Armin Wolf Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240223163901.13504-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/pmf.h | 2 +- drivers/platform/x86/amd/pmf/tee-if.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 16999c5b334f..bcf777a5659a 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -229,7 +229,7 @@ struct amd_pmf_dev { struct delayed_work pb_work; struct pmf_action_table *prev_data; u64 policy_addr; - void *policy_base; + void __iomem *policy_base; bool smart_pc_enabled; }; diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index f8c0177afb0d..16973bebf55f 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -346,7 +346,7 @@ static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) if (!dev->policy_base) return -ENOMEM; - memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz); + memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz); amd_pmf_hex_dump_pb(dev); if (pb_side_load) -- cgit v1.2.3 From 2e2431dc64a4272365f4d065ca70a4d4b6ca098e Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 23 Feb 2024 17:39:01 +0100 Subject: platform/x86: intel_scu_ipcutil: Make scu static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable is only used internally and has no external users, so it should me made static. Compile-tested only. Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Armin Wolf Reviewed-by: Mika Westerberg Link: https://lore.kernel.org/r/20240223163901.13504-3-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel_scu_ipcutil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index b7c10c15a3d6..7d87cbd4b9c6 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -22,7 +22,7 @@ static int major; -struct intel_scu_ipc_dev *scu; +static struct intel_scu_ipc_dev *scu; static DEFINE_MUTEX(scu_lock); /* IOCTL commands */ -- cgit v1.2.3 From 20a36ec343d4c5abc2378a45ab5e7ea1ca85020a Mon Sep 17 00:00:00 2001 From: SungHwan Jung Date: Tue, 20 Feb 2024 14:52:31 +0900 Subject: platform/x86: acer-wmi: Add support for Acer PH16-71 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Acer Predator PH16-71 to Acer_quirks with predator_v4 to support mode button and fan speed sensor. Signed-off-by: SungHwan Jung Link: https://lore.kernel.org/r/20240220055231.6451-1-onenowy@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/acer-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 88b826e88ebd..771b0ce34c8f 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -584,6 +584,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = { }, .driver_data = &quirk_acer_predator_v4, }, + { + .callback = dmi_matched, + .ident = "Acer Predator PH16-71", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH16-71"), + }, + .driver_data = &quirk_acer_predator_v4, + }, { .callback = set_force_caps, .ident = "Acer Aspire Switch 10E SW3-016", -- cgit v1.2.3 From f9124f2a454a6f1edb4eae9f0646b1a61fd74dba Mon Sep 17 00:00:00 2001 From: SungHwan Jung Date: Tue, 20 Feb 2024 17:04:16 +0900 Subject: platform/x86: acer-wmi: Add predator_v4 module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This parameter allows predator laptop users to test and use features (mode button, platform profile, fan speed monitoring) without adding model names to acer_quirks and compiling kernel. Signed-off-by: SungHwan Jung Link: https://lore.kernel.org/r/20240220080416.6395-1-onenowy@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/acer-wmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 771b0ce34c8f..ee2e164f86b9 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -276,6 +276,7 @@ static bool has_type_aa; static u16 commun_func_bitmap; static u8 commun_fn_key_number; static bool cycle_gaming_thermal_profile = true; +static bool predator_v4; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -284,6 +285,7 @@ module_param(force_series, int, 0444); module_param(force_caps, int, 0444); module_param(ec_raw_mode, bool, 0444); module_param(cycle_gaming_thermal_profile, bool, 0644); +module_param(predator_v4, bool, 0444); MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); @@ -292,6 +294,8 @@ MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value"); MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); MODULE_PARM_DESC(cycle_gaming_thermal_profile, "Set thermal mode key in cycle mode. Disabling it sets the mode key in turbo toggle mode"); +MODULE_PARM_DESC(predator_v4, + "Enable features for predator laptops that use predator sense v4"); struct acer_data { int mailled; @@ -734,7 +738,9 @@ enum acer_predator_v4_thermal_profile_wmi { /* Find which quirks are needed for a particular vendor/ model pair */ static void __init find_quirks(void) { - if (!force_series) { + if (predator_v4) { + quirks = &quirk_acer_predator_v4; + } else if (!force_series) { dmi_check_system(acer_quirks); dmi_check_system(non_acer_quirks); } else if (force_series == 2490) { -- cgit v1.2.3 From 701d40af59373ac3a60c620cbd0ceff7b2b8e565 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Tue, 27 Feb 2024 11:01:32 -0800 Subject: platform/x86/intel/vsec: Remove nuisance message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_vsec_walk_header() is used to configure features from devices that don't provide a PCI VSEC or DVSEC structure. Some of these features may be unsupported and fail to load. Ignore them silently as we do for unsupported features described by VSEC/DVSEC. Signed-off-by: "David E. Box" Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240227190134.1592072-1-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 778eb0aa3479..0fdfaf3a4f5c 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -236,10 +236,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, for ( ; *header; header++) { ret = intel_vsec_add_dev(pdev, *header, info); - if (ret) - dev_info(&pdev->dev, "Could not add device for VSEC id %d\n", - (*header)->id); - else + if (!ret) have_devices = true; } -- cgit v1.2.3 From e6ba4acde44957dc9bdc3222b5739217a102752d Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Tue, 27 Feb 2024 11:01:33 -0800 Subject: platform/x86/intel/pmc/lnl: Remove SSRAM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent PMC firmware change in Lunar Lake caused the pmc_core driver to fail to probe. This is due to a change in the GUID for PMC telemetry coming from the SSRAM device. Until a final release is ready this value may change again. In the meantime, disable the SSRAM support for Lunar Lake so the driver can load and provide some basic functionality. Fixes: 3748dfdae2a6 ("platform/x86/intel/pmc: Add Lunar Lake M support to intel_pmc_core driver") Signed-off-by: "David E. Box" Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240227190134.1592072-2-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/lnl.c | 40 +++++------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index abad17cdd3d7..068d72504683 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -13,21 +13,6 @@ #include "core.h" -#define SOCM_LPM_REQ_GUID 0x11594920 - -#define PMC_DEVID_SOCM 0xa87f - -static const u8 LNL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; - -static struct pmc_info lnl_pmc_info_list[] = { - { - .guid = SOCM_LPM_REQ_GUID, - .devid = PMC_DEVID_SOCM, - .map = &lnl_socm_reg_map, - }, - {} -}; - const struct pmc_bit_map lnl_ltr_show_map[] = { {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, @@ -490,7 +475,6 @@ const struct pmc_reg_map lnl_socm_reg_map = { .lpm_sts = lnl_lpm_maps, .lpm_status_offset = MTL_LPM_STATUS_OFFSET, .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, - .lpm_reg_index = LNL_LPM_REG_INDEX, }; #define LNL_NPU_PCI_DEV 0x643e @@ -517,33 +501,19 @@ static int lnl_resume(struct pmc_dev *pmcdev) int lnl_core_init(struct pmc_dev *pmcdev) { int ret; - int func = 2; - bool ssram_init = true; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; lnl_d3_fixup(); pmcdev->suspend = cnl_suspend; pmcdev->resume = lnl_resume; - pmcdev->regmap_list = lnl_pmc_info_list; - ret = pmc_core_ssram_init(pmcdev, func); - - /* If regbase not assigned, set map and discover using legacy method */ - if (ret) { - ssram_init = false; - pmc->map = &lnl_socm_reg_map; - ret = get_primary_reg_base(pmc); - if (ret) - return ret; - } - pmc_core_get_low_power_modes(pmcdev); + pmc->map = &lnl_socm_reg_map; + ret = get_primary_reg_base(pmc); + if (ret) + return ret; - if (ssram_init) { - ret = pmc_core_ssram_get_lpm_reqs(pmcdev); - if (ret) - return ret; - } + pmc_core_get_low_power_modes(pmcdev); return 0; } -- cgit v1.2.3 From ac2d1fd9688fcdfba5acc815fb2b13fec83e5dad Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Tue, 27 Feb 2024 11:01:34 -0800 Subject: platform/x86/intel/pmc/arl: Put GNA device in D3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As is the case on Meteor Lake, the Gaussian & Neural Accelerator (GNA) device is powered by BIOS to D0 by default. If no driver is loaded, this will cause the Package C state to be limited to PC2, leading to significant power consumption and decrease in batter life. Put the GNA device in D3 by default if no driver is loaded for it. Fixes: 83f168a1a437 ("platform/x86/intel/pmc: Add Arrow Lake S support to intel_pmc_core driver") Signed-off-by: "David E. Box" Reviewed-by: Ilpo Järvinen Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240227190134.1592072-3-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 683ae828276b..34b4cd23bfe5 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -673,6 +673,7 @@ static struct pmc_info arl_pmc_info_list[] = { }; #define ARL_NPU_PCI_DEV 0xad1d +#define ARL_GNA_PCI_DEV 0xae4c /* * Set power state of select devices that do not have drivers to D3 * so that they do not block Package C entry. @@ -680,6 +681,7 @@ static struct pmc_info arl_pmc_info_list[] = { static void arl_d3_fixup(void) { pmc_core_set_device_d3(ARL_NPU_PCI_DEV); + pmc_core_set_device_d3(ARL_GNA_PCI_DEV); } static int arl_resume(struct pmc_dev *pmcdev) -- cgit v1.2.3 From 1266e2efb7512dbf20eac820ca2ed34de6b1c3e7 Mon Sep 17 00:00:00 2001 From: Alban Boyé Date: Tue, 27 Feb 2024 22:40:17 +0000 Subject: platform/x86: touchscreen_dmi: Add an extra entry for a variant of the Chuwi Vi8 tablet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alban Boyé Link: https://lore.kernel.org/r/20240227223919.11587-1-alban.boye@protonmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/touchscreen_dmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 7aee5e9ff2b8..1eb9d4cf5487 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -1222,6 +1222,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"), }, }, + { + /* Chuwi Vi8 dual-boot (CWI506) */ + .driver_data = (void *)&chuwi_vi8_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "i86"), + DMI_MATCH(DMI_BIOS_VERSION, "CHUWI2.D86JHBNR02"), + }, + }, { /* Chuwi Vi8 Plus (CWI519) */ .driver_data = (void *)&chuwi_vi8_plus_data, -- cgit v1.2.3 From a6dcd3f1725e048de521e9d9421b0751ba0aa379 Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Thu, 29 Feb 2024 00:01:49 +0900 Subject: platform/x86: thinkpad_acpi: Add more ThinkPads with non-standard reg address for fan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add more ThinkPads with non-standard register addresses to read fan values. ThinkPads added are L13 Yoga Gen1, X13 Yoga Gen1, L380, L390, 11e Gen5 GL, 11e Gen5 GL-R, 11e Gen5 KL-Y. Signed-off-by: Vishnu Sankar Reviewed-by: Mark Pearson Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240228150149.4799-1-vishnuocv@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index df4ee025516c..ba040591c877 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8832,6 +8832,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL), /* T15g (2nd gen) */ TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS), /* L13 Yoga Gen 2 */ TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS), /* X13 Yoga Gen 2*/ + TPACPI_Q_LNV3('R', '0', 'R', TPACPI_FAN_NS), /* L380 */ + TPACPI_Q_LNV3('R', '1', '5', TPACPI_FAN_NS), /* L13 Yoga Gen 1 */ + TPACPI_Q_LNV3('R', '1', '0', TPACPI_FAN_NS), /* L390 */ + TPACPI_Q_LNV3('N', '2', 'L', TPACPI_FAN_NS), /* X13 Yoga Gen 1 */ + TPACPI_Q_LNV3('R', '0', 'T', TPACPI_FAN_NS), /* 11e Gen5 GL */ + TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS), /* 11e Gen5 GL-R */ + TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */ TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */ }; -- cgit v1.2.3 From e8b4223dbf12cb6b722e1b8c48a9386cb096d4fb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 28 Feb 2024 16:26:59 -0800 Subject: platform/x86: ISST: Allow reading core-power state on HWP disabled systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When HWP (Hardware P-states) is disabled, dynamic SST features are disabled. But user should still be able to read the current core-power state, with legacy P-states. This will allow users to read current configuration with static SST enabled from BIOS. To address this, do not call disable_dynamic_sst_features() when the request is for reading the state. Signed-off-by: Srinivas Pandruvada Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240229002659.1416623-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 2662fbbddf0c..1d918000d72b 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -462,10 +462,10 @@ static long isst_if_core_power_state(void __user *argp) struct tpmi_per_power_domain_info *power_domain_info; struct isst_core_power core_power; - if (disable_dynamic_sst_features()) + if (copy_from_user(&core_power, argp, sizeof(core_power))) return -EFAULT; - if (copy_from_user(&core_power, argp, sizeof(core_power))) + if (core_power.get_set && disable_dynamic_sst_features()) return -EFAULT; power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id); -- cgit v1.2.3 From 7c8772fef2c25b951660ff31aa1d2174b45af043 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 29 Feb 2024 16:11:36 +0300 Subject: platform/mellanox: mlxbf-pmc: fix signedness bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These need to be signed for the error handling to work. The mlxbf_pmc_get_event_num() function returns int so int type is correct. Fixes: 1ae9ffd303c2 ("platform/mellanox: mlxbf-pmc: Cleanup signed/unsigned mix-up") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/a4af764e-990b-4ebd-b342-852844374032@moroto.mountain Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-pmc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 746567767e5b..4ed9c7fd2b62 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1505,8 +1505,9 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, { struct mlxbf_pmc_attribute *attr_counter = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - unsigned int blk_num, cnt_num, offset; + unsigned int blk_num, cnt_num; bool is_l3 = false; + int offset; u64 value; blk_num = attr_counter->nr; @@ -1539,9 +1540,10 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev, { struct mlxbf_pmc_attribute *attr_counter = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - unsigned int blk_num, cnt_num, offset, data; + unsigned int blk_num, cnt_num, data; bool is_l3 = false; u64 evt_num; + int offset; int err; blk_num = attr_counter->nr; @@ -1621,8 +1623,9 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev, { struct mlxbf_pmc_attribute *attr_event = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - unsigned int blk_num, cnt_num, evt_num; + unsigned int blk_num, cnt_num; bool is_l3 = false; + int evt_num; int err; blk_num = attr_event->nr; -- cgit v1.2.3 From d1e33cd66670cd9c07258398ffe93961d2089287 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 4 Mar 2024 21:50:02 +0100 Subject: platform/x86/amd/pmf: Fix return value of amd_pmf_start_policy_engine() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amd_pmf_start_policy_engine() returns an negative error code upon failure, so the TA_PMF_* error codes cannot be used here. Return -EIO instead. Also stop shadowing the return code in amd_pmf_get_pb_data(). Compile-tested only. Suggested-by: Ilpo Järvinen Fixes: 7c45534afa44 ("platform/x86/amd/pmf: Add support for PMF Policy Binary") Signed-off-by: Armin Wolf Reviewed-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240304205005.10078-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/tee-if.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 16973bebf55f..13dd4462e1e3 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -269,7 +269,7 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) } else { dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res); dev->smart_pc_enabled = PMF_SMART_PC_DISABLED; - return res; + return -EIO; } return 0; @@ -309,8 +309,8 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, amd_pmf_hex_dump_pb(dev); ret = amd_pmf_start_policy_engine(dev); - if (ret) - return -EINVAL; + if (ret < 0) + return ret; return length; } -- cgit v1.2.3 From e42dddce83a0578a518cb7078930c8269a6083af Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 4 Mar 2024 21:50:03 +0100 Subject: platform/x86/amd/pmf: Do not use readl() for policy buffer access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The policy buffer is allocated using normal memory allocation functions, so readl() should not be used on it. Compile-tested only. Fixes: 7c45534afa44 ("platform/x86/amd/pmf: Add support for PMF Policy Binary") Signed-off-by: Armin Wolf Reviewed-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240304205005.10078-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/tee-if.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 13dd4462e1e3..58ec2c9606e1 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -249,8 +249,8 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) u32 cookie, length; int res; - cookie = readl(dev->policy_buf + POLICY_COOKIE_OFFSET); - length = readl(dev->policy_buf + POLICY_COOKIE_LEN); + cookie = *(u32 *)(dev->policy_buf + POLICY_COOKIE_OFFSET); + length = *(u32 *)(dev->policy_buf + POLICY_COOKIE_LEN); if (cookie != POLICY_SIGN_COOKIE || !length) return -EINVAL; -- cgit v1.2.3 From 9ced197640a837ed4a7505e1be8be3ce541efffa Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 4 Mar 2024 21:50:04 +0100 Subject: platform/x86/amd/pmf: Use struct for cookie header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cookie header consists of a sign field and a length field. Combine both in a single struct to make accesses simpler. Compile-tested only. Suggested-by: Ilpo Järvinen Signed-off-by: Armin Wolf Reviewed-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240304205005.10078-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/pmf.h | 6 +++++- drivers/platform/x86/amd/pmf/tee-if.c | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index bcf777a5659a..0c90805dc85b 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -17,7 +17,11 @@ #define POLICY_BUF_MAX_SZ 0x4b000 #define POLICY_SIGN_COOKIE 0x31535024 #define POLICY_COOKIE_OFFSET 0x10 -#define POLICY_COOKIE_LEN 0x14 + +struct cookie_header { + u32 sign; + u32 length; +} __packed; /* APMF Functions */ #define APMF_FUNC_VERIFY_INTERFACE 0 diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 58ec2c9606e1..71ea7eefc211 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -246,17 +246,16 @@ static void amd_pmf_invoke_cmd(struct work_struct *work) static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) { - u32 cookie, length; + struct cookie_header *header; int res; - cookie = *(u32 *)(dev->policy_buf + POLICY_COOKIE_OFFSET); - length = *(u32 *)(dev->policy_buf + POLICY_COOKIE_LEN); + header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET); - if (cookie != POLICY_SIGN_COOKIE || !length) + if (header->sign != POLICY_SIGN_COOKIE || !header->length) return -EINVAL; /* Update the actual length */ - dev->policy_sz = length + 512; + dev->policy_sz = header->length + 512; res = amd_pmf_invoke_cmd_init(dev); if (res == TA_PMF_TYPE_SUCCESS) { /* Now its safe to announce that smart pc is enabled */ -- cgit v1.2.3 From 8c9be42172e2a18f39c41dde3ce3e4cddaf6cf75 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 4 Mar 2024 21:50:05 +0100 Subject: platform/x86/amd/pmf: Fix possible out-of-bound memory accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The length of the policy buffer is not validated before accessing it, which means that multiple out-of-bounds memory accesses can occur. This is especially bad since userspace can load policy binaries over debugfs. Compile-tested only. Fixes: 7c45534afa44 ("platform/x86/amd/pmf: Add support for PMF Policy Binary") Signed-off-by: Armin Wolf Reviewed-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240304205005.10078-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/tee-if.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 71ea7eefc211..75370431a82e 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -249,11 +249,17 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) struct cookie_header *header; int res; + if (dev->policy_sz < POLICY_COOKIE_OFFSET + sizeof(*header)) + return -EINVAL; + header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET); if (header->sign != POLICY_SIGN_COOKIE || !header->length) return -EINVAL; + if (dev->policy_sz < header->length + 512) + return -EINVAL; + /* Update the actual length */ dev->policy_sz = header->length + 512; res = amd_pmf_invoke_cmd_init(dev); -- cgit v1.2.3 From 988f3bf3d9504225e1e662a87a70700c1e9708c7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Mar 2024 18:14:58 +0200 Subject: platform/x86: intel_scu_wdt: Remove unused intel-mid.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel-mid.h is providing some core parts of the South Complex PM, which are usually not used by individual drivers. In particular, this driver doesn't use it, so simply remove the unused header. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240305161539.1364717-2-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel_scu_wdt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/intel_scu_wdt.c b/drivers/platform/x86/intel_scu_wdt.c index c2479777a1d6..a5031a25632e 100644 --- a/drivers/platform/x86/intel_scu_wdt.c +++ b/drivers/platform/x86/intel_scu_wdt.c @@ -13,7 +13,6 @@ #include #include -#include #include #include -- cgit v1.2.3 From 51384654f350ec953202e79588b14ef0ec0a8d4e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Mar 2024 18:14:59 +0200 Subject: platform/x86: intel_scu_pcidrv: Remove unused intel-mid.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel-mid.h is providing some core parts of the South Complex PM, which are usually not used by individual drivers. In particular, this driver doesn't use it, so simply remove the unused header. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240305161539.1364717-3-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel_scu_pcidrv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c index d904fad499aa..dbf0310448da 100644 --- a/drivers/platform/x86/intel_scu_pcidrv.c +++ b/drivers/platform/x86/intel_scu_pcidrv.c @@ -11,7 +11,6 @@ #include #include -#include #include static int intel_scu_pci_probe(struct pci_dev *pdev, -- cgit v1.2.3 From 57221a07ff37ff356f9265acd228bc3c8744c8fc Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 5 Mar 2024 11:46:44 -0800 Subject: platform/x86/intel/tpmi: Change vsec offset to u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsec offset can be 64 bit long depending on the PFS start. So change type to u64. Also use 64 bit formatting for seq_printf. Fixes: 47731fd2865f ("platform/x86/intel: Intel TPMI enumeration driver") Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org # v6.3+ Link: https://lore.kernel.org/r/20240305194644.2077867-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/tpmi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index e73cdea67fff..910df7c654f4 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -96,7 +96,7 @@ struct intel_tpmi_pfs_entry { */ struct intel_tpmi_pm_feature { struct intel_tpmi_pfs_entry pfs_header; - unsigned int vsec_offset; + u64 vsec_offset; struct intel_vsec_device *vsec_dev; }; @@ -376,7 +376,7 @@ static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) read_blocked = feature_state.read_blocked ? 'Y' : 'N'; write_blocked = feature_state.write_blocked ? 'Y' : 'N'; } - seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\t\t%c\t\t%c\n", + seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%016llx\t%c\t%c\t\t%c\t\t%c\n", pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries, pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset, pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled, @@ -395,7 +395,8 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused) struct intel_tpmi_pm_feature *pfs = s->private; int count, ret = 0; void __iomem *mem; - u32 off, size; + u32 size; + u64 off; u8 *buffer; size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); @@ -411,7 +412,7 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused) mutex_lock(&tpmi_dev_lock); for (count = 0; count < pfs->pfs_header.num_entries; ++count) { - seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off); + seq_printf(s, "TPMI Instance:%d offset:0x%llx\n", count, off); mem = ioremap(off, size); if (!mem) { -- cgit v1.2.3 From 5878e5b760b6fcf7bc00dec085ba2b439a929871 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 5 Mar 2024 15:55:04 -0300 Subject: platform/x86: make fw_attr_class constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 43a7206b0963 ("driver core: class: make class_register() take a const *"), the driver core allows for struct class to be in read-only memory, so move the fw_attr_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at boot time. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Link: https://lore.kernel.org/r/20240305-class_cleanup-platform-v1-1-9085c97b9355@marliere.net Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 2 +- drivers/platform/x86/firmware_attributes_class.c | 4 ++-- drivers/platform/x86/firmware_attributes_class.h | 2 +- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 2 +- drivers/platform/x86/think-lmi.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index b929b4f82420..9def7983d7d6 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -25,7 +25,7 @@ struct wmi_sysman_priv wmi_priv = { /* reset bios to defaults */ static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; static int reset_option = -1; -static struct class *fw_attr_class; +static const struct class *fw_attr_class; /** diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c index fafe8eaf6e3e..dd8240009565 100644 --- a/drivers/platform/x86/firmware_attributes_class.c +++ b/drivers/platform/x86/firmware_attributes_class.c @@ -10,11 +10,11 @@ static DEFINE_MUTEX(fw_attr_lock); static int fw_attr_inuse; -static struct class firmware_attributes_class = { +static const struct class firmware_attributes_class = { .name = "firmware-attributes", }; -int fw_attributes_class_get(struct class **fw_attr_class) +int fw_attributes_class_get(const struct class **fw_attr_class) { int err; diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h index 486485cb1f54..363c75f1ac1b 100644 --- a/drivers/platform/x86/firmware_attributes_class.h +++ b/drivers/platform/x86/firmware_attributes_class.h @@ -5,7 +5,7 @@ #ifndef FW_ATTR_CLASS_H #define FW_ATTR_CLASS_H -int fw_attributes_class_get(struct class **fw_attr_class); +int fw_attributes_class_get(const struct class **fw_attr_class); int fw_attributes_class_put(void); #endif /* FW_ATTR_CLASS_H */ diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 8c9f4f3227fc..2dc50152158a 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -24,7 +24,7 @@ struct bioscfg_priv bioscfg_drv = { .mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex), }; -static struct class *fw_attr_class; +static const struct class *fw_attr_class; ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr, diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 3a396b763c49..9eeef356e308 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -195,7 +195,7 @@ static const char * const level_options[] = { [TLMI_LEVEL_MASTER] = "master", }; static struct think_lmi tlmi_priv; -static struct class *fw_attr_class; +static const struct class *fw_attr_class; static DEFINE_MUTEX(tlmi_mutex); /* Convert BIOS WMI error string to suitable error code */ -- cgit v1.2.3 From dbab9afe8640a51ffcce87bfdb59a814e0dc7780 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Mar 2024 11:59:11 +0100 Subject: clk: x86: Move clk-pmc-atom register defines to include/linux/platform_data/x86/pmc_atom.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the register defines for the Atom (Bay Trail, Cherry Trail) PMC clocks to include/linux/platform_data/x86/pmc_atom.h. This is a preparation patch to extend the S0i3 readiness checks in drivers/platform/x86/pmc_atom.c with checking that the PMC clocks are off on suspend entry. Note these are added to include/linux/platform_data/x86/pmc_atom.h rather then to include/linux/platform_data/x86/clk-pmc-atom.h because the former already has all the other Atom PMC register defines. Reviewed-by: Ilpo Järvinen Acked-by: Stephen Boyd Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305105915.76242-2-hdegoede@redhat.com Signed-off-by: Ilpo Järvinen --- drivers/clk/x86/clk-pmc-atom.c | 13 +------------ include/linux/platform_data/x86/pmc_atom.h | 13 +++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c index 2974dd0ec6f4..5ec9255e33fa 100644 --- a/drivers/clk/x86/clk-pmc-atom.c +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -11,23 +11,12 @@ #include #include #include +#include #include #include #define PLT_CLK_NAME_BASE "pmc_plt_clk" -#define PMC_CLK_CTL_OFFSET 0x60 -#define PMC_CLK_CTL_SIZE 4 -#define PMC_CLK_NUM 6 -#define PMC_CLK_CTL_GATED_ON_D3 0x0 -#define PMC_CLK_CTL_FORCE_ON 0x1 -#define PMC_CLK_CTL_FORCE_OFF 0x2 -#define PMC_CLK_CTL_RESERVED 0x3 -#define PMC_MASK_CLK_CTL GENMASK(1, 0) -#define PMC_MASK_CLK_FREQ BIT(2) -#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ -#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ - struct clk_plt_fixed { struct clk_hw *clk; struct clk_lookup *lookup; diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h index b8a701c77fd0..557622ef0390 100644 --- a/include/linux/platform_data/x86/pmc_atom.h +++ b/include/linux/platform_data/x86/pmc_atom.h @@ -43,6 +43,19 @@ BIT_ORED_DEDICATED_IRQ_GPSC | \ BIT_SHARED_IRQ_GPSS) +/* External clk generator settings */ +#define PMC_CLK_CTL_OFFSET 0x60 +#define PMC_CLK_CTL_SIZE 4 +#define PMC_CLK_NUM 6 +#define PMC_CLK_CTL_GATED_ON_D3 0x0 +#define PMC_CLK_CTL_FORCE_ON 0x1 +#define PMC_CLK_CTL_FORCE_OFF 0x2 +#define PMC_CLK_CTL_RESERVED 0x3 +#define PMC_MASK_CLK_CTL GENMASK(1, 0) +#define PMC_MASK_CLK_FREQ BIT(2) +#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ +#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ + /* The timers accumulate time spent in sleep state */ #define PMC_S0IR_TMR 0x80 #define PMC_S0I1_TMR 0x84 -- cgit v1.2.3 From a21ff5a0a7948b6ef1364f8f6d07eda49426d09a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Mar 2024 11:59:12 +0100 Subject: platform/x86: pmc_atom: Annotate d3_sts register bit defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The include/linux/platform_data/x86/pmc_atom.h d3_sts register bit defines are named after how these bits are used on Bay Trail devices. On Cherry Trail (CHT) devices some of these bits have a different meaning according to the datasheet. At a comment to the defines for bits which have a different meaning on Cherry Trail devices. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305105915.76242-3-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/platform_data/x86/pmc_atom.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h index 557622ef0390..161e4bc1c9ee 100644 --- a/include/linux/platform_data/x86/pmc_atom.h +++ b/include/linux/platform_data/x86/pmc_atom.h @@ -117,14 +117,14 @@ #define BIT_SCC_SDIO BIT(9) #define BIT_SCC_SDCARD BIT(10) #define BIT_SCC_MIPI BIT(11) -#define BIT_HDA BIT(12) +#define BIT_HDA BIT(12) /* CHT datasheet: reserved */ #define BIT_LPE BIT(13) #define BIT_OTG BIT(14) -#define BIT_USH BIT(15) -#define BIT_GBE BIT(16) -#define BIT_SATA BIT(17) -#define BIT_USB_EHCI BIT(18) -#define BIT_SEC BIT(19) +#define BIT_USH BIT(15) /* CHT datasheet: reserved */ +#define BIT_GBE BIT(16) /* CHT datasheet: reserved */ +#define BIT_SATA BIT(17) /* CHT datasheet: reserved */ +#define BIT_USB_EHCI BIT(18) /* CHT datasheet: XHCI! */ +#define BIT_SEC BIT(19) /* BYT datasheet: reserved */ #define BIT_PCIE_PORT0 BIT(20) #define BIT_PCIE_PORT1 BIT(21) #define BIT_PCIE_PORT2 BIT(22) -- cgit v1.2.3 From 1bde4afcd190e74368438ddf98d50e637ff9303f Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Tue, 5 Mar 2024 11:59:13 +0100 Subject: platform/x86: pmc_atom: Check state of PMC managed devices on s2idle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the Bay Trail or Cherry Trail SoC to enter the S0i3 power-level at s2idle suspend requires most of the hw-blocks / devices in the SoC to be in D3 when entering s2idle suspend. If some devices are not in D3 then the SoC will stay in a higher power state, consuming much more power from the battery then in S0i3. Use the new acpi_s2idle_dev_ops and acpi_register_lps0_dev() functionality to register a new s2idle check function which checks that all hardware blocks in the South complex (controlled by the PMC) are in a state that allows the SoC to enter S0i3 and prints an error message for any device in D0. Some blocks are not used on lower-featured versions of the SoC and these blocks will always report being in D0 on SoCs were they are not used. A false-positive mask is used to identify these blocks and for blocks in this mask the error is turned into a debug message to avoid false-positive error messages. Note the pmc_atom code is enabled by CONFIG_X86_INTEL_LPSS which already depends on ACPI. Signed-off-by: Johannes Stezenbach Signed-off-by: Takashi Iwai Reviewed-by: Ilpo Järvinen [hdegoede: Use acpi_s2idle_dev_ops, ignore fused off blocks, PMIC I2C] Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305105915.76242-4-hdegoede@redhat.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pmc_atom.c | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index 93a6414c6611..7d391bd36eae 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include struct pmc_bit_map { const char *name; @@ -448,6 +450,71 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap, return 0; } +#ifdef CONFIG_SUSPEND +static void pmc_dev_state_check(u32 sts, const struct pmc_bit_map *sts_map, + u32 fd, const struct pmc_bit_map *fd_map, + u32 sts_possible_false_pos) +{ + int index; + + for (index = 0; sts_map[index].name; index++) { + if (!(fd_map[index].bit_mask & fd) && + !(sts_map[index].bit_mask & sts)) { + if (sts_map[index].bit_mask & sts_possible_false_pos) + pm_pr_dbg("%s is in D0 prior to s2idle\n", + sts_map[index].name); + else + pr_err("%s is in D0 prior to s2idle\n", + sts_map[index].name); + } + } +} + +static void pmc_s2idle_check(void) +{ + struct pmc_dev *pmc = &pmc_device; + const struct pmc_reg_map *m = pmc->map; + u32 func_dis, func_dis_2; + u32 d3_sts_0, d3_sts_1; + u32 false_pos_sts_0, false_pos_sts_1; + + func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS); + func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2); + d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0); + d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1); + + /* + * Some blocks are not used on lower-featured versions of the SoC and + * always report D0, add these to false_pos mask to log at debug level. + */ + if (m->d3_sts_1 == byt_d3_sts_1_map) { + /* Bay Trail */ + false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_PCIE_PORT0 | + BIT_PCIE_PORT1 | BIT_PCIE_PORT2 | BIT_PCIE_PORT3 | + BIT_LPSS2_F5_I2C5; + false_pos_sts_1 = BIT_SMB | BIT_USH_SS_PHY | BIT_DFX; + } else { + /* Cherry Trail */ + false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_LPSS2_F7_I2C7; + false_pos_sts_1 = BIT_SMB | BIT_STS_ISH; + } + + pmc_dev_state_check(d3_sts_0, m->d3_sts_0, func_dis, m->func_dis, false_pos_sts_0); + pmc_dev_state_check(d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2, false_pos_sts_1); +} + +static struct acpi_s2idle_dev_ops pmc_s2idle_ops = { + .check = pmc_s2idle_check, +}; + +static void pmc_s2idle_check_register(void) +{ + acpi_register_lps0_dev(&pmc_s2idle_ops); +} +#else +static void pmc_s2idle_check_register(void) {} +#endif + static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pmc_dev *pmc = &pmc_device; @@ -485,6 +552,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) dev_warn(&pdev->dev, "platform clocks register failed: %d\n", ret); + pmc_s2idle_check_register(); pmc->init = true; return ret; } -- cgit v1.2.3 From 86cef4593e04b192d62b71f7bcf97fced916150b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Mar 2024 11:59:14 +0100 Subject: platform/x86: pmc_atom: Check state of PMC clocks on s2idle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the s2idle check with checking that none of the PMC clocks is in the forced-on state. If one of the clocks is in forced on state then S0i3 cannot be reached. Reviewed-by: Ilpo Järvinen Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305105915.76242-5-hdegoede@redhat.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pmc_atom.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index 7d391bd36eae..0aa7076bc9cc 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -477,6 +477,7 @@ static void pmc_s2idle_check(void) u32 func_dis, func_dis_2; u32 d3_sts_0, d3_sts_1; u32 false_pos_sts_0, false_pos_sts_1; + int i; func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS); func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2); @@ -501,6 +502,16 @@ static void pmc_s2idle_check(void) pmc_dev_state_check(d3_sts_0, m->d3_sts_0, func_dis, m->func_dis, false_pos_sts_0); pmc_dev_state_check(d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2, false_pos_sts_1); + + /* Forced-on PMC clocks prevent S0i3 */ + for (i = 0; i < PMC_CLK_NUM; i++) { + u32 ctl = pmc_reg_read(pmc, PMC_CLK_CTL_OFFSET + 4 * i); + + if ((ctl & PMC_MASK_CLK_CTL) != PMC_CLK_CTL_FORCE_ON) + continue; + + pr_err("clock %d is ON prior to freeze (ctl 0x%08x)\n", i, ctl); + } } static struct acpi_s2idle_dev_ops pmc_s2idle_ops = { -- cgit v1.2.3 From f62f012f998ad2e43a93fdff2720a4c746025ee9 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Tue, 5 Mar 2024 11:59:15 +0100 Subject: x86/platform/atom: Check state of Punit managed devices on s2idle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the Bay Trail or Cherry Trail SoC to enter the S0i3 power-level at s2idle suspend requires most of the hw-blocks / devices in the SoC to be in D3 when entering s2idle suspend. If some devices are not in D3 then the SoC will stay in a higher power state, consuming much more power from the battery then in S0i3. Use the new acpi_s2idle_dev_ops and acpi_register_lps0_dev() functionality to register a new s2idle check function which checks that all hardware blocks in the North complex (controlled by Punit) are in a state that allows the SoC to enter S0i3 and prints an error message for any device in D0. Signed-off-by: Johannes Stezenbach Signed-off-by: Takashi Iwai Acked-by: "Borislav Petkov (AMD)" Reviewed-by: Ilpo Järvinen [hdegoede: Use acpi_s2idle_dev_ops] Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305105915.76242-6-hdegoede@redhat.com Signed-off-by: Ilpo Järvinen --- arch/x86/platform/atom/punit_atom_debug.c | 54 ++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/arch/x86/platform/atom/punit_atom_debug.c b/arch/x86/platform/atom/punit_atom_debug.c index f8ed5f66cd20..6b9c6deca8ba 100644 --- a/arch/x86/platform/atom/punit_atom_debug.c +++ b/arch/x86/platform/atom/punit_atom_debug.c @@ -7,6 +7,9 @@ * Copyright (c) 2015, Intel Corporation. */ +#define pr_fmt(fmt) "punit_atom: " fmt + +#include #include #include #include @@ -117,6 +120,51 @@ static void punit_dbgfs_unregister(void) debugfs_remove_recursive(punit_dbg_file); } +#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND) +static const struct punit_device *punit_dev; + +static void punit_s2idle_check(void) +{ + const struct punit_device *punit_devp; + u32 punit_pwr_status, dstate; + int status; + + for (punit_devp = punit_dev; punit_devp->name; punit_devp++) { + /* Skip MIO, it is on till the very last moment */ + if (punit_devp->reg == MIO_SS_PM) + continue; + + status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, + punit_devp->reg, &punit_pwr_status); + if (status) { + pr_err("%s read failed\n", punit_devp->name); + } else { + dstate = (punit_pwr_status >> punit_devp->sss_pos) & 3; + if (!dstate) + pr_err("%s is in D0 prior to s2idle\n", punit_devp->name); + } + } +} + +static struct acpi_s2idle_dev_ops punit_s2idle_ops = { + .check = punit_s2idle_check, +}; + +static void punit_s2idle_check_register(struct punit_device *punit_device) +{ + punit_dev = punit_device; + acpi_register_lps0_dev(&punit_s2idle_ops); +} + +static void punit_s2idle_check_unregister(void) +{ + acpi_unregister_lps0_dev(&punit_s2idle_ops); +} +#else +static void punit_s2idle_check_register(struct punit_device *punit_device) {} +static void punit_s2idle_check_unregister(void) {} +#endif + #define X86_MATCH(model, data) \ X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \ X86_FEATURE_MWAIT, data) @@ -131,19 +179,23 @@ MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids); static int __init punit_atom_debug_init(void) { + struct punit_device *punit_device; const struct x86_cpu_id *id; id = x86_match_cpu(intel_punit_cpu_ids); if (!id) return -ENODEV; - punit_dbgfs_register((struct punit_device *)id->driver_data); + punit_device = (struct punit_device *)id->driver_data; + punit_dbgfs_register(punit_device); + punit_s2idle_check_register(punit_device); return 0; } static void __exit punit_atom_debug_exit(void) { + punit_s2idle_check_unregister(); punit_dbgfs_unregister(); } -- cgit v1.2.3 From a33e9e106601b5271561e7d79a3d3c4e7e84f07f Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:09 +0530 Subject: platform/x86/amd/pmf: Differentiate PMF ACPI versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For family 1AH, certain PMF features have been enhanced - leading to a newer APMF (AMD PMF) spec (BIOS and PMF driver interface) called v2. This information would be fed into the if_version field of the verify_interface method of the APMF call from the BIOS. Use this information to store the version number to differentiate between v1 or v2 and also store the information into the PMF private data structure, as this information would be required for further code branching to support the latest silicon. Reviewed-by: Kuppuswamy Sathyanarayanan Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-2-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/acpi.c | 6 ++++-- drivers/platform/x86/amd/pmf/pmf.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index f2eb07ef855a..1f287a147c57 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -218,8 +218,10 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev) return err; pdev->supported_func = output.supported_functions; - dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n", - output.supported_functions, output.notification_mask); + dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x version:%u\n", + output.supported_functions, output.notification_mask, output.version); + + pdev->pmf_if_version = output.version; return 0; } diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 0c90805dc85b..2f23ced7da66 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -235,6 +235,7 @@ struct amd_pmf_dev { u64 policy_addr; void __iomem *policy_base; bool smart_pc_enabled; + u16 pmf_if_version; }; struct apmf_sps_prop_granular { -- cgit v1.2.3 From 233f78e11e1fa0387d1600875bef02a2d714f2e2 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:10 +0530 Subject: platform/x86/amd/pmf: Disable debugfs support for querying power thermals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GET interface to receive the active power thermal information from the PMFW has been deprecated. Hence drop the debugfs support from version2 onwards. Reviewed-by: Kuppuswamy Sathyanarayanan Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-3-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/core.c | 5 +++-- drivers/platform/x86/amd/pmf/pmf.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index feaa09f5b35a..a6bdde4f1c68 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -113,8 +113,9 @@ static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev) static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev) { dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL); - debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev, - ¤t_power_limits_fops); + if (dev->pmf_if_version == PMF_IF_V1) + debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev, + ¤t_power_limits_fops); } int amd_pmf_get_power_source(void) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 2f23ced7da66..51e31d008ef9 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -87,6 +87,8 @@ struct cookie_header { #define TA_OUTPUT_RESERVED_MEM 906 #define MAX_OPERATION_PARAMS 4 +#define PMF_IF_V1 1 + /* AMD PMF BIOS interfaces */ struct apmf_verify_interface { u16 size; -- cgit v1.2.3 From 5fdc8b82aab4c8430bbd8d26a3c3898aaff90a40 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:11 +0530 Subject: platform/x86/amd/pmf: Add support to get sbios requests in PMF driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the APMF function index 2 for family 1Ah, that gets the information of SBIOS requests (like the pending requests from BIOS, custom notifications, updation of power limits etc). Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-4-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/acpi.c | 5 +++++ drivers/platform/x86/amd/pmf/pmf.h | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1f287a147c57..13af83b187ac 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -166,6 +166,11 @@ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data)); } +int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req) +{ + return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req)); +} + int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req) { return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 51e31d008ef9..aeaaff290bff 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -120,6 +120,18 @@ struct apmf_sbios_req { u8 skin_temp_hs2; } __packed; +struct apmf_sbios_req_v2 { + u16 size; + u32 pending_req; + u8 rsd; + u32 ppt_pmf; + u32 ppt_pmf_apu_only; + u32 stt_min_limit; + u8 skin_temp_apu; + u8 skin_temp_hs2; + u32 custom_policy[10]; +} __packed; + struct apmf_fan_idx { u16 size; u8 fan_ctl_mode; @@ -621,6 +633,7 @@ void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev); void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev); void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms); int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req); +int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req); void amd_pmf_update_2_cql(struct amd_pmf_dev *dev, bool is_cql_event); int amd_pmf_reset_amt(struct amd_pmf_dev *dev); -- cgit v1.2.3 From 6262938eef28254f49c34af48e18cb543b0db5cd Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:12 +0530 Subject: platform/x86/amd/pmf: Add support to notify sbios heart beat event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for newer revision of the heart beat notify events. This event is used to notify to the OEM BIOS on driver load/unload/suspend/resume scenarios. If OEM BIOS does not receive the heart beat event from PMF driver, OEM BIOS shall conclude that PMF driver is no more active and BIOS will update to the legacy system power thermals. Reviewed-by: Kuppuswamy Sathyanarayanan Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-5-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/acpi.c | 41 +++++++++++++++++++++++++++++++++++-- drivers/platform/x86/amd/pmf/core.c | 10 +++++++++ drivers/platform/x86/amd/pmf/pmf.h | 17 +++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 13af83b187ac..f0360eea81e6 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -140,6 +140,43 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work) kfree(info); } +int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag) +{ + struct sbios_hb_event_v2 args = { }; + struct acpi_buffer params; + union acpi_object *info; + + args.size = sizeof(args); + + switch (flag) { + case ON_LOAD: + args.load = 1; + break; + case ON_UNLOAD: + args.unload = 1; + break; + case ON_SUSPEND: + args.suspend = 1; + break; + case ON_RESUME: + args.resume = 1; + break; + default: + dev_dbg(dev->dev, "Failed to send v2 heartbeat event, flag:0x%x\n", flag); + return -EINVAL; + } + + params.length = sizeof(args); + params.pointer = &args; + + info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2, ¶ms); + if (!info) + return -EIO; + + kfree(info); + return 0; +} + int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) { union acpi_object *info; @@ -327,7 +364,7 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) { acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); - if (pmf_dev->hb_interval) + if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) cancel_delayed_work_sync(&pmf_dev->heart_beat); if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) && @@ -351,7 +388,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev) goto out; } - if (pmf_dev->hb_interval) { + if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) { /* send heartbeats only if the interval is not zero */ INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify); schedule_delayed_work(&pmf_dev->heart_beat, 0); diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index a6bdde4f1c68..53c0f61e5c5f 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -299,6 +299,9 @@ static int amd_pmf_suspend_handler(struct device *dev) kfree(pdev->buf); + if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2)) + amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_SUSPEND); + return 0; } @@ -313,6 +316,9 @@ static int amd_pmf_resume_handler(struct device *dev) return ret; } + if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2)) + amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_RESUME); + return 0; } @@ -435,6 +441,8 @@ static int amd_pmf_probe(struct platform_device *pdev) amd_pmf_dbgfs_register(dev); amd_pmf_init_features(dev); apmf_install_handler(dev); + if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2)) + amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD); dev_info(dev->dev, "registered PMF device successfully\n"); @@ -446,6 +454,8 @@ static void amd_pmf_remove(struct platform_device *pdev) struct amd_pmf_dev *dev = platform_get_drvdata(pdev); amd_pmf_deinit_features(dev); + if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2)) + amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD); apmf_acpi_deinit(dev); amd_pmf_dbgfs_unregister(dev); mutex_destroy(&dev->lock); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index aeaaff290bff..bcd1ae71109e 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -34,6 +34,7 @@ struct cookie_header { #define APMF_FUNC_STATIC_SLIDER_GRANULAR 9 #define APMF_FUNC_DYN_SLIDER_AC 11 #define APMF_FUNC_DYN_SLIDER_DC 12 +#define APMF_FUNC_SBIOS_HEARTBEAT_V2 16 /* Message Definitions */ #define SET_SPL 0x03 /* SPL: Sustained Power Limit */ @@ -89,6 +90,21 @@ struct cookie_header { #define PMF_IF_V1 1 +struct sbios_hb_event_v2 { + u16 size; + u8 load; + u8 unload; + u8 suspend; + u8 resume; +} __packed; + +enum sbios_hb_v2 { + ON_LOAD, + ON_UNLOAD, + ON_SUSPEND, + ON_RESUME, +}; + /* AMD PMF BIOS interfaces */ struct apmf_verify_interface { u16 size; @@ -609,6 +625,7 @@ int amd_pmf_get_power_source(void); int apmf_install_handler(struct amd_pmf_dev *pmf_dev); int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag); int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer); +int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag); /* SPS Layer */ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf); -- cgit v1.2.3 From 48d38f569261bc6faa2b099be7a4029e538e095f Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:13 +0530 Subject: platform/x86/amd/pmf: Add support to get APTS index numbers for static slider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit APMF spec has a newer section called the APTS (AMD Performance and Thermal State) information, where each slider/power mode is associated with an index number. Add support to get these indices for the Static Slider. Reviewed-by: Ilpo Järvinen Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-6-Shyam-sundar.S-k@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/acpi.c | 10 +++++++ drivers/platform/x86/amd/pmf/pmf.h | 25 +++++++++++++++++ drivers/platform/x86/amd/pmf/sps.c | 56 ++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index f0360eea81e6..82dafe74fc6d 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -96,6 +96,16 @@ int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index) return !!(pdev->supported_func & BIT(index - 1)); } +int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev, + struct apmf_static_slider_granular_output_v2 *data) +{ + if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) + return -EINVAL; + + return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR, + data, sizeof(*data)); +} + int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev, struct apmf_static_slider_granular_output *data) { diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index bcd1ae71109e..44ac22098d79 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -89,6 +89,7 @@ struct cookie_header { #define MAX_OPERATION_PARAMS 4 #define PMF_IF_V1 1 +#define PMF_IF_V2 2 struct sbios_hb_event_v2 { u16 size; @@ -228,6 +229,14 @@ enum power_modes { POWER_MODE_MAX, }; +enum power_modes_v2 { + POWER_MODE_BEST_PERFORMANCE, + POWER_MODE_BALANCED, + POWER_MODE_BEST_POWER_EFFICIENCY, + POWER_MODE_ENERGY_SAVE, + POWER_MODE_V2_MAX, +}; + struct amd_pmf_dev { void __iomem *regbase; void __iomem *smu_virt_addr; @@ -268,6 +277,10 @@ struct amd_pmf_dev { u16 pmf_if_version; }; +struct apmf_sps_prop_granular_v2 { + u8 power_states[POWER_SOURCE_MAX][POWER_MODE_V2_MAX]; +} __packed; + struct apmf_sps_prop_granular { u32 fppt; u32 sppt; @@ -289,6 +302,16 @@ struct amd_pmf_static_slider_granular { struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX]; }; +struct apmf_static_slider_granular_output_v2 { + u16 size; + struct apmf_sps_prop_granular_v2 sps_idx; +} __packed; + +struct amd_pmf_static_slider_granular_v2 { + u16 size; + struct apmf_sps_prop_granular_v2 sps_idx; +}; + struct os_power_slider { u16 size; u8 slider_event; @@ -643,6 +666,8 @@ const char *amd_pmf_source_as_str(unsigned int state); int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf); +int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *dev, + struct apmf_static_slider_granular_output_v2 *data); /* Auto Mode Layer */ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data); diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 33e23e25c8b1..0cf38bdae7a4 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -10,9 +10,26 @@ #include "pmf.h" +static struct amd_pmf_static_slider_granular_v2 config_store_v2; static struct amd_pmf_static_slider_granular config_store; #ifdef CONFIG_AMD_PMF_DEBUG +static const char *slider_v2_as_str(unsigned int state) +{ + switch (state) { + case POWER_MODE_BEST_PERFORMANCE: + return "Best Performance"; + case POWER_MODE_BALANCED: + return "Balanced"; + case POWER_MODE_BEST_POWER_EFFICIENCY: + return "Best Power Efficiency"; + case POWER_MODE_ENERGY_SAVE: + return "Energy Save"; + default: + return "Unknown Power Mode"; + } +} + static const char *slider_as_str(unsigned int state) { switch (state) { @@ -63,10 +80,44 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat pr_debug("Static Slider Data - END\n"); } + +static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) +{ + unsigned int i, j; + + pr_debug("Static Slider APTS state index data - BEGIN"); + pr_debug("size: %u\n", data->size); + + for (i = 0; i < POWER_SOURCE_MAX; i++) + for (j = 0; j < POWER_MODE_V2_MAX; j++) + pr_debug("%s %s: %u\n", amd_pmf_source_as_str(i), slider_v2_as_str(j), + data->sps_idx.power_states[i][j]); + + pr_debug("Static Slider APTS state index data - END\n"); +} #else static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {} +static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) {} #endif +static void amd_pmf_load_defaults_sps_v2(struct amd_pmf_dev *dev) +{ + struct apmf_static_slider_granular_output_v2 output; + unsigned int i, j; + + memset(&config_store_v2, 0, sizeof(config_store_v2)); + apmf_get_static_slider_granular_v2(dev, &output); + + config_store_v2.size = output.size; + + for (i = 0; i < POWER_SOURCE_MAX; i++) + for (j = 0; j < POWER_MODE_V2_MAX; j++) + config_store_v2.sps_idx.power_states[i][j] = + output.sps_idx.power_states[i][j]; + + amd_pmf_dump_sps_defaults_v2(&config_store_v2); +} + static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev) { struct apmf_static_slider_granular_output output; @@ -256,7 +307,10 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) dev->current_profile = PLATFORM_PROFILE_BALANCED; if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { - amd_pmf_load_defaults_sps(dev); + if (dev->pmf_if_version == PMF_IF_V2) + amd_pmf_load_defaults_sps_v2(dev); + else + amd_pmf_load_defaults_sps(dev); /* update SPS balanced power mode thermals */ amd_pmf_set_sps_power_limits(dev); -- cgit v1.2.3 From 3eecb434d7f21be5fc79604b67954be30d7e39cb Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:14 +0530 Subject: platform/x86/amd/pmf: Add support to get sps default APTS index values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the driver probe, the default cache values for the static slider would be obtained by evaluating the APTS method. Add support to use these values as the thermal settings to be updated on the system based on the changing platform-profiles. Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-7-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/acpi.c | 74 +++++++++++++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/pmf.h | 25 +++++++++++++ drivers/platform/x86/amd/pmf/sps.c | 51 ++++++++++++++++++++++++- 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 82dafe74fc6d..d0cf46e2fc8e 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -90,12 +90,86 @@ out: return err; } +static union acpi_object *apts_if_call(struct amd_pmf_dev *pdev, u32 state_index) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_handle ahandle = ACPI_HANDLE(pdev->dev); + struct acpi_object_list apts_if_arg_list; + union acpi_object apts_if_args[3]; + acpi_status status; + + apts_if_arg_list.count = 3; + apts_if_arg_list.pointer = &apts_if_args[0]; + + apts_if_args[0].type = ACPI_TYPE_INTEGER; + apts_if_args[0].integer.value = 1; + apts_if_args[1].type = ACPI_TYPE_INTEGER; + apts_if_args[1].integer.value = state_index; + apts_if_args[2].type = ACPI_TYPE_INTEGER; + apts_if_args[2].integer.value = 0; + + status = acpi_evaluate_object(ahandle, "APTS", &apts_if_arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(pdev->dev, "APTS state_idx:%u call failed\n", state_index); + kfree(buffer.pointer); + return NULL; + } + + return buffer.pointer; +} + +static int apts_if_call_store_buffer(struct amd_pmf_dev *pdev, + u32 index, void *data, size_t out_sz) +{ + union acpi_object *info; + size_t size; + int err = 0; + + info = apts_if_call(pdev, index); + if (!info) + return -EIO; + + if (info->type != ACPI_TYPE_BUFFER) { + dev_err(pdev->dev, "object is not a buffer\n"); + err = -EINVAL; + goto out; + } + + size = *(u16 *)info->buffer.pointer; + if (info->buffer.length < size) { + dev_err(pdev->dev, "buffer smaller than header size %u < %zu\n", + info->buffer.length, size); + err = -EINVAL; + goto out; + } + + if (size < out_sz) { + dev_err(pdev->dev, "buffer too small %zu\n", size); + err = -EINVAL; + goto out; + } + + memcpy(data, info->buffer.pointer, out_sz); +out: + kfree(info); + return err; +} + int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index) { /* If bit-n is set, that indicates function n+1 is supported */ return !!(pdev->supported_func & BIT(index - 1)); } +int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev, + struct amd_pmf_apts_granular_output *data, u32 apts_idx) +{ + if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) + return -EINVAL; + + return apts_if_call_store_buffer(pdev, apts_idx, data, sizeof(*data)); +} + int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev, struct apmf_static_slider_granular_output_v2 *data) { diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 44ac22098d79..10d5f8cef27e 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -91,6 +91,29 @@ struct cookie_header { #define PMF_IF_V1 1 #define PMF_IF_V2 2 +#define APTS_MAX_STATES 16 + +/* APTS PMF BIOS Interface */ +struct amd_pmf_apts_output { + u16 table_version; + u32 fan_table_idx; + u32 pmf_ppt; + u32 ppt_pmf_apu_only; + u32 stt_min_limit; + u8 stt_skin_temp_limit_apu; + u8 stt_skin_temp_limit_hs2; +} __packed; + +struct amd_pmf_apts_granular_output { + u16 size; + struct amd_pmf_apts_output val; +} __packed; + +struct amd_pmf_apts_granular { + u16 size; + struct amd_pmf_apts_output val[APTS_MAX_STATES]; +}; + struct sbios_hb_event_v2 { u16 size; u8 load; @@ -668,6 +691,8 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf); int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *dev, struct apmf_static_slider_granular_output_v2 *data); +int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev, + struct amd_pmf_apts_granular_output *data, u32 apts_idx); /* Auto Mode Layer */ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data); diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 0cf38bdae7a4..96bd2140302c 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -12,6 +12,7 @@ static struct amd_pmf_static_slider_granular_v2 config_store_v2; static struct amd_pmf_static_slider_granular config_store; +static struct amd_pmf_apts_granular apts_config_store; #ifdef CONFIG_AMD_PMF_DEBUG static const char *slider_v2_as_str(unsigned int state) @@ -95,11 +96,55 @@ static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v pr_debug("Static Slider APTS state index data - END\n"); } + +static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info) +{ + int i; + + pr_debug("Static Slider APTS index default values data - BEGIN"); + + for (i = 0; i < APTS_MAX_STATES; i++) { + pr_debug("Table Version[%d] = %u\n", i, info->val[i].table_version); + pr_debug("Fan Index[%d] = %u\n", i, info->val[i].fan_table_idx); + pr_debug("PPT[%d] = %u\n", i, info->val[i].pmf_ppt); + pr_debug("PPT APU[%d] = %u\n", i, info->val[i].ppt_pmf_apu_only); + pr_debug("STT Min[%d] = %u\n", i, info->val[i].stt_min_limit); + pr_debug("STT APU[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_apu); + pr_debug("STT HS2[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_hs2); + } + + pr_debug("Static Slider APTS index default values data - END"); +} #else static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {} static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) {} +static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info) {} #endif +static void amd_pmf_load_apts_defaults_sps_v2(struct amd_pmf_dev *pdev) +{ + struct amd_pmf_apts_granular_output output; + struct amd_pmf_apts_output *ps; + int i; + + memset(&apts_config_store, 0, sizeof(apts_config_store)); + + ps = apts_config_store.val; + + for (i = 0; i < APTS_MAX_STATES; i++) { + apts_get_static_slider_granular_v2(pdev, &output, i); + ps[i].table_version = output.val.table_version; + ps[i].fan_table_idx = output.val.fan_table_idx; + ps[i].pmf_ppt = output.val.pmf_ppt; + ps[i].ppt_pmf_apu_only = output.val.ppt_pmf_apu_only; + ps[i].stt_min_limit = output.val.stt_min_limit; + ps[i].stt_skin_temp_limit_apu = output.val.stt_skin_temp_limit_apu; + ps[i].stt_skin_temp_limit_hs2 = output.val.stt_skin_temp_limit_hs2; + } + + amd_pmf_dump_apts_sps_defaults(&apts_config_store); +} + static void amd_pmf_load_defaults_sps_v2(struct amd_pmf_dev *dev) { struct apmf_static_slider_granular_output_v2 output; @@ -307,10 +352,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) dev->current_profile = PLATFORM_PROFILE_BALANCED; if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { - if (dev->pmf_if_version == PMF_IF_V2) + if (dev->pmf_if_version == PMF_IF_V2) { amd_pmf_load_defaults_sps_v2(dev); - else + amd_pmf_load_apts_defaults_sps_v2(dev); + } else { amd_pmf_load_defaults_sps(dev); + } /* update SPS balanced power mode thermals */ amd_pmf_set_sps_power_limits(dev); -- cgit v1.2.3 From 8362e862fb87992f083f75dd49d029e8fc0d803b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 6 Mar 2024 17:14:15 +0530 Subject: platform/x86/amd/pmf: Update sps power thermals according to the platform-profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update power thermals according to the platform-profiles selected by the user. Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240306114415.3267603-8-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/pmf.h | 2 ++ drivers/platform/x86/amd/pmf/sps.c | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 10d5f8cef27e..907e046c3c96 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -55,6 +55,8 @@ struct cookie_header { #define GET_STT_LIMIT_APU 0x20 #define GET_STT_LIMIT_HS2 0x21 #define SET_P3T 0x23 /* P3T: Peak Package Power Limit */ +#define SET_PMF_PPT 0x25 +#define SET_PMF_PPT_APU_ONLY 0x26 /* OS slider update notification */ #define DC_BEST_PERF 0 diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 96bd2140302c..92f7fb22277d 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -190,6 +190,19 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev) amd_pmf_dump_sps_defaults(&config_store); } +static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx) +{ + amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL); + amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false, + apts_config_store.val[idx].ppt_pmf_apu_only, NULL); + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, + apts_config_store.val[idx].stt_min_limit, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL); +} + void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, struct amd_pmf_static_slider_granular *table) { @@ -222,6 +235,32 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, } } +static int amd_pmf_update_sps_power_limits_v2(struct amd_pmf_dev *pdev, int pwr_mode) +{ + int src, index; + + src = amd_pmf_get_power_source(); + + switch (pwr_mode) { + case POWER_MODE_PERFORMANCE: + index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_PERFORMANCE]; + amd_pmf_update_slider_v2(pdev, index); + break; + case POWER_MODE_BALANCED_POWER: + index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BALANCED]; + amd_pmf_update_slider_v2(pdev, index); + break; + case POWER_MODE_POWER_SAVER: + index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_POWER_EFFICIENCY]; + amd_pmf_update_slider_v2(pdev, index); + break; + default: + return -EINVAL; + } + + return 0; +} + int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf) { int mode; @@ -230,6 +269,9 @@ int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf) if (mode < 0) return mode; + if (pmf->pmf_if_version == PMF_IF_V2) + return amd_pmf_update_sps_power_limits_v2(pmf, mode); + amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL); return 0; -- cgit v1.2.3 From 30f96b2cb70d882515efae4d4af698f18a65c75e Mon Sep 17 00:00:00 2001 From: Daniil Dulov Date: Wed, 6 Mar 2024 18:38:04 +0300 Subject: platform/mellanox: mlxreg-hotplug: Remove redundant NULL-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointer item is checked fo NULL at mlxreg_hotplug_work_helper() and then it is dereferenced to produce dev_err(). This pointer is also dereferenced before calling this function and should never be NULL except some piece of hardware is broken as it is said in the comment before the check. So, this check can be safely removed. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: c6acad68eb2d ("platform/mellanox: mlxreg-hotplug: Modify to use a regmap interface") Signed-off-by: Daniil Dulov Reviewed-by: Vadim Pasternak Link: https://lore.kernel.org/r/20240306153804.6509-1-d.dulov@aladdin.ru Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxreg-hotplug.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 5c022b258f91..0ce9fff1f7d4 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -348,20 +348,6 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, u32 regval, bit; int ret; - /* - * Validate if item related to received signal type is valid. - * It should never happen, excepted the situation when some - * piece of hardware is broken. In such situation just produce - * error message and return. Caller must continue to handle the - * signals from other devices if any. - */ - if (unlikely(!item)) { - dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n", - item->reg, item->mask); - - return; - } - /* Mask event. */ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF, 0); -- cgit v1.2.3 From e9df5436fd61c8cf375ac086d936f90385cd6888 Mon Sep 17 00:00:00 2001 From: Erwan Velu Date: Thu, 7 Mar 2024 14:35:59 +0100 Subject: Documentation/x86/amd/hsmp: Updating urls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading this page, some links were broken. This commit updates links to get documentation actually pointing the intended content. Signed-off-by: Erwan Velu Link: https://lore.kernel.org/r/20240307133601.103521-1-e.velu@criteo.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/arch/x86/amd_hsmp.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index c92bfd55359f..1e499ecf5f4e 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -13,7 +13,8 @@ set of mailbox registers. More details on the interface can be found in chapter "7 Host System Management Port (HSMP)" of the family/model PPR -Eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip +Eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip + HSMP interface is supported on EPYC server CPU models only. @@ -97,8 +98,8 @@ what happened. The transaction returns 0 on success. More details on the interface and message definitions can be found in chapter "7 Host System Management Port (HSMP)" of the respective family/model PPR -eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip +eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip User space C-APIs are made available by linking against the esmi library, -which is provided by the E-SMS project https://developer.amd.com/e-sms/. +which is provided by the E-SMS project https://www.amd.com/en/developer/e-sms.html. See: https://github.com/amd/esmi_ib_library -- cgit v1.2.3 From f7b7066508d69934e4545db0c709c98ce506df0c Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 8 Mar 2024 13:32:55 +0800 Subject: platform/x86: asus-wmi: Consider device is absent when the read is ~0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AAEON PICO-TGU4 board doesn't have any LED but there are bogus LED controls under /sys/class/leds: $ ls /sys/class/leds asus::kbd_backlight asus::lightbar platform::micmute The reason is that the ~0 read from asus_wmi_get_devstate() is treated as a valid state, in truth it means the device is absent. So filter out ~0 read to prevent bogus LED controls being created. Signed-off-by: Kai-Heng Feng Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240308053255.224496-1-kai.heng.feng@canonical.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 2865af89e95c..3f07bbf809ef 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -481,7 +481,17 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) { - return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval); + int err; + + err = asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval); + + if (err) + return err; + + if (*retval == ~0) + return -ENODEV; + + return 0; } static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, -- cgit v1.2.3 From 16f8091b49175f327120cdbbdde135d38a853ae1 Mon Sep 17 00:00:00 2001 From: Kane Chen Date: Fri, 8 Mar 2024 11:31:27 +0800 Subject: platform/x86/intel/pmc: Improve PKGC residency counters debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code only prints PKGC-10 residency when the PKGC-10 is not reached in previous 'freeze' attempt. To debug PKGC-10 issues, we also need to know other PKGC residency counters to better triage issues. Ex: 1. When system is stuck in PC2, it can be caused short LTR from device. 2. When system is stuck in PC8, it can be caused by display engine. To better triage issues, all PKGC residency are needed when issues happen. Example log: CPU did not enter Package C10!!! (Package C10 cnt=0x0) Prev Package C2 cnt = 0x2191a325de, Current Package C2 cnt = 0x21aba30724 Prev Package C3 cnt = 0x0, Current Package C3 cnt = 0x0 Prev Package C6 cnt = 0x0, Current Package C6 cnt = 0x0 Prev Package C7 cnt = 0x0, Current Package C7 cnt = 0x0 Prev Package C8 cnt = 0x0, Current Package C8 cnt = 0x0 Prev Package C9 cnt = 0x0, Current Package C9 cnt = 0x0 Prev Package C10 cnt = 0x0, Current Package C10 cnt = 0x0 With this log, we can know whether it's a stuck PC2 issue, and we can check whether the short LTR from device causes the issue. Signed-off-by: Kane Chen Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240308033127.1013053-1-kane.chen@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 47 +++++++++++++++++++++++++++-------- drivers/platform/x86/intel/pmc/core.h | 7 ++++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 8f9c036809c7..10c96c1a850a 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1389,6 +1389,15 @@ static int pmc_core_probe(struct platform_device *pdev) return -ENOMEM; pmcdev->pmcs[PMC_IDX_MAIN] = primary_pmc; + /* The last element in msr_map is empty */ + pmcdev->num_of_pkgc = ARRAY_SIZE(msr_map) - 1; + pmcdev->pkgc_res_cnt = devm_kcalloc(&pdev->dev, + pmcdev->num_of_pkgc, + sizeof(*pmcdev->pkgc_res_cnt), + GFP_KERNEL); + if (!pmcdev->pkgc_res_cnt) + return -ENOMEM; + /* * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap @@ -1432,6 +1441,7 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) { struct pmc_dev *pmcdev = dev_get_drvdata(dev); struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + unsigned int i; if (pmcdev->suspend) pmcdev->suspend(pmcdev); @@ -1440,9 +1450,11 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) if (pm_suspend_via_firmware()) return 0; - /* Save PC10 residency for checking later */ - if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pmcdev->pc10_counter)) - return -EIO; + /* Save PKGC residency for checking later */ + for (i = 0; i < pmcdev->num_of_pkgc; i++) { + if (rdmsrl_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i])) + return -EIO; + } /* Save S0ix residency for checking later */ if (pmc_core_dev_state_get(pmc, &pmcdev->s0ix_counter)) @@ -1451,14 +1463,15 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) return 0; } -static inline bool pmc_core_is_pc10_failed(struct pmc_dev *pmcdev) +static inline bool pmc_core_is_deepest_pkgc_failed(struct pmc_dev *pmcdev) { - u64 pc10_counter; + u32 deepest_pkgc_msr = msr_map[pmcdev->num_of_pkgc - 1].bit_mask; + u64 deepest_pkgc_residency; - if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pc10_counter)) + if (rdmsrl_safe(deepest_pkgc_msr, &deepest_pkgc_residency)) return false; - if (pc10_counter == pmcdev->pc10_counter) + if (deepest_pkgc_residency == pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]) return true; return false; @@ -1497,10 +1510,22 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) if (!warn_on_s0ix_failures) return 0; - if (pmc_core_is_pc10_failed(pmcdev)) { - /* S0ix failed because of PC10 entry failure */ - dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n", - pmcdev->pc10_counter); + if (pmc_core_is_deepest_pkgc_failed(pmcdev)) { + /* S0ix failed because of deepest PKGC entry failure */ + dev_info(dev, "CPU did not enter %s!!! (%s cnt=0x%llx)\n", + msr_map[pmcdev->num_of_pkgc - 1].name, + msr_map[pmcdev->num_of_pkgc - 1].name, + pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]); + + for (i = 0; i < pmcdev->num_of_pkgc; i++) { + u64 pc_cnt; + + if (!rdmsrl_safe(msr_map[i].bit_mask, &pc_cnt)) { + dev_info(dev, "Prev %s cnt = 0x%llx, Current %s cnt = 0x%llx\n", + msr_map[i].name, pmcdev->pkgc_res_cnt[i], + msr_map[i].name, pc_cnt); + } + } return 0; } diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 54137faaae2b..83504c49a0e3 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -385,7 +385,8 @@ struct pmc { * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers * used to read MPHY PG and PLL status are available * @mutex_lock: mutex to complete one transcation - * @pc10_counter: PC10 residency counter + * @pkgc_res_cnt: Array of PKGC residency counters + * @num_of_pkgc: Number of PKGC * @s0ix_counter: S0ix residency (step adjusted) * @num_lpm_modes: Count of enabled modes * @lpm_en_modes: Array of enabled modes from lowest to highest priority @@ -403,13 +404,15 @@ struct pmc_dev { int pmc_xram_read_bit; struct mutex lock; /* generic mutex lock for PMC Core */ - u64 pc10_counter; u64 s0ix_counter; int num_lpm_modes; int lpm_en_modes[LPM_MAX_NUM_MODES]; void (*suspend)(struct pmc_dev *pmcdev); int (*resume)(struct pmc_dev *pmcdev); + u64 *pkgc_res_cnt; + u8 num_of_pkgc; + bool has_die_c6; u32 die_c6_offset; struct telem_endpoint *punit_ep; -- cgit v1.2.3