From bcbfcebda2cbc6a10a347d726e4a4f69e43a864e Mon Sep 17 00:00:00 2001 From: Mohamed Ghanmi Date: Sun, 9 Jun 2024 15:48:49 +0100 Subject: [PATCH 01/65] platform/x86: asus-wmi: add support for vivobook fan profiles Add support for vivobook fan profiles wmi call on the ASUS VIVOBOOK to adjust power limits. These fan profiles have a different device id than the ROG series and different order. This reorders the existing modes. As part of keeping the patch clean the throttle_thermal_policy_available boolean stored in the driver struct is removed and throttle_thermal_policy_dev is used in place (as on init it is zeroed). Co-developed-by: Luke D. Jones Signed-off-by: Luke D. Jones Signed-off-by: Mohamed Ghanmi Reviewed-by: Luke D. Jones Link: https://lore.kernel.org/r/20240609144849.2532-2-mohamed.ghanmi@supcom.tn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 120 ++++++++++++--------- include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index cc735931f97b..00669cf678fb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -97,6 +97,12 @@ module_param(fnlock_default, bool, 0444); #define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1 #define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2 +#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO 0 +#define ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO 1 +#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO 2 + +#define PLATFORM_PROFILE_MAX 2 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -286,8 +292,8 @@ struct asus_wmi { u32 kbd_rgb_dev; bool kbd_rgb_state_available; - bool throttle_thermal_policy_available; u8 throttle_thermal_policy_mode; + u32 throttle_thermal_policy_dev; bool cpu_fan_curve_available; bool gpu_fan_curve_available; @@ -3162,7 +3168,7 @@ static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev) int err, fan_idx; u8 mode = 0; - if (asus->throttle_thermal_policy_available) + if (asus->throttle_thermal_policy_dev) mode = asus->throttle_thermal_policy_mode; /* DEVID_PU_FAN_CURVE is switched for OVERBOOST vs SILENT */ if (mode == 2) @@ -3369,7 +3375,7 @@ static ssize_t fan_curve_enable_store(struct device *dev, * For machines with throttle this is the only way to reset fans * to default mode of operation (does not erase curve data). */ - if (asus->throttle_thermal_policy_available) { + if (asus->throttle_thermal_policy_dev) { err = throttle_thermal_policy_write(asus); if (err) return err; @@ -3586,8 +3592,8 @@ static const struct attribute_group asus_fan_curve_attr_group = { __ATTRIBUTE_GROUPS(asus_fan_curve_attr); /* - * Must be initialised after throttle_thermal_policy_check_present() as - * we check the status of throttle_thermal_policy_available during init. + * Must be initialised after throttle_thermal_policy_dev is set as + * we check the status of throttle_thermal_policy_dev during init. */ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) { @@ -3628,38 +3634,13 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) } /* Throttle thermal policy ****************************************************/ - -static int throttle_thermal_policy_check_present(struct asus_wmi *asus) -{ - u32 result; - int err; - - asus->throttle_thermal_policy_available = false; - - err = asus_wmi_get_devstate(asus, - ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, - &result); - if (err) { - if (err == -ENODEV) - return 0; - return err; - } - - if (result & ASUS_WMI_DSTS_PRESENCE_BIT) - asus->throttle_thermal_policy_available = true; - - return 0; -} - static int throttle_thermal_policy_write(struct asus_wmi *asus) { - int err; - u8 value; + u8 value = asus->throttle_thermal_policy_mode; u32 retval; + int err; - value = asus->throttle_thermal_policy_mode; - - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, + err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev, value, &retval); sysfs_notify(&asus->platform_device->dev.kobj, NULL, @@ -3689,7 +3670,7 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus) static int throttle_thermal_policy_set_default(struct asus_wmi *asus) { - if (!asus->throttle_thermal_policy_available) + if (!asus->throttle_thermal_policy_dev) return 0; asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; @@ -3701,7 +3682,7 @@ static int throttle_thermal_policy_switch_next(struct asus_wmi *asus) u8 new_mode = asus->throttle_thermal_policy_mode + 1; int err; - if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) + if (new_mode > PLATFORM_PROFILE_MAX) new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; asus->throttle_thermal_policy_mode = new_mode; @@ -3740,7 +3721,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, if (result < 0) return result; - if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) + if (new_mode > PLATFORM_PROFILE_MAX) return -EINVAL; asus->throttle_thermal_policy_mode = new_mode; @@ -3757,10 +3738,52 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, return count; } -// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent +/* + * Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent + */ static DEVICE_ATTR_RW(throttle_thermal_policy); /* Platform profile ***********************************************************/ +static int asus_wmi_platform_profile_to_vivo(struct asus_wmi *asus, int mode) +{ + bool vivo; + + vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; + + if (vivo) { + switch (mode) { + case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: + return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO; + case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST: + return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO; + case ASUS_THROTTLE_THERMAL_POLICY_SILENT: + return ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO; + } + } + + return mode; +} + +static int asus_wmi_platform_profile_mode_from_vivo(struct asus_wmi *asus, int mode) +{ + bool vivo; + + vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; + + if (vivo) { + switch (mode) { + case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO: + return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; + case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO: + return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST; + case ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO: + return ASUS_THROTTLE_THERMAL_POLICY_SILENT; + } + } + + return mode; +} + static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, enum platform_profile_option *profile) { @@ -3768,10 +3791,9 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, int tp; asus = container_of(pprof, struct asus_wmi, platform_profile_handler); - tp = asus->throttle_thermal_policy_mode; - switch (tp) { + switch (asus_wmi_platform_profile_mode_from_vivo(asus, tp)) { case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: *profile = PLATFORM_PROFILE_BALANCED; break; @@ -3810,7 +3832,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof, return -EOPNOTSUPP; } - asus->throttle_thermal_policy_mode = tp; + asus->throttle_thermal_policy_mode = asus_wmi_platform_profile_to_vivo(asus, tp); return throttle_thermal_policy_write(asus); } @@ -3823,7 +3845,7 @@ static int platform_profile_setup(struct asus_wmi *asus) * Not an error if a component platform_profile relies on is unavailable * so early return, skipping the setup of platform_profile. */ - if (!asus->throttle_thermal_policy_available) + if (!asus->throttle_thermal_policy_dev) return 0; dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n"); @@ -4238,7 +4260,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) { if (asus->fan_boost_mode_available) fan_boost_mode_switch_next(asus); - if (asus->throttle_thermal_policy_available) + if (asus->throttle_thermal_policy_dev) throttle_thermal_policy_switch_next(asus); return; @@ -4410,7 +4432,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_throttle_thermal_policy.attr) - ok = asus->throttle_thermal_policy_available; + ok = asus->throttle_thermal_policy_dev != 0; else if (attr == &dev_attr_ppt_pl2_sppt.attr) devid = ASUS_WMI_DEVID_PPT_PL2_SPPT; else if (attr == &dev_attr_ppt_pl1_spl.attr) @@ -4702,16 +4724,15 @@ static int asus_wmi_add(struct platform_device *pdev) else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2)) asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2; + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY)) + asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY; + else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO)) + asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; + err = fan_boost_mode_check_present(asus); if (err) goto fail_fan_boost_mode; - err = throttle_thermal_policy_check_present(asus); - if (err) - goto fail_throttle_thermal_policy; - else - throttle_thermal_policy_set_default(asus); - err = platform_profile_setup(asus); if (err) goto fail_platform_profile_setup; @@ -4806,7 +4827,6 @@ fail_hwmon: fail_input: asus_wmi_sysfs_exit(asus->platform_device); fail_sysfs: -fail_throttle_thermal_policy: fail_custom_fan_curve: fail_platform_profile_setup: if (asus->platform_profile_support) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 0aeeae1c1943..5e6f407b2def 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -68,6 +68,7 @@ #define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032 #define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 +#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019 /* Misc */ #define ASUS_WMI_DEVID_PANEL_OD 0x00050019 From a720dee5e039238a44c0142dfccdc0e35c1125f7 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 13 Jul 2024 19:47:33 +1200 Subject: [PATCH 02/65] hid-asus: use hid for brightness control on keyboard On almost all ASUS ROG series laptops the MCU used for the USB keyboard also has a HID packet used for setting the brightness. This is usually the same as the WMI method. But in some laptops the WMI method either is missing or doesn't work, so we should default to the HID control. Signed-off-by: Luke D. Jones Acked-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20240713074733.77334-2-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/hid/hid-asus.c | 7 +++++ drivers/platform/x86/asus-wmi.c | 3 +- include/linux/platform_data/x86/asus-wmi.h | 36 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 37e6d25593c2..af57a5f03193 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -492,12 +492,19 @@ static void asus_kbd_backlight_work(struct work_struct *work) */ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) { + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); u32 value; int ret; if (!IS_ENABLED(CONFIG_ASUS_WMI)) return false; + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && + dmi_check_system(asus_use_hid_led_dmi_ids)) { + hid_info(hdev, "using HID for asus::kbd_backlight\n"); + return false; + } + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 00669cf678fb..35ebb37f68bf 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1714,7 +1714,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (!kbd_led_read(asus, &led_val, NULL)) { + if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) { + pr_info("using asus-wmi for asus::kbd_backlight\n"); asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 5e6f407b2def..b601b245a035 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -4,6 +4,7 @@ #include #include +#include /* WMI Methods */ #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ @@ -165,4 +166,39 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, } #endif +/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ +static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA403U"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605M"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "RC71L"), + }, + }, + { }, +}; + #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ From 4c29e80ab885fed99d863d744e41a9b50cd32f2d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 29 May 2024 11:49:55 +0200 Subject: [PATCH 03/65] platform/x86: int3472: make common part a separate module Linking an object file into multiple modules is not supported and causes a W=1 warning: scripts/Makefile.build:236: drivers/platform/x86/intel/int3472/Makefile: common.o is added to multiple modules: intel_skl_int3472_discrete intel_skl_int3472_tps68470 Split out the common part here into a separate module to make it more reliable. Fixes: a2f9fbc247ee ("platform/x86: int3472: Split into 2 drivers") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240529095009.1895618-1-arnd@kernel.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int3472/Makefile | 9 ++++++--- drivers/platform/x86/intel/int3472/common.c | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile index 9f16cb514397..a8aba07bf1dc 100644 --- a/drivers/platform/x86/intel/int3472/Makefile +++ b/drivers/platform/x86/intel/int3472/Makefile @@ -1,4 +1,7 @@ obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ - intel_skl_int3472_tps68470.o -intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o -intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o + intel_skl_int3472_tps68470.o \ + intel_skl_int3472_common.o +intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o +intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o + +intel_skl_int3472_common-y += common.o diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c index 9db2bb0bbba4..b3a2578e06c1 100644 --- a/drivers/platform/x86/intel/int3472/common.c +++ b/drivers/platform/x86/intel/int3472/common.c @@ -29,6 +29,7 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *i return obj; } +EXPORT_SYMBOL_GPL(skl_int3472_get_acpi_buffer); int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) { @@ -52,6 +53,7 @@ out_free_obj: kfree(obj); return ret; } +EXPORT_SYMBOL_GPL(skl_int3472_fill_cldb); /* sensor_adev_ret may be NULL, name_ret must not be NULL */ int skl_int3472_get_sensor_adev_and_name(struct device *dev, @@ -80,3 +82,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, return ret; } +EXPORT_SYMBOL_GPL(skl_int3472_get_sensor_adev_and_name); + +MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver library"); +MODULE_AUTHOR("Daniel Scally "); +MODULE_LICENSE("GPL"); From a6fe07ce522a9f6242161ec516d46234c124c35f Mon Sep 17 00:00:00 2001 From: Kane Chen Date: Fri, 19 Jul 2024 20:28:07 +0800 Subject: [PATCH 04/65] platform/x86/intel/pmc: Show live substate requirements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While debugging runtime s0ix, we do need to check which required IPs are not power gated. This patch adds code to show live substate status vs requirements in sys/kernel/debug/pmc_core/substate_requirements to help runtime s0ix debug. Signed-off-by: Kane Chen Link: https://lore.kernel.org/r/20240719122807.3853292-1-kane.chen@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 01ae71c6df59..d70eee6b6b32 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -794,13 +794,15 @@ static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index) pmc_for_each_mode(i, mode, pmcdev) seq_printf(s, " %9s |", pmc_lpm_modes[mode]); - seq_printf(s, " %9s |\n", "Status"); + seq_printf(s, " %9s |", "Status"); + seq_printf(s, " %11s |\n", "Live Status"); } static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; u32 sts_offset; + u32 sts_offset_live; u32 *lpm_req_regs; unsigned int mp, pmc_index; int num_maps; @@ -815,6 +817,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) maps = pmc->map->lpm_sts; num_maps = pmc->map->lpm_num_maps; sts_offset = pmc->map->lpm_status_offset; + sts_offset_live = pmc->map->lpm_live_status_offset; lpm_req_regs = pmc->lpm_req_regs; /* @@ -832,6 +835,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) for (mp = 0; mp < num_maps; mp++) { u32 req_mask = 0; u32 lpm_status; + u32 lpm_status_live; const struct pmc_bit_map *map; int mode, idx, i, len = 32; @@ -846,6 +850,9 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) /* Get the last latched status for this map */ lpm_status = pmc_core_reg_read(pmc, sts_offset + (mp * 4)); + /* Get the runtime status for this map */ + lpm_status_live = pmc_core_reg_read(pmc, sts_offset_live + (mp * 4)); + /* Loop over elements in this map */ map = maps[mp]; for (i = 0; map[i].name && i < len; i++) { @@ -872,6 +879,9 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) /* In Status column, show the last captured state of this agent */ seq_printf(s, " %9s |", lpm_status & bit_mask ? "Yes" : " "); + /* In Live status column, show the live state of this agent */ + seq_printf(s, " %11s |", lpm_status_live & bit_mask ? "Yes" : " "); + seq_puts(s, "\n"); } } From fc9aef4382c02774662da3d7e1de8ba224e04f80 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 25 Jul 2024 08:23:40 -0400 Subject: [PATCH 05/65] platform/x86/intel/vsec.h: Move to include/linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some drivers outside of PDX86 need access to the vsec header. Move it to include/linux to make it easier to include. Reviewed-by: Ilpo Järvinen Reviewed-by: Michael J. Ruhl Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20240725122346.4063913-2-michael.j.ruhl@intel.com Signed-off-by: Hans de Goede --- MAINTAINERS | 3 +- drivers/platform/x86/intel/pmc/core_ssram.c | 2 +- drivers/platform/x86/intel/pmt/class.c | 2 +- drivers/platform/x86/intel/pmt/class.h | 2 +- drivers/platform/x86/intel/pmt/crashlog.c | 2 +- drivers/platform/x86/intel/pmt/telemetry.c | 2 +- drivers/platform/x86/intel/sdsi.c | 3 +- drivers/platform/x86/intel/tpmi.c | 3 +- drivers/platform/x86/intel/vsec.c | 7 ++-- .../vsec.h => include/linux/intel_vsec.h | 32 +++++++++++++++++-- 10 files changed, 41 insertions(+), 17 deletions(-) rename drivers/platform/x86/intel/vsec.h => include/linux/intel_vsec.h (71%) diff --git a/MAINTAINERS b/MAINTAINERS index 42decde38320..894608f6555c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11599,7 +11599,8 @@ F: drivers/platform/x86/intel/uncore-frequency/ INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER M: David E. Box S: Supported -F: drivers/platform/x86/intel/vsec.* +F: drivers/platform/x86/intel/vsec.c +F: include/linux/intel_vsec.h INTEL VIRTUAL BUTTON DRIVER M: AceLan Kao diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 1bde86c54eb9..baddaaec25ee 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -9,11 +9,11 @@ */ #include +#include #include #include #include "core.h" -#include "../vsec.h" #include "../pmt/telemetry.h" #define SSRAM_HDR_SIZE 0x100 diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 4b53940a64e2..d7939b28e937 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -9,12 +9,12 @@ */ #include +#include #include #include #include #include -#include "../vsec.h" #include "class.h" #define PMT_XA_START 1 diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index d23c63b73ab7..d6f9ccaf28c8 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -2,13 +2,13 @@ #ifndef _INTEL_PMT_CLASS_H #define _INTEL_PMT_CLASS_H +#include #include #include #include #include #include -#include "../vsec.h" #include "telemetry.h" /* PMT access types */ diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 4014c02cafdb..9079d5dffc03 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -16,7 +17,6 @@ #include #include -#include "../vsec.h" #include "class.h" /* Crashlog discovery header types */ diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 09258564dfc4..3478f891ea0b 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -16,7 +17,6 @@ #include #include -#include "../vsec.h" #include "class.h" #define TELEM_SIZE_OFFSET 0x0 diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 277e4f4b20ac..9d137621f0e6 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,8 +23,6 @@ #include #include -#include "vsec.h" - #define ACCESS_TYPE_BARID 2 #define ACCESS_TYPE_LOCAL 3 diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 83e8b1fe53b3..486ddc9b3592 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -59,8 +60,6 @@ #include #include -#include "vsec.h" - /** * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry * @tpmi_id: TPMI feature identifier (what the feature is and its data format). diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 0fdfaf3a4f5c..2b46807f868b 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -17,14 +17,13 @@ #include #include #include -#include #include +#include +#include #include #include #include -#include "vsec.h" - #define PMT_XA_START 0 #define PMT_XA_MAX INT_MAX #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) @@ -341,7 +340,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, void intel_vsec_register(struct pci_dev *pdev, struct intel_vsec_platform_info *info) { - if (!pdev || !info) + if (!pdev || !info || !info->headers) return; intel_vsec_walk_header(pdev, info); diff --git a/drivers/platform/x86/intel/vsec.h b/include/linux/intel_vsec.h similarity index 71% rename from drivers/platform/x86/intel/vsec.h rename to include/linux/intel_vsec.h index e23e76129691..6495e37c9079 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/include/linux/intel_vsec.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _VSEC_H -#define _VSEC_H +#ifndef _INTEL_VSEC_H +#define _INTEL_VSEC_H #include #include @@ -67,7 +67,14 @@ enum intel_vsec_quirks { VSEC_QUIRK_EARLY_HW = BIT(4), }; -/* Platform specific data */ +/** + * struct intel_vsec_platform_info - Platform specific data + * @parent: parent device in the auxbus chain + * @headers: list of headers to define the PMT client devices to create + * @caps: bitmask of PMT capabilities for the given headers + * @quirks: bitmask of VSEC device quirks + * @base_addr: allow a base address to be specified (rather than derived) + */ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; @@ -76,6 +83,18 @@ struct intel_vsec_platform_info { u64 base_addr; }; +/** + * struct intel_sec_device - Auxbus specific device information + * @auxdev: auxbus device struct for auxbus access + * @pcidev: pci device associated with the device + * @resource: any resources shared by the parent + * @ida: id reference + * @num_resources: number of resources + * @id: xarray id + * @priv_data: any private data needed + * @quirks: specified quirks + * @base_addr: base address of entries (if specified) + */ struct intel_vsec_device { struct auxiliary_device auxdev; struct pci_dev *pcidev; @@ -103,6 +122,13 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device return container_of(auxdev, struct intel_vsec_device, auxdev); } +#if IS_ENABLED(CONFIG_INTEL_VSEC) void intel_vsec_register(struct pci_dev *pdev, struct intel_vsec_platform_info *info); +#else +static inline void intel_vsec_register(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) +{ +} +#endif #endif From e92affc74cd8624a548b380af7364be037adef35 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 25 Jul 2024 08:23:41 -0400 Subject: [PATCH 06/65] platform/x86/intel/vsec: Add PMT read callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some PMT providers require device specific actions before their telemetry can be read. Provide assignable PMT read callbacks to allow providers to perform those actions. Reviewed-by: Ilpo Järvinen Reviewed-by: Michael J. Ruhl Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20240725122346.4063913-3-michael.j.ruhl@intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 1 + include/linux/intel_vsec.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 2b46807f868b..7b5cc9993974 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -212,6 +212,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->base_addr = info->base_addr; + intel_vsec_dev->priv_data = info->priv_data; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 6495e37c9079..11ee185566c3 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -67,10 +67,24 @@ enum intel_vsec_quirks { VSEC_QUIRK_EARLY_HW = BIT(4), }; +/** + * struct pmt_callbacks - Callback infrastructure for PMT devices + * ->read_telem() when specified, called by client driver to access PMT data (instead + * of direct copy). + * @pdev: PCI device reference for the callback's use + * @guid: ID of data to acccss + * @data: buffer for the data to be copied + * @count: size of buffer + */ +struct pmt_callbacks { + int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, u32 count); +}; + /** * struct intel_vsec_platform_info - Platform specific data * @parent: parent device in the auxbus chain * @headers: list of headers to define the PMT client devices to create + * @priv_data: private data, usable by parent devices, currently a callback * @caps: bitmask of PMT capabilities for the given headers * @quirks: bitmask of VSEC device quirks * @base_addr: allow a base address to be specified (rather than derived) @@ -78,6 +92,7 @@ enum intel_vsec_quirks { struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; + void *priv_data; unsigned long caps; unsigned long quirks; u64 base_addr; From 045a513040cc0242d364c05c3791594e2294f32d Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 25 Jul 2024 08:23:42 -0400 Subject: [PATCH 07/65] platform/x86/intel/pmt: Use PMT callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMT providers may require device specific actions before their telemetry may be read. If the read_telem() is assigned, call it instead of memcpy_fromio() and return. Since this needs to be done in multiple locations, add pmt_telem_read_mmio() as a wrapper function to perform this and any other needed checks. Reviewed-by: Ilpo Järvinen Reviewed-by: Michael J. Ruhl Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20240725122346.4063913-4-michael.j.ruhl@intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 26 +++++++++++++++++----- drivers/platform/x86/intel/pmt/class.h | 8 +++++-- drivers/platform/x86/intel/pmt/telemetry.c | 10 +++++---- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index d7939b28e937..c04bb7f97a4d 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -58,6 +58,22 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) return count; } +int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, + void __iomem *addr, u32 count) +{ + if (cb && cb->read_telem) + return cb->read_telem(pdev, guid, buf, count); + + if (guid == GUID_SPR_PUNIT) + /* PUNIT on SPR only supports aligned 64-bit read */ + return pmt_memcpy64_fromio(buf, addr, count); + + memcpy_fromio(buf, addr, count); + + return count; +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_read_mmio, INTEL_PMT); + /* * sysfs */ @@ -79,11 +95,8 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, if (count > entry->size - off) count = entry->size - off; - if (entry->guid == GUID_SPR_PUNIT) - /* PUNIT on SPR only supports aligned 64-bit read */ - count = pmt_memcpy64_fromio(buf, entry->base + off, count); - else - memcpy_fromio(buf, entry->base + off, count); + count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf, + entry->base + off, count); return count; } @@ -239,6 +252,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, entry->guid = header->guid; entry->size = header->size; + entry->cb = ivdev->priv_data; return 0; } @@ -300,7 +314,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, goto fail_ioremap; if (ns->pmt_add_endpoint) { - ret = ns->pmt_add_endpoint(entry, ivdev->pcidev); + ret = ns->pmt_add_endpoint(ivdev, entry); if (ret) goto fail_add_endpoint; } diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index d6f9ccaf28c8..a267ac964423 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -24,6 +24,7 @@ struct pci_dev; struct telem_endpoint { struct pci_dev *pcidev; struct telem_header header; + struct pmt_callbacks *cb; void __iomem *base; bool present; struct kref kref; @@ -43,6 +44,7 @@ struct intel_pmt_entry { struct kobject *kobj; void __iomem *disc_table; void __iomem *base; + struct pmt_callbacks *cb; unsigned long base_addr; size_t size; u32 guid; @@ -55,10 +57,12 @@ struct intel_pmt_namespace { const struct attribute_group *attr_grp; int (*pmt_header_decode)(struct intel_pmt_entry *entry, struct device *dev); - int (*pmt_add_endpoint)(struct intel_pmt_entry *entry, - struct pci_dev *pdev); + int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry); }; +int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, + void __iomem *addr, u32 count); bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 3478f891ea0b..c9feac859e57 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -93,8 +93,8 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, return 0; } -static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry, - struct pci_dev *pdev) +static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry) { struct telem_endpoint *ep; @@ -104,13 +104,14 @@ static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry, return -ENOMEM; ep = entry->ep; - ep->pcidev = pdev; + ep->pcidev = ivdev->pcidev; ep->header.access_type = entry->header.access_type; ep->header.guid = entry->header.guid; ep->header.base_offset = entry->header.base_offset; ep->header.size = entry->header.size; ep->base = entry->base; ep->present = true; + ep->cb = ivdev->priv_data; kref_init(&ep->kref); @@ -218,7 +219,8 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) if (offset + NUM_BYTES_QWORD(count) > size) return -EINVAL; - memcpy_fromio(data, ep->base + offset, NUM_BYTES_QWORD(count)); + pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base + offset, + NUM_BYTES_QWORD(count)); return ep->present ? 0 : -EPIPE; } From 754d389cdde9215f751ed4f35f1b1e5b765563cd Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:51 +0200 Subject: [PATCH 08/65] platform/x86: acer-wmi: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: "Lee, Chun-Yi" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-2-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/acer-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 38c932df6446..349169d050c5 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -1685,7 +1684,7 @@ static int acer_backlight_init(struct device *dev) acer_backlight_device = bd; - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = BACKLIGHT_POWER_ON; bd->props.brightness = read_brightness(bd); backlight_update_status(bd); return 0; From 101cc8c6fcfa9f2ac49636b31682764a5ef2c626 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:52 +0200 Subject: [PATCH 09/65] platform/x86: asus-laptop: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: "Luke D. Jones" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-3-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-laptop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index ccb33d034e2a..9d7e6b712abf 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -818,7 +817,7 @@ static int asus_backlight_init(struct asus_laptop *asus) asus->backlight_device = bd; bd->props.brightness = asus_read_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = BACKLIGHT_POWER_ON; backlight_update_status(bd); return 0; } From a04c5547a69d218d265e4a95faaf03a84493be96 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:53 +0200 Subject: [PATCH 10/65] platform/x86: asus-nb-wmi: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: "Luke D. Jones" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-4-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-nb-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index fceffe2082ec..e20e241bda98 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -7,12 +7,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include #include #include -#include #include #include @@ -525,7 +525,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) dmi_check_system(asus_quirks); driver->quirks = quirks; - driver->panel_power = FB_BLANK_UNBLANK; + driver->panel_power = BACKLIGHT_POWER_ON; /* overwrite the wapf setting if the wapf paramater is specified */ if (wapf != -1) From a406bb7e086aa21164da72ab90f09e3167ded800 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:54 +0200 Subject: [PATCH 11/65] platform/x86: asus-wmi: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: "Luke D. Jones" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-5-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 35ebb37f68bf..9c6b3937ac71 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -3883,7 +3882,7 @@ static int read_backlight_power(struct asus_wmi *asus) if (ret < 0) return ret; - return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; + return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; } static int read_brightness_max(struct asus_wmi *asus) @@ -3942,7 +3941,7 @@ static int update_bl_status(struct backlight_device *bd) power = read_backlight_power(asus); if (power != -ENODEV && bd->props.power != power) { - ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); + ctrl_param = !!(bd->props.power == BACKLIGHT_POWER_ON); err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, ctrl_param, NULL); if (asus->driver->quirks->store_backlight_power) @@ -4001,7 +4000,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) power = read_backlight_power(asus); if (power == -ENODEV) - power = FB_BLANK_UNBLANK; + power = BACKLIGHT_POWER_ON; else if (power < 0) return power; @@ -4059,7 +4058,7 @@ static int read_screenpad_backlight_power(struct asus_wmi *asus) if (ret < 0) return ret; /* 1 == powered */ - return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; + return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; } static int read_screenpad_brightness(struct backlight_device *bd) @@ -4072,7 +4071,7 @@ static int read_screenpad_brightness(struct backlight_device *bd) if (err < 0) return err; /* The device brightness can only be read if powered, so return stored */ - if (err == FB_BLANK_POWERDOWN) + if (err == BACKLIGHT_POWER_OFF) return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval); @@ -4093,7 +4092,7 @@ static int update_screenpad_bl_status(struct backlight_device *bd) return power; if (bd->props.power != power) { - if (power != FB_BLANK_UNBLANK) { + if (power != BACKLIGHT_POWER_ON) { /* Only brightness > 0 can power it back on */ ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, @@ -4101,7 +4100,7 @@ static int update_screenpad_bl_status(struct backlight_device *bd) } else { err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); } - } else if (power == FB_BLANK_UNBLANK) { + } else if (power == BACKLIGHT_POWER_ON) { /* Only set brightness if powered on or we get invalid/unsync state */ ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL); @@ -4131,7 +4130,7 @@ static int asus_screenpad_init(struct asus_wmi *asus) if (power < 0) return power; - if (power != FB_BLANK_POWERDOWN) { + if (power != BACKLIGHT_POWER_OFF) { err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); if (err < 0) return err; From 902c0863936e0c435d6e8fa6754246fab48f020c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:55 +0200 Subject: [PATCH 12/65] platform/x86: eeepc-laptop: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: "Luke D. Jones" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-6-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/eeepc-laptop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 447364bed249..03319a80e114 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -1137,7 +1136,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc) } eeepc->backlight_device = bd; bd->props.brightness = read_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = BACKLIGHT_POWER_ON; backlight_update_status(bd); return 0; } From 1df0015074c973dd9aa51716fb9b7918c5dde717 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:56 +0200 Subject: [PATCH 13/65] platform/x86: eeepc-wmi: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: "Luke D. Jones" Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-7-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/eeepc-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 32d9f0ba6be3..37edb9ae67b8 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -13,13 +13,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include #include #include #include -#include #include #include "asus-wmi.h" @@ -192,7 +192,7 @@ static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) driver->quirks = quirks; driver->quirks->wapf = -1; - driver->panel_power = FB_BLANK_UNBLANK; + driver->panel_power = BACKLIGHT_POWER_ON; } static struct asus_wmi_driver asus_wmi_driver = { From 6ecf83eaf902254f5c3195faf65f4a00b7d05749 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:57 +0200 Subject: [PATCH 14/65] platform/x86: fujitsu-laptop: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Jonathan Woithe Cc: Hans de Goede Cc: "Ilpo Järvinen" Acked-by: Jonathan Woithe Link: https://lore.kernel.org/r/20240731125220.1147348-8-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/fujitsu-laptop.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 968fc91bd5e4..ae992ac1ab4a 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -356,7 +355,7 @@ static int bl_get_brightness(struct backlight_device *b) { struct acpi_device *device = bl_get_data(b); - return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device); + return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(device); } static int bl_update_status(struct backlight_device *b) @@ -364,7 +363,7 @@ static int bl_update_status(struct backlight_device *b) struct acpi_device *device = bl_get_data(b); if (fext) { - if (b->props.power == FB_BLANK_POWERDOWN) + if (b->props.power == BACKLIGHT_POWER_OFF) call_fext_func(fext, FUNC_BACKLIGHT, 0x1, BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF); else @@ -933,9 +932,9 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) acpi_video_get_backlight_type() == acpi_backlight_vendor) { if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF) - fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN; + fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF; else - fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; + fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON; } ret = acpi_fujitsu_laptop_input_setup(device); From f6619520530417bbef368c4194fc6ec6d224dc2d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:58 +0200 Subject: [PATCH 15/65] platform/x86: ideapad-laptop: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Ike Panhc Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-9-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 1ace711f7442..8270925db03f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -1256,7 +1255,7 @@ static int ideapad_backlight_update_status(struct backlight_device *blightdev) return err; err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER, - blightdev->props.power != FB_BLANK_POWERDOWN); + blightdev->props.power != BACKLIGHT_POWER_OFF); if (err) return err; @@ -1306,7 +1305,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv) priv->blightdev = blightdev; blightdev->props.brightness = now; - blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; + blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; backlight_update_status(blightdev); @@ -1330,7 +1329,7 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power)) return; - blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; + blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; } static void ideapad_backlight_notify_brightness(struct ideapad_private *priv) From b780aaffb16ca41819ebd4a3ed0635ea35b93901 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:50:59 +0200 Subject: [PATCH 16/65] platform/x86: oaktrail: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-10-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/oaktrail.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/oaktrail.c b/drivers/platform/x86/intel/oaktrail.c index 217630f40c3f..265cef327b4f 100644 --- a/drivers/platform/x86/intel/oaktrail.c +++ b/drivers/platform/x86/intel/oaktrail.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -250,7 +249,7 @@ static int oaktrail_backlight_init(void) oaktrail_bl_device = bd; bd->props.brightness = get_backlight_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = BACKLIGHT_POWER_ON; backlight_update_status(bd); return 0; From 523b1c036ba970beaac3584f92a367be367f9146 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 31 Jul 2024 14:51:00 +0200 Subject: [PATCH 17/65] platform/x86: samsung-laptop: Use backlight power constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace FB_BLANK_ constants with their counterparts from the backlight subsystem. The values are identical, so there's no change in functionality or semantics. Signed-off-by: Thomas Zimmermann Cc: Corentin Chary Cc: Hans de Goede Cc: "Ilpo Järvinen" Link: https://lore.kernel.org/r/20240731125220.1147348-11-tzimmermann@suse.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/samsung-laptop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 3d2f8e758369..0d3e3ca20b1b 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -554,7 +553,7 @@ static int update_status(struct backlight_device *bd) set_brightness(samsung, bd->props.brightness); - if (bd->props.power == FB_BLANK_UNBLANK) + if (bd->props.power == BACKLIGHT_POWER_ON) sabi_set_commandb(samsung, commands->set_backlight, 1); else sabi_set_commandb(samsung, commands->set_backlight, 0); @@ -1189,7 +1188,7 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung) samsung->backlight_device = bd; samsung->backlight_device->props.brightness = read_brightness(samsung); - samsung->backlight_device->props.power = FB_BLANK_UNBLANK; + samsung->backlight_device->props.power = BACKLIGHT_POWER_ON; backlight_update_status(samsung->backlight_device); return 0; From 440814caedb0e33c56f0478d7fa5b54479013904 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 31 Jul 2024 11:42:56 -0700 Subject: [PATCH 18/65] platform/x86: ISST: Simplify isst_misc_reg() and isst_misc_unreg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit '1630dc626c87 ("platform/x86: ISST: Add model specific loading for common module")' isst_misc_reg() and isst_misc_unreg() can be simplified. Since these functions are only called during module_init() and module_exit() respectively, there is no contention while calling misc_register()/misc_deregister or isst_if_cpu_info_init()/ isst_if_cpu_info_exit(). Hence remove mutex and reference counting. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240731184256.1852840-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../intel/speed_select_if/isst_if_common.c | 40 +++++-------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 10e21563fa46..9ad35fefea47 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -651,10 +651,6 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, /* Lock to prevent module registration when already opened by user space */ static DEFINE_MUTEX(punit_misc_dev_open_lock); -/* Lock to allow one shared misc device for all ISST interfaces */ -static DEFINE_MUTEX(punit_misc_dev_reg_lock); -static int misc_usage_count; -static int misc_device_ret; static int misc_device_open; static int isst_if_open(struct inode *inode, struct file *file) @@ -720,39 +716,23 @@ static struct miscdevice isst_if_char_driver = { static int isst_misc_reg(void) { - mutex_lock(&punit_misc_dev_reg_lock); - if (misc_device_ret) - goto unlock_exit; + int ret; - if (!misc_usage_count) { - misc_device_ret = isst_if_cpu_info_init(); - if (misc_device_ret) - goto unlock_exit; + ret = isst_if_cpu_info_init(); + if (ret) + return ret; - misc_device_ret = misc_register(&isst_if_char_driver); - if (misc_device_ret) { - isst_if_cpu_info_exit(); - goto unlock_exit; - } - } - misc_usage_count++; + ret = misc_register(&isst_if_char_driver); + if (ret) + isst_if_cpu_info_exit(); -unlock_exit: - mutex_unlock(&punit_misc_dev_reg_lock); - - return misc_device_ret; + return ret; } static void isst_misc_unreg(void) { - mutex_lock(&punit_misc_dev_reg_lock); - if (misc_usage_count) - misc_usage_count--; - if (!misc_usage_count && !misc_device_ret) { - misc_deregister(&isst_if_char_driver); - isst_if_cpu_info_exit(); - } - mutex_unlock(&punit_misc_dev_reg_lock); + misc_deregister(&isst_if_char_driver); + isst_if_cpu_info_exit(); } /** From 7e597d496dfd69c8940a924bc2cc96f1666d33a9 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 1 Aug 2024 05:18:11 +0000 Subject: [PATCH 19/65] platform/x86/intel/ifs: Refactor MSR usage in IFS test code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IFS tests such as Scan at Field (SAF) or Structural Based Functional Test at Field (SBAF), require the user to load a test image. The image loading process is similar across these tests, with the only difference being MSR addresses used. To reuse the code between these tests, remove the hard coding of MSR addresses and allow the driver to pass the MSR addresses per IFS test (via driver device data). Add a new structure named "struct ifs_test_msrs" to specify the test-specific MSR addresses. Each IFS test will provide this structure, enabling them to reuse the common code. This is a preliminary patch in preparation for the addition of SBAF support. Reviewed-by: Ashok Raj Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Signed-off-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240801051814.1935149-2-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 9 +++++++++ drivers/platform/x86/intel/ifs/ifs.h | 25 +++++++++++++++++++++++++ drivers/platform/x86/intel/ifs/load.c | 24 ++++++++++++++---------- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 33412a584836..f204ebbbf769 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -39,9 +39,18 @@ static const struct ifs_test_caps array_test = { .test_num = IFS_TYPE_ARRAY_BIST, }; +static const struct ifs_test_msrs scan_msrs = { + .copy_hashes = MSR_COPY_SCAN_HASHES, + .copy_hashes_status = MSR_SCAN_HASHES_STATUS, + .copy_chunks = MSR_AUTHENTICATE_AND_COPY_CHUNK, + .copy_chunks_status = MSR_CHUNKS_AUTHENTICATION_STATUS, + .test_ctrl = MSR_SAF_CTRL, +}; + static struct ifs_device ifs_devices[] = { [IFS_TYPE_SAF] = { .test_caps = &scan_test, + .test_msrs = &scan_msrs, .misc = { .name = "intel_ifs_0", .minor = MISC_DYNAMIC_MINOR, diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 56b9f3e3cf76..738cbc7a5d00 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -266,6 +266,22 @@ struct ifs_test_caps { int test_num; }; +/** + * struct ifs_test_msrs - MSRs used in IFS tests + * @copy_hashes: Copy test hash data + * @copy_hashes_status: Status of copied test hash data + * @copy_chunks: Copy chunks of the test data + * @copy_chunks_status: Status of the copied test data chunks + * @test_ctrl: Control the test attributes + */ +struct ifs_test_msrs { + u32 copy_hashes; + u32 copy_hashes_status; + u32 copy_chunks; + u32 copy_chunks_status; + u32 test_ctrl; +}; + /** * struct ifs_data - attributes related to intel IFS driver * @loaded_version: stores the currently loaded ifs image version. @@ -299,6 +315,7 @@ struct ifs_work { struct ifs_device { const struct ifs_test_caps *test_caps; + const struct ifs_test_msrs *test_msrs; struct ifs_data rw_data; struct miscdevice misc; }; @@ -319,6 +336,14 @@ static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev) return d->test_caps; } +static inline const struct ifs_test_msrs *ifs_get_test_msrs(struct device *dev) +{ + struct miscdevice *m = dev_get_drvdata(dev); + struct ifs_device *d = container_of(m, struct ifs_device, misc); + + return d->test_msrs; +} + extern bool *ifs_pkg_auth; int ifs_load_firmware(struct device *dev); int do_core_test(int cpu, struct device *dev); diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 39f19cb51749..ad0c107f0922 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -118,15 +118,17 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work) union ifs_scan_hashes_status hashes_status; union ifs_chunks_auth_status chunk_status; struct device *dev = local_work->dev; + const struct ifs_test_msrs *msrs; int i, num_chunks, chunk_size; struct ifs_data *ifsd; u64 linear_addr, base; u32 err_code; ifsd = ifs_get_data(dev); + msrs = ifs_get_test_msrs(dev); /* run scan hash copy */ - wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); - rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); + wrmsrl(msrs->copy_hashes, ifs_hash_ptr); + rdmsrl(msrs->copy_hashes_status, hashes_status.data); /* enumerate the scan image information */ num_chunks = hashes_status.num_chunks; @@ -147,8 +149,8 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work) linear_addr = base + i * chunk_size; linear_addr |= i; - wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, linear_addr); - rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); + wrmsrl(msrs->copy_chunks, linear_addr); + rdmsrl(msrs->copy_chunks_status, chunk_status.data); ifsd->valid_chunks = chunk_status.valid_chunks; err_code = chunk_status.error_code; @@ -180,6 +182,7 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev) union ifs_scan_hashes_status_gen2 hashes_status; union ifs_chunks_auth_status_gen2 chunk_status; u32 err_code, valid_chunks, total_chunks; + const struct ifs_test_msrs *msrs; int i, num_chunks, chunk_size; union meta_data *ifs_meta; int starting_chunk_nr; @@ -189,10 +192,11 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev) int retry_count; ifsd = ifs_get_data(dev); + msrs = ifs_get_test_msrs(dev); if (need_copy_scan_hashes(ifsd)) { - wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); - rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); + wrmsrl(msrs->copy_hashes, ifs_hash_ptr); + rdmsrl(msrs->copy_hashes_status, hashes_status.data); /* enumerate the scan image information */ chunk_size = hashes_status.chunk_size * SZ_1K; @@ -212,8 +216,8 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev) } if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) { - wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE); - rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); + wrmsrl(msrs->test_ctrl, INVALIDATE_STRIDE); + rdmsrl(msrs->copy_chunks_status, chunk_status.data); if (chunk_status.valid_chunks != 0) { dev_err(dev, "Couldn't invalidate installed stride - %d\n", chunk_status.valid_chunks); @@ -234,9 +238,9 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev) chunk_table[1] = linear_addr; do { local_irq_disable(); - wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table); + wrmsrl(msrs->copy_chunks, (u64)chunk_table); local_irq_enable(); - rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); + rdmsrl(msrs->copy_chunks_status, chunk_status.data); err_code = chunk_status.error_code; } while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count); From 0a3e4e94d137daacd5ec092365080eed847f8f01 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 1 Aug 2024 05:18:12 +0000 Subject: [PATCH 20/65] platform/x86/intel/ifs: Add SBAF test image loading support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Structural Based Functional Test at Field (SBAF) is a new type of testing that provides comprehensive core test coverage complementing existing IFS tests like Scan at Field (SAF) or ArrayBist. SBAF device will appear as a new device instance (intel_ifs_2) under /sys/devices/virtual/misc. The user interaction necessary to load the test image and test a particular core is the same as the existing scan test (intel_ifs_0). During the loading stage, the driver will look for a file named ff-mm-ss-.sbft in the /lib/firmware/intel/ifs_2 directory. The hardware interaction needed for loading the image is similar to SAF, with the only difference being the MSR addresses used. Reuse the SAF image loading code, passing the SBAF-specific MSR addresses via struct ifs_test_msrs in the driver device data. Unlike SAF, the SBAF test image chunks are further divided into smaller logical entities called bundles. Since the SBAF test is initiated per bundle, cache the maximum number of bundles in the current image, which is used for iterating through bundles during SBAF test execution. Reviewed-by: Ashok Raj Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Signed-off-by: Jithu Joseph Co-developed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240801051814.1935149-3-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Hans de Goede --- arch/x86/include/asm/msr-index.h | 2 ++ drivers/platform/x86/intel/ifs/core.c | 24 +++++++++++++++++ drivers/platform/x86/intel/ifs/ifs.h | 37 ++++++++++++++++++++++++++- drivers/platform/x86/intel/ifs/load.c | 16 +++++++++--- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 82c6a4d350e0..a7c06a46fb76 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -247,6 +247,8 @@ #define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT) #define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) +#define MSR_INTEGRITY_CAPS_SBAF_BIT 8 +#define MSR_INTEGRITY_CAPS_SBAF BIT(MSR_INTEGRITY_CAPS_SBAF_BIT) #define MSR_INTEGRITY_CAPS_SAF_GEN_MASK GENMASK_ULL(10, 9) #define MSR_LBR_NHM_FROM 0x00000680 diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index f204ebbbf769..bc252b883210 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -32,6 +32,7 @@ bool *ifs_pkg_auth; static const struct ifs_test_caps scan_test = { .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, .test_num = IFS_TYPE_SAF, + .image_suffix = "scan", }; static const struct ifs_test_caps array_test = { @@ -47,6 +48,20 @@ static const struct ifs_test_msrs scan_msrs = { .test_ctrl = MSR_SAF_CTRL, }; +static const struct ifs_test_msrs sbaf_msrs = { + .copy_hashes = MSR_COPY_SBAF_HASHES, + .copy_hashes_status = MSR_SBAF_HASHES_STATUS, + .copy_chunks = MSR_AUTHENTICATE_AND_COPY_SBAF_CHUNK, + .copy_chunks_status = MSR_SBAF_CHUNKS_AUTHENTICATION_STATUS, + .test_ctrl = MSR_SBAF_CTRL, +}; + +static const struct ifs_test_caps sbaf_test = { + .integrity_cap_bit = MSR_INTEGRITY_CAPS_SBAF_BIT, + .test_num = IFS_TYPE_SBAF, + .image_suffix = "sbft", +}; + static struct ifs_device ifs_devices[] = { [IFS_TYPE_SAF] = { .test_caps = &scan_test, @@ -65,6 +80,15 @@ static struct ifs_device ifs_devices[] = { .groups = plat_ifs_array_groups, }, }, + [IFS_TYPE_SBAF] = { + .test_caps = &sbaf_test, + .test_msrs = &sbaf_msrs, + .misc = { + .name = "intel_ifs_2", + .minor = MISC_DYNAMIC_MINOR, + .groups = plat_ifs_groups, + }, + }, }; #define IFS_NUMTESTS ARRAY_SIZE(ifs_devices) diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 738cbc7a5d00..600bb8a1b285 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -126,11 +126,38 @@ * The driver does not make use of this, it only tests one core at a time. * * .. [#f1] https://github.com/intel/TBD + * + * + * Structural Based Functional Test at Field (SBAF): + * ------------------------------------------------ + * + * SBAF is a new type of testing that provides comprehensive core test + * coverage complementing Scan at Field (SAF) testing. SBAF mimics the + * manufacturing screening environment and leverages the same test suite. + * It makes use of Design For Test (DFT) observation sites and features + * to maximize coverage in minimum time. + * + * Similar to the SAF test, SBAF isolates the core under test from the + * rest of the system during execution. Upon completion, the core + * seamlessly resets to its pre-test state and resumes normal operation. + * Any machine checks or hangs encountered during the test are confined to + * the isolated core, preventing disruption to the overall system. + * + * Like the SAF test, the SBAF test is also divided into multiple batches, + * and each batch test can take hundreds of milliseconds (100-200 ms) to + * complete. If such a lengthy interruption is undesirable, it is + * recommended to relocate the time-sensitive applications to other cores. */ #include #include #define MSR_ARRAY_BIST 0x00000105 + +#define MSR_COPY_SBAF_HASHES 0x000002b8 +#define MSR_SBAF_HASHES_STATUS 0x000002b9 +#define MSR_AUTHENTICATE_AND_COPY_SBAF_CHUNK 0x000002ba +#define MSR_SBAF_CHUNKS_AUTHENTICATION_STATUS 0x000002bb + #define MSR_COPY_SCAN_HASHES 0x000002c2 #define MSR_SCAN_HASHES_STATUS 0x000002c3 #define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4 @@ -140,6 +167,7 @@ #define MSR_ARRAY_TRIGGER 0x000002d6 #define MSR_ARRAY_STATUS 0x000002d7 #define MSR_SAF_CTRL 0x000004f0 +#define MSR_SBAF_CTRL 0x000004f8 #define SCAN_NOT_TESTED 0 #define SCAN_TEST_PASS 1 @@ -147,6 +175,7 @@ #define IFS_TYPE_SAF 0 #define IFS_TYPE_ARRAY_BIST 1 +#define IFS_TYPE_SBAF 2 #define ARRAY_GEN0 0 #define ARRAY_GEN1 1 @@ -196,7 +225,8 @@ union ifs_chunks_auth_status_gen2 { u16 valid_chunks; u16 total_chunks; u32 error_code :8; - u32 rsvd2 :24; + u32 rsvd2 :8; + u32 max_bundle :16; }; }; @@ -261,9 +291,12 @@ union ifs_array { #define IFS_SW_TIMEOUT 0xFD #define IFS_SW_PARTIAL_COMPLETION 0xFE +#define IFS_SUFFIX_SZ 5 + struct ifs_test_caps { int integrity_cap_bit; int test_num; + char image_suffix[IFS_SUFFIX_SZ]; }; /** @@ -294,6 +327,7 @@ struct ifs_test_msrs { * @generation: IFS test generation enumerated by hardware * @chunk_size: size of a test chunk * @array_gen: test generation of array test + * @max_bundle: maximum bundle index */ struct ifs_data { int loaded_version; @@ -306,6 +340,7 @@ struct ifs_data { u32 generation; u32 chunk_size; u32 array_gen; + u32 max_bundle; }; struct ifs_work { diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index ad0c107f0922..de54bd1a5970 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -261,20 +261,22 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev) return -EIO; } ifsd->valid_chunks = valid_chunks; + ifsd->max_bundle = chunk_status.max_bundle; return 0; } static int validate_ifs_metadata(struct device *dev) { + const struct ifs_test_caps *test = ifs_get_test_caps(dev); struct ifs_data *ifsd = ifs_get_data(dev); union meta_data *ifs_meta; char test_file[64]; int ret = -EINVAL; - snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.scan", + snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.%s", boot_cpu_data.x86, boot_cpu_data.x86_model, - boot_cpu_data.x86_stepping, ifsd->cur_batch); + boot_cpu_data.x86_stepping, ifsd->cur_batch, test->image_suffix); ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); if (!ifs_meta) { @@ -304,6 +306,12 @@ static int validate_ifs_metadata(struct device *dev) return ret; } + if (ifs_meta->test_type != test->test_num) { + dev_warn(dev, "Metadata test_type %d mismatches with device type\n", + ifs_meta->test_type); + return ret; + } + return 0; } @@ -391,9 +399,9 @@ int ifs_load_firmware(struct device *dev) char scan_path[64]; int ret; - snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", + snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.%s", test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, - boot_cpu_data.x86_stepping, ifsd->cur_batch); + boot_cpu_data.x86_stepping, ifsd->cur_batch, test->image_suffix); ret = request_firmware_direct(&fw, scan_path, dev); if (ret) { From 3c4d06bd6e3713235fba5aa5eed9d1898239ec1f Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 1 Aug 2024 05:18:13 +0000 Subject: [PATCH 21/65] platform/x86/intel/ifs: Add SBAF test support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a core, the SBAF test engine is shared between sibling CPUs. An SBAF test image contains multiple bundles. Each bundle is further composed of subunits called programs. When a SBAF test (for a particular core) is triggered by the user, each SBAF bundle from the loaded test image is executed sequentially on all the threads on the core using the stop_core_cpuslocked mechanism. Each bundle execution is initiated by writing to MSR_ACTIVATE_SBAF. SBAF test bundle execution may be aborted when an interrupt occurs or if the CPU does not have enough power budget for the test. In these cases the kernel restarts the test from the aborted bundle. SBAF execution is not retried if the test fails or if the test makes no forward progress after 5 retries. Reviewed-by: Ashok Raj Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Signed-off-by: Jithu Joseph Signed-off-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240801051814.1935149-4-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/ifs.h | 30 +++ drivers/platform/x86/intel/ifs/runtest.c | 232 +++++++++++++++++++++++ 2 files changed, 262 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 600bb8a1b285..b261be46bce8 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -157,6 +157,8 @@ #define MSR_SBAF_HASHES_STATUS 0x000002b9 #define MSR_AUTHENTICATE_AND_COPY_SBAF_CHUNK 0x000002ba #define MSR_SBAF_CHUNKS_AUTHENTICATION_STATUS 0x000002bb +#define MSR_ACTIVATE_SBAF 0x000002bc +#define MSR_SBAF_STATUS 0x000002bd #define MSR_COPY_SCAN_HASHES 0x000002c2 #define MSR_SCAN_HASHES_STATUS 0x000002c3 @@ -283,6 +285,34 @@ union ifs_array { }; }; +/* MSR_ACTIVATE_SBAF bit fields */ +union ifs_sbaf { + u64 data; + struct { + u32 bundle_idx :9; + u32 rsvd1 :5; + u32 pgm_idx :2; + u32 rsvd2 :16; + u32 delay :31; + u32 sigmce :1; + }; +}; + +/* MSR_SBAF_STATUS bit fields */ +union ifs_sbaf_status { + u64 data; + struct { + u32 bundle_idx :9; + u32 rsvd1 :5; + u32 pgm_idx :2; + u32 rsvd2 :16; + u32 error_code :8; + u32 rsvd3 :21; + u32 test_fail :1; + u32 sbaf_status :2; + }; +}; + /* * Driver populated error-codes * 0xFD: Test timed out before completing all the chunks. diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 282e4bfe30da..2a37f009d0b3 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -29,6 +29,13 @@ struct run_params { union ifs_status status; }; +struct sbaf_run_params { + struct ifs_data *ifsd; + int *retry_cnt; + union ifs_sbaf *activate; + union ifs_sbaf_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). @@ -146,6 +153,7 @@ static bool can_restart(union ifs_status status) #define SPINUNIT 100 /* 100 nsec */ static atomic_t array_cpus_in; static atomic_t scan_cpus_in; +static atomic_t sbaf_cpus_in; /* * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() @@ -387,6 +395,224 @@ static void ifs_array_test_gen1(int cpu, struct device *dev) ifsd->status = SCAN_TEST_PASS; } +#define SBAF_STATUS_PASS 0 +#define SBAF_STATUS_SIGN_FAIL 1 +#define SBAF_STATUS_INTR 2 +#define SBAF_STATUS_TEST_FAIL 3 + +enum sbaf_status_err_code { + IFS_SBAF_NO_ERROR = 0, + IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN = 1, + IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS = 2, + IFS_SBAF_UNASSIGNED_ERROR_CODE3 = 3, + IFS_SBAF_INVALID_BUNDLE_INDEX = 4, + IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS = 5, + IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY = 6, + IFS_SBAF_UNASSIGNED_ERROR_CODE7 = 7, + IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT = 8, + IFS_SBAF_INTERRUPTED_DURING_EXECUTION = 9, + IFS_SBAF_INVALID_PROGRAM_INDEX = 0xA, + IFS_SBAF_CORRUPTED_CHUNK = 0xB, + IFS_SBAF_DID_NOT_START = 0xC, +}; + +static const char * const sbaf_test_status[] = { + [IFS_SBAF_NO_ERROR] = "SBAF no error", + [IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN] = "Other thread could not join.", + [IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS] = "Interrupt occurred prior to SBAF coordination.", + [IFS_SBAF_UNASSIGNED_ERROR_CODE3] = "Unassigned error code 0x3", + [IFS_SBAF_INVALID_BUNDLE_INDEX] = "Non-valid sbaf bundles. Reload test image", + [IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS] = "Mismatch in arguments between threads T0/T1.", + [IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY] = "Core not capable of performing SBAF currently", + [IFS_SBAF_UNASSIGNED_ERROR_CODE7] = "Unassigned error code 0x7", + [IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT] = "Exceeded number of Logical Processors (LP) allowed to run Scan-At-Field concurrently", + [IFS_SBAF_INTERRUPTED_DURING_EXECUTION] = "Interrupt occurred prior to SBAF start", + [IFS_SBAF_INVALID_PROGRAM_INDEX] = "SBAF program index not valid", + [IFS_SBAF_CORRUPTED_CHUNK] = "SBAF operation aborted due to corrupted chunk", + [IFS_SBAF_DID_NOT_START] = "SBAF operation did not start", +}; + +static void sbaf_message_not_tested(struct device *dev, int cpu, u64 status_data) +{ + union ifs_sbaf_status status = (union ifs_sbaf_status)status_data; + + if (status.error_code < ARRAY_SIZE(sbaf_test_status)) { + dev_info(dev, "CPU(s) %*pbl: SBAF operation did not start. %s\n", + cpumask_pr_args(cpu_smt_mask(cpu)), + sbaf_test_status[status.error_code]); + } else if (status.error_code == IFS_SW_TIMEOUT) { + dev_info(dev, "CPU(s) %*pbl: software timeout during scan\n", + cpumask_pr_args(cpu_smt_mask(cpu))); + } else if (status.error_code == IFS_SW_PARTIAL_COMPLETION) { + dev_info(dev, "CPU(s) %*pbl: %s\n", + cpumask_pr_args(cpu_smt_mask(cpu)), + "Not all SBAF bundles executed. Maximum forward progress retries exceeded"); + } else { + dev_info(dev, "CPU(s) %*pbl: SBAF unknown status %llx\n", + cpumask_pr_args(cpu_smt_mask(cpu)), status.data); + } +} + +static void sbaf_message_fail(struct device *dev, int cpu, union ifs_sbaf_status status) +{ + /* Failed signature check is set when SBAF signature did not match the expected value */ + if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL) { + dev_err(dev, "CPU(s) %*pbl: Failed signature check\n", + cpumask_pr_args(cpu_smt_mask(cpu))); + } + + /* Failed to reach end of test */ + if (status.sbaf_status == SBAF_STATUS_TEST_FAIL) { + dev_err(dev, "CPU(s) %*pbl: Failed to complete test\n", + cpumask_pr_args(cpu_smt_mask(cpu))); + } +} + +static bool sbaf_bundle_completed(union ifs_sbaf_status status) +{ + return !(status.sbaf_status || status.error_code); +} + +static bool sbaf_can_restart(union ifs_sbaf_status status) +{ + enum sbaf_status_err_code err_code = status.error_code; + + /* Signature for chunk is bad, or scan test failed */ + if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL || + status.sbaf_status == SBAF_STATUS_TEST_FAIL) + return false; + + switch (err_code) { + case IFS_SBAF_NO_ERROR: + case IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN: + case IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS: + case IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT: + case IFS_SBAF_INTERRUPTED_DURING_EXECUTION: + return true; + case IFS_SBAF_UNASSIGNED_ERROR_CODE3: + case IFS_SBAF_INVALID_BUNDLE_INDEX: + case IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS: + case IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY: + case IFS_SBAF_UNASSIGNED_ERROR_CODE7: + case IFS_SBAF_INVALID_PROGRAM_INDEX: + case IFS_SBAF_CORRUPTED_CHUNK: + case IFS_SBAF_DID_NOT_START: + break; + } + return false; +} + +/* + * Execute the SBAF test. Called "simultaneously" on all threads of a core + * at high priority using the stop_cpus mechanism. + */ +static int dosbaf(void *data) +{ + struct sbaf_run_params *run_params = data; + int cpu = smp_processor_id(); + union ifs_sbaf_status status; + struct ifs_data *ifsd; + int first; + + ifsd = run_params->ifsd; + + /* Only the first logical CPU on a core reports result */ + first = cpumask_first(cpu_smt_mask(cpu)); + wait_for_sibling_cpu(&sbaf_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 + * starts scan of each requested bundle. The core test happens + * during the "execution" of the WRMSR. + */ + wrmsrl(MSR_ACTIVATE_SBAF, run_params->activate->data); + rdmsrl(MSR_SBAF_STATUS, status.data); + + /* Pass back the result of the test */ + if (cpu == first) + run_params->status = status; + + return 0; +} + +static void ifs_sbaf_test_core(int cpu, struct device *dev) +{ + struct sbaf_run_params run_params; + union ifs_sbaf_status status = {}; + union ifs_sbaf activate; + unsigned long timeout; + struct ifs_data *ifsd; + int stop_bundle; + int retries; + + ifsd = ifs_get_data(dev); + + activate.data = 0; + activate.delay = IFS_THREAD_WAIT; + + timeout = jiffies + 2 * HZ; + retries = MAX_IFS_RETRIES; + activate.bundle_idx = 0; + stop_bundle = ifsd->max_bundle; + + while (activate.bundle_idx <= stop_bundle) { + if (time_after(jiffies, timeout)) { + status.error_code = IFS_SW_TIMEOUT; + break; + } + + atomic_set(&sbaf_cpus_in, 0); + + run_params.ifsd = ifsd; + run_params.activate = &activate; + run_params.retry_cnt = &retries; + stop_core_cpuslocked(cpu, dosbaf, &run_params); + + status = run_params.status; + + if (sbaf_bundle_completed(status)) { + activate.bundle_idx = status.bundle_idx + 1; + activate.pgm_idx = 0; + retries = MAX_IFS_RETRIES; + continue; + } + + /* Some cases can be retried, give up for others */ + if (!sbaf_can_restart(status)) + break; + + if (status.pgm_idx == activate.pgm_idx) { + /* If no progress retry */ + if (--retries == 0) { + if (status.error_code == IFS_NO_ERROR) + status.error_code = IFS_SW_PARTIAL_COMPLETION; + break; + } + } else { + /* if some progress, more pgms remaining in bundle, reset retries */ + retries = MAX_IFS_RETRIES; + activate.bundle_idx = status.bundle_idx; + activate.pgm_idx = status.pgm_idx; + } + } + + /* Update status for this core */ + ifsd->scan_details = status.data; + + if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL || + status.sbaf_status == SBAF_STATUS_TEST_FAIL) { + ifsd->status = SCAN_TEST_FAIL; + sbaf_message_fail(dev, cpu, status); + } else if (status.error_code || status.sbaf_status == SBAF_STATUS_INTR || + (activate.bundle_idx < stop_bundle)) { + ifsd->status = SCAN_NOT_TESTED; + sbaf_message_not_tested(dev, cpu, status.data); + } else { + ifsd->status = SCAN_TEST_PASS; + } +} + /* * Initiate per core test. It wakes up work queue threads on the target cpu and * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and @@ -420,6 +646,12 @@ int do_core_test(int cpu, struct device *dev) else ifs_array_test_gen1(cpu, dev); break; + case IFS_TYPE_SBAF: + if (!ifsd->loaded) + ret = -EPERM; + else + ifs_sbaf_test_core(cpu, dev); + break; default: ret = -EINVAL; } From 61b74964536e86445d43acff5cff6ad907ba9321 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 1 Aug 2024 05:18:14 +0000 Subject: [PATCH 22/65] trace: platform/x86/intel/ifs: Add SBAF trace support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracing support for the SBAF IFS tests, which may be useful for debugging systems that fail these tests. Log details like test content batch number, SBAF bundle ID, program index and the exact errors or warnings encountered by each HT thread during the test. Reviewed-by: Ashok Raj Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Reviewed-by: Steven Rostedt (Google) Signed-off-by: Jithu Joseph Signed-off-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240801051814.1935149-5-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/runtest.c | 1 + include/trace/events/intel_ifs.h | 27 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 2a37f009d0b3..7670fc89153d 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -528,6 +528,7 @@ static int dosbaf(void *data) */ wrmsrl(MSR_ACTIVATE_SBAF, run_params->activate->data); rdmsrl(MSR_SBAF_STATUS, status.data); + trace_ifs_sbaf(ifsd->cur_batch, *run_params->activate, status); /* Pass back the result of the test */ if (cpu == first) diff --git a/include/trace/events/intel_ifs.h b/include/trace/events/intel_ifs.h index 0d88ebf2c980..70323acde1de 100644 --- a/include/trace/events/intel_ifs.h +++ b/include/trace/events/intel_ifs.h @@ -35,6 +35,33 @@ TRACE_EVENT(ifs_status, __entry->status) ); +TRACE_EVENT(ifs_sbaf, + + TP_PROTO(int batch, union ifs_sbaf activate, union ifs_sbaf_status status), + + TP_ARGS(batch, activate, status), + + TP_STRUCT__entry( + __field( u64, status ) + __field( int, batch ) + __field( u16, bundle ) + __field( u16, pgm ) + ), + + TP_fast_assign( + __entry->status = status.data; + __entry->batch = batch; + __entry->bundle = activate.bundle_idx; + __entry->pgm = activate.pgm_idx; + ), + + TP_printk("batch: 0x%.2x, bundle_idx: 0x%.4x, pgm_idx: 0x%.4x, status: 0x%.16llx", + __entry->batch, + __entry->bundle, + __entry->pgm, + __entry->status) +); + #endif /* _TRACE_IFS_H */ /* This part must be outside protection */ From 6c1fa8edfef815a97db57f30216265bfa152792d Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Thu, 25 Jul 2024 11:21:09 +0200 Subject: [PATCH 23/65] platform/x86: ideapad-laptop: move ACPI helpers from header to source file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since moving ymc_trigger_ec from lenovo-ymc to ideapad-laptop, only the latter uses these definitions and functions. Move them back to source file. Signed-off-by: Gergo Koteles Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/57a48d2582b567f6c6fbcd3b379e17aee0fb5a94.1721898747.git.soyer@irl.hu Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 136 +++++++++++++++++++++++++ drivers/platform/x86/ideapad-laptop.h | 139 -------------------------- 2 files changed, 136 insertions(+), 139 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 2020d8db97d9..35c75bcff195 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,34 @@ enum { SALS_FNLOCK_OFF = 0xf, }; +enum { + VPCCMD_R_VPC1 = 0x10, + VPCCMD_R_BL_MAX, + VPCCMD_R_BL, + VPCCMD_W_BL, + VPCCMD_R_WIFI, + VPCCMD_W_WIFI, + VPCCMD_R_BT, + VPCCMD_W_BT, + VPCCMD_R_BL_POWER, + VPCCMD_R_NOVO, + VPCCMD_R_VPC2, + VPCCMD_R_TOUCHPAD, + VPCCMD_W_TOUCHPAD, + VPCCMD_R_CAMERA, + VPCCMD_W_CAMERA, + VPCCMD_R_3G, + VPCCMD_W_3G, + VPCCMD_R_ODD, /* 0x21 */ + VPCCMD_W_FAN, + VPCCMD_R_RF, + VPCCMD_W_RF, + VPCCMD_W_YMC = 0x2A, + VPCCMD_R_FAN = 0x2B, + VPCCMD_R_SPECIAL_BUTTONS = 0x31, + VPCCMD_W_BL_POWER = 0x33, +}; + /* * These correspond to the number of supported states - 1 * Future keyboard types may need a new system, if there's a collision @@ -236,6 +265,7 @@ static void ideapad_shared_exit(struct ideapad_private *priv) /* * ACPI Helpers */ +#define IDEAPAD_EC_TIMEOUT 200 /* in ms */ static int eval_int(acpi_handle handle, const char *name, unsigned long *res) { @@ -251,6 +281,29 @@ static int eval_int(acpi_handle handle, const char *name, unsigned long *res) return 0; } +static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, + unsigned long *res) +{ + struct acpi_object_list params; + unsigned long long result; + union acpi_object in_obj; + acpi_status status; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = arg; + + status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + if (res) + *res = result; + + return 0; +} + static int exec_simple_method(acpi_handle handle, const char *name, unsigned long arg) { acpi_status status = acpi_execute_simple_method(handle, (char *)name, arg); @@ -293,6 +346,89 @@ static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) return eval_int_with_arg(handle, "DYTC", cmd, res); } +static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) +{ + return eval_int_with_arg(handle, "VPCR", cmd, res); +} + +static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) +{ + struct acpi_object_list params; + union acpi_object in_obj[2]; + acpi_status status; + + params.count = 2; + params.pointer = in_obj; + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = cmd; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = data; + + status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) +{ + unsigned long end_jiffies, val; + int err; + + err = eval_vpcw(handle, 1, cmd); + if (err) + return err; + + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; + + while (time_before(jiffies, end_jiffies)) { + schedule(); + + err = eval_vpcr(handle, 1, &val); + if (err) + return err; + + if (val == 0) + return eval_vpcr(handle, 0, data); + } + + acpi_handle_err(handle, "timeout in %s\n", __func__); + + return -ETIMEDOUT; +} + +static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) +{ + unsigned long end_jiffies, val; + int err; + + err = eval_vpcw(handle, 0, data); + if (err) + return err; + + err = eval_vpcw(handle, 1, cmd); + if (err) + return err; + + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; + + while (time_before(jiffies, end_jiffies)) { + schedule(); + + err = eval_vpcr(handle, 1, &val); + if (err) + return err; + + if (val == 0) + return 0; + } + + acpi_handle_err(handle, "timeout in %s\n", __func__); + + return -ETIMEDOUT; +} + /* * debugfs */ diff --git a/drivers/platform/x86/ideapad-laptop.h b/drivers/platform/x86/ideapad-laptop.h index 948cc61800a9..1e52f2aa0aac 100644 --- a/drivers/platform/x86/ideapad-laptop.h +++ b/drivers/platform/x86/ideapad-laptop.h @@ -9,9 +9,6 @@ #ifndef _IDEAPAD_LAPTOP_H_ #define _IDEAPAD_LAPTOP_H_ -#include -#include -#include #include enum ideapad_laptop_notifier_actions { @@ -22,140 +19,4 @@ int ideapad_laptop_register_notifier(struct notifier_block *nb); int ideapad_laptop_unregister_notifier(struct notifier_block *nb); void ideapad_laptop_call_notifier(unsigned long action, void *data); -enum { - VPCCMD_R_VPC1 = 0x10, - VPCCMD_R_BL_MAX, - VPCCMD_R_BL, - VPCCMD_W_BL, - VPCCMD_R_WIFI, - VPCCMD_W_WIFI, - VPCCMD_R_BT, - VPCCMD_W_BT, - VPCCMD_R_BL_POWER, - VPCCMD_R_NOVO, - VPCCMD_R_VPC2, - VPCCMD_R_TOUCHPAD, - VPCCMD_W_TOUCHPAD, - VPCCMD_R_CAMERA, - VPCCMD_W_CAMERA, - VPCCMD_R_3G, - VPCCMD_W_3G, - VPCCMD_R_ODD, /* 0x21 */ - VPCCMD_W_FAN, - VPCCMD_R_RF, - VPCCMD_W_RF, - VPCCMD_W_YMC = 0x2A, - VPCCMD_R_FAN = 0x2B, - VPCCMD_R_SPECIAL_BUTTONS = 0x31, - VPCCMD_W_BL_POWER = 0x33, -}; - -static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res) -{ - struct acpi_object_list params; - unsigned long long result; - union acpi_object in_obj; - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = arg; - - status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result); - if (ACPI_FAILURE(status)) - return -EIO; - - if (res) - *res = result; - - return 0; -} - -static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) -{ - return eval_int_with_arg(handle, "VPCR", cmd, res); -} - -static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) -{ - struct acpi_object_list params; - union acpi_object in_obj[2]; - acpi_status status; - - params.count = 2; - params.pointer = in_obj; - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = cmd; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = data; - - status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; -} - -#define IDEAPAD_EC_TIMEOUT 200 /* in ms */ - -static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) -{ - unsigned long end_jiffies, val; - int err; - - err = eval_vpcw(handle, 1, cmd); - if (err) - return err; - - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; - - while (time_before(jiffies, end_jiffies)) { - schedule(); - - err = eval_vpcr(handle, 1, &val); - if (err) - return err; - - if (val == 0) - return eval_vpcr(handle, 0, data); - } - - acpi_handle_err(handle, "timeout in %s\n", __func__); - - return -ETIMEDOUT; -} - -static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) -{ - unsigned long end_jiffies, val; - int err; - - err = eval_vpcw(handle, 0, data); - if (err) - return err; - - err = eval_vpcw(handle, 1, cmd); - if (err) - return err; - - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; - - while (time_before(jiffies, end_jiffies)) { - schedule(); - - err = eval_vpcr(handle, 1, &val); - if (err) - return err; - - if (val == 0) - return 0; - } - - acpi_handle_err(handle, "timeout in %s\n", __func__); - - return -ETIMEDOUT; -} - -#undef IDEAPAD_EC_TIMEOUT #endif /* !_IDEAPAD_LAPTOP_H_ */ From e35ee8ee498d68de24e6a831ff5d33665b41a802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 6 Aug 2024 11:30:47 +0300 Subject: [PATCH 24/65] platform/x86: intel/pmc: Remove unused param idx from pmc_for_each_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pmc_for_each_mode() takes i (index) variable name as a parameter but the loop index is not used by any of the callers. Make the index variable internal to pmc_for_each_mode(). This also changes the loop logic such that ->lpm_en_modes[] is never read beyond num_lpm_modes. Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240806083047.1562-1-ilpo.jarvinen@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 18 +++++++----------- drivers/platform/x86/intel/pmc/core.h | 10 ++++++---- drivers/platform/x86/intel/pmc/core_ssram.c | 4 ++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index d70eee6b6b32..692049ba3925 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -728,12 +728,11 @@ static int pmc_core_substate_res_show(struct seq_file *s, void *unused) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2; u32 offset = pmc->map->lpm_residency_offset; - unsigned int i; int mode; seq_printf(s, "%-10s %-15s\n", "Substate", "Residency"); - pmc_for_each_mode(i, mode, pmcdev) { + pmc_for_each_mode(mode, pmcdev) { seq_printf(s, "%-10s %-15llu\n", pmc_lpm_modes[mode], adjust_lpm_residency(pmc, offset + (4 * mode), lpm_adj_x2)); } @@ -787,11 +786,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs); static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index) { struct pmc_dev *pmcdev = s->private; - unsigned int i; int mode; seq_printf(s, "%30s |", "Element"); - pmc_for_each_mode(i, mode, pmcdev) + pmc_for_each_mode(mode, pmcdev) seq_printf(s, " %9s |", pmc_lpm_modes[mode]); seq_printf(s, " %9s |", "Status"); @@ -837,14 +835,14 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) u32 lpm_status; u32 lpm_status_live; const struct pmc_bit_map *map; - int mode, idx, i, len = 32; + int mode, i, len = 32; /* * Capture the requirements and create a mask so that we only * show an element if it's required for at least one of the * enabled low power modes */ - pmc_for_each_mode(idx, mode, pmcdev) + pmc_for_each_mode(mode, pmcdev) req_mask |= lpm_req_regs[mp + (mode * num_maps)]; /* Get the last latched status for this map */ @@ -870,7 +868,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name); /* Loop over the enabled states and display if required */ - pmc_for_each_mode(idx, mode, pmcdev) { + pmc_for_each_mode(mode, pmcdev) { bool required = lpm_req_regs[mp + (mode * num_maps)] & bit_mask; seq_printf(s, " %9s |", required ? "Required" : " "); @@ -935,7 +933,6 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; - unsigned int idx; bool c10; u32 reg; int mode; @@ -949,7 +946,7 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) c10 = true; } - pmc_for_each_mode(idx, mode, pmcdev) { + pmc_for_each_mode(mode, pmcdev) { if ((BIT(mode) & reg) && !c10) seq_printf(s, " [%s]", pmc_lpm_modes[mode]); else @@ -970,7 +967,6 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; bool clear = false, c10 = false; unsigned char buf[8]; - unsigned int idx; int m, mode; u32 reg; @@ -989,7 +985,7 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, mode = sysfs_match_string(pmc_lpm_modes, buf); /* Check string matches enabled mode */ - pmc_for_each_mode(idx, m, pmcdev) + pmc_for_each_mode(m, pmcdev) if (mode == m) break; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index ea04de7eb9e8..c8851f128adc 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -604,10 +604,12 @@ int lnl_core_init(struct pmc_dev *pmcdev); void cnl_suspend(struct pmc_dev *pmcdev); int cnl_resume(struct pmc_dev *pmcdev); -#define pmc_for_each_mode(i, mode, pmcdev) \ - for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ - i < pmcdev->num_lpm_modes; \ - i++, mode = pmcdev->lpm_en_modes[i]) +#define pmc_for_each_mode(mode, pmcdev) \ + for (unsigned int __i = 0, __cond; \ + __cond = __i < (pmcdev)->num_lpm_modes, \ + __cond && ((mode) = (pmcdev)->lpm_en_modes[__i]), \ + __cond; \ + __i++) #define DEFINE_PMC_CORE_ATTR_WRITE(__name) \ static int __name ## _open(struct inode *inode, struct file *file) \ diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index baddaaec25ee..c259c96b7dfd 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -45,7 +45,7 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) struct telem_endpoint *ep; const u8 *lpm_indices; int num_maps, mode_offset = 0; - int ret, mode, i; + int ret, mode; int lpm_size; u32 guid; @@ -116,7 +116,7 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) * */ mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET; - pmc_for_each_mode(i, mode, pmcdev) { + pmc_for_each_mode(mode, pmcdev) { u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps); int m; From 023a25b071a2d28f8eb8ba46da14f275c92d38d1 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 12 Aug 2024 18:48:39 +0530 Subject: [PATCH 25/65] platform/x86/amd/pmf: Add support for notifying Smart PC Solution updates The APMF function 14 (Notify Smart PC Solution Updates) allows the BIOS (AMD/OEM) to be informed about the outputs of custom Smart PC policies. Enhance the PMF driver to invoke APMF function 14 when these custom policy outputs are triggered. 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/20240812131839.308768-1-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/acpi.c | 31 +++++++++++++++++++++ drivers/platform/x86/amd/pmf/pmf.h | 18 ++++++++++++ drivers/platform/x86/amd/pmf/tee-if.c | 40 +++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1157ec148880..d5b496433d69 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -282,6 +282,29 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) return 0; } +static int apmf_notify_smart_pc_update(struct amd_pmf_dev *pdev, u32 val, u32 preq, u32 index) +{ + struct amd_pmf_notify_smart_pc_update args; + struct acpi_buffer params; + union acpi_object *info; + + args.size = sizeof(args); + args.pending_req = preq; + args.custom_bios[index] = val; + + params.length = sizeof(args); + params.pointer = &args; + + info = apmf_if_call(pdev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES, ¶ms); + if (!info) + return -EIO; + + kfree(info); + dev_dbg(pdev->dev, "Notify smart pc update, val: %u\n", val); + + return 0; +} + 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)); @@ -447,6 +470,14 @@ int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) return 0; } +int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx) +{ + if (!is_apmf_func_supported(dev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES)) + return -EINVAL; + + return apmf_notify_smart_pc_update(dev, val, preq, idx); +} + void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) { acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 753d5662c080..9bf4326d06c3 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -35,6 +35,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_NOTIFY_SMART_PC_UPDATES 14 #define APMF_FUNC_SBIOS_HEARTBEAT_V2 16 /* Message Definitions */ @@ -82,7 +83,17 @@ struct cookie_header { #define PMF_POLICY_STT_SKINTEMP_APU 7 #define PMF_POLICY_STT_SKINTEMP_HS2 8 #define PMF_POLICY_SYSTEM_STATE 9 +#define PMF_POLICY_BIOS_OUTPUT_1 10 +#define PMF_POLICY_BIOS_OUTPUT_2 11 #define PMF_POLICY_P3T 38 +#define PMF_POLICY_BIOS_OUTPUT_3 57 +#define PMF_POLICY_BIOS_OUTPUT_4 58 +#define PMF_POLICY_BIOS_OUTPUT_5 59 +#define PMF_POLICY_BIOS_OUTPUT_6 60 +#define PMF_POLICY_BIOS_OUTPUT_7 61 +#define PMF_POLICY_BIOS_OUTPUT_8 62 +#define PMF_POLICY_BIOS_OUTPUT_9 63 +#define PMF_POLICY_BIOS_OUTPUT_10 64 /* TA macros */ #define PMF_TA_IF_VERSION_MAJOR 1 @@ -344,6 +355,12 @@ struct os_power_slider { u8 slider_event; } __packed; +struct amd_pmf_notify_smart_pc_update { + u16 size; + u32 pending_req; + u32 custom_bios[10]; +} __packed; + struct fan_table_control { bool manual; unsigned long fan_id; @@ -717,6 +734,7 @@ extern const struct attribute_group cnqf_feature_attribute_group; int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev); void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev); int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev); +int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx); /* Smart PC - TA interfaces */ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index e246367aacee..19c27b6e4666 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -160,6 +160,46 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n", amd_pmf_uevent_as_str(val)); break; + + case PMF_POLICY_BIOS_OUTPUT_1: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(0), 0); + break; + + case PMF_POLICY_BIOS_OUTPUT_2: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(1), 1); + break; + + case PMF_POLICY_BIOS_OUTPUT_3: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(2), 2); + break; + + case PMF_POLICY_BIOS_OUTPUT_4: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(3), 3); + break; + + case PMF_POLICY_BIOS_OUTPUT_5: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(4), 4); + break; + + case PMF_POLICY_BIOS_OUTPUT_6: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(5), 5); + break; + + case PMF_POLICY_BIOS_OUTPUT_7: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(6), 6); + break; + + case PMF_POLICY_BIOS_OUTPUT_8: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(7), 7); + break; + + case PMF_POLICY_BIOS_OUTPUT_9: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(8), 8); + break; + + case PMF_POLICY_BIOS_OUTPUT_10: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(9), 9); + break; } } } From 37578054173919d898d2fe0b76d2f5d713937403 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 19 Aug 2024 12:04:03 +0530 Subject: [PATCH 26/65] platform/x86/amd/pmf: Relocate CPU ID macros to the PMF header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPU ID macros are needed by the Smart PC builder. Therefore, transfer the CPU ID macros from core.c to the common PMF header file. 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/20240819063404.378061-1-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/core.c | 6 ------ drivers/platform/x86/amd/pmf/pmf.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 8f1f719befa3..88314b0277a3 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -37,12 +37,6 @@ #define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE #define AMD_PMF_RESULT_FAILED 0xFF -/* List of supported CPU ids */ -#define AMD_CPU_ID_RMB 0x14b5 -#define AMD_CPU_ID_PS 0x14e8 -#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 -#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 - #define PMF_MSG_DELAY_MIN_US 50 #define RESPONSE_REGISTER_LOOP_MAX 20000 diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 9bf4326d06c3..9fc26f672f12 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -19,6 +19,12 @@ #define POLICY_SIGN_COOKIE 0x31535024 #define POLICY_COOKIE_OFFSET 0x10 +/* List of supported CPU ids */ +#define AMD_CPU_ID_RMB 0x14b5 +#define AMD_CPU_ID_PS 0x14e8 +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 +#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 + struct cookie_header { u32 sign; u32 length; From 8f2407cb3f1e8586622e80269338efb7bed2f05b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 19 Aug 2024 12:04:04 +0530 Subject: [PATCH 27/65] platform/x86/amd/pmf: Update SMU metrics table for 1AH family series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMU metrics table has been revised for the 1AH family series. Introduce a new metrics table structure to retrieve comprehensive metrics information from the PMFW. This information will be utilized by the PMF driver to adjust system thermals. 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/20240819063404.378061-2-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/core.c | 14 +++++++- drivers/platform/x86/amd/pmf/pmf.h | 49 +++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/spc.c | 51 ++++++++++++++++++++--------- 3 files changed, 97 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 88314b0277a3..d6af0ca036f1 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -255,7 +255,19 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer) /* Get Metrics Table Address */ if (alloc_buffer) { - dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL); + switch (dev->cpu_id) { + case AMD_CPU_ID_PS: + case AMD_CPU_ID_RMB: + dev->mtable_size = sizeof(dev->m_table); + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + dev->mtable_size = sizeof(dev->m_table_v2); + break; + default: + dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id); + } + + dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL); if (!dev->buf) return -ENOMEM; } diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 9fc26f672f12..8ce8816da9c1 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -198,6 +198,53 @@ struct apmf_fan_idx { u32 fan_ctl_idx; } __packed; +struct smu_pmf_metrics_v2 { + u16 core_frequency[16]; /* MHz */ + u16 core_power[16]; /* mW */ + u16 core_temp[16]; /* centi-C */ + u16 gfx_temp; /* centi-C */ + u16 soc_temp; /* centi-C */ + u16 stapm_opn_limit; /* mW */ + u16 stapm_cur_limit; /* mW */ + u16 infra_cpu_maxfreq; /* MHz */ + u16 infra_gfx_maxfreq; /* MHz */ + u16 skin_temp; /* centi-C */ + u16 gfxclk_freq; /* MHz */ + u16 fclk_freq; /* MHz */ + u16 gfx_activity; /* GFX busy % [0-100] */ + u16 socclk_freq; /* MHz */ + u16 vclk_freq; /* MHz */ + u16 vcn_activity; /* VCN busy % [0-100] */ + u16 vpeclk_freq; /* MHz */ + u16 ipuclk_freq; /* MHz */ + u16 ipu_busy[8]; /* NPU busy % [0-100] */ + u16 dram_reads; /* MB/sec */ + u16 dram_writes; /* MB/sec */ + u16 core_c0residency[16]; /* C0 residency % [0-100] */ + u16 ipu_power; /* mW */ + u32 apu_power; /* mW */ + u32 gfx_power; /* mW */ + u32 dgpu_power; /* mW */ + u32 socket_power; /* mW */ + u32 all_core_power; /* mW */ + u32 filter_alpha_value; /* time constant [us] */ + u32 metrics_counter; + u16 memclk_freq; /* MHz */ + u16 mpipuclk_freq; /* MHz */ + u16 ipu_reads; /* MB/sec */ + u16 ipu_writes; /* MB/sec */ + u32 throttle_residency_prochot; + u32 throttle_residency_spl; + u32 throttle_residency_fppt; + u32 throttle_residency_sppt; + u32 throttle_residency_thm_core; + u32 throttle_residency_thm_gfx; + u32 throttle_residency_thm_soc; + u16 psys; + u16 spare1; + u32 spare[6]; +} __packed; + struct smu_pmf_metrics { u16 gfxclk_freq; /* in MHz */ u16 socclk_freq; /* in MHz */ @@ -295,6 +342,7 @@ struct amd_pmf_dev { int hb_interval; /* SBIOS heartbeat interval */ struct delayed_work heart_beat; struct smu_pmf_metrics m_table; + struct smu_pmf_metrics_v2 m_table_v2; struct delayed_work work_buffer; ktime_t start_time; int socket_power_history[AVG_SAMPLE_SIZE]; @@ -319,6 +367,7 @@ struct amd_pmf_dev { bool smart_pc_enabled; u16 pmf_if_version; struct input_dev *pmf_idev; + size_t mtable_size; }; struct apmf_sps_prop_granular_v2 { diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 3c153fb1425e..b5183969f9bf 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -53,30 +53,49 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} #endif -static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) { u16 max, avg = 0; int i; - memset(dev->buf, 0, sizeof(dev->m_table)); - amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); - memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table)); - - in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; - in->ev_info.skin_temperature = dev->m_table.skin_temp; - /* Get the avg and max C0 residency of all the cores */ - max = dev->m_table.avg_core_c0residency[0]; - for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) { - avg += dev->m_table.avg_core_c0residency[i]; - if (dev->m_table.avg_core_c0residency[i] > max) - max = dev->m_table.avg_core_c0residency[i]; + max = *core_res; + for (i = 0; i < size; i++) { + avg += core_res[i]; + if (core_res[i] > max) + max = core_res[i]; } - - avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency)); + avg = DIV_ROUND_CLOSEST(avg, size); in->ev_info.avg_c0residency = avg; in->ev_info.max_c0residency = max; - in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; +} + +static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + /* Get the updated metrics table data */ + memset(dev->buf, 0, dev->mtable_size); + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); + + switch (dev->cpu_id) { + case AMD_CPU_ID_PS: + memcpy(&dev->m_table, dev->buf, dev->mtable_size); + in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; + in->ev_info.skin_temperature = dev->m_table.skin_temp; + in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; + amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency, + ARRAY_SIZE(dev->m_table.avg_core_c0residency), in); + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size); + in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power; + in->ev_info.skin_temperature = dev->m_table_v2.skin_temp; + in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity; + amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency, + ARRAY_SIZE(dev->m_table_v2.core_c0residency), in); + break; + default: + dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id); + } } static const char * const pmf_battery_supply_name[] = { From 3573fee87fe36e7c078d801809b186984ebe10d9 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 13 Aug 2024 04:29:03 +0200 Subject: [PATCH 28/65] platform/x86: lg-laptop: Add operation region support The LEGX0820 ACPI device is expected to provide a custom operation region: OperationRegion (XIN1, 0x8F, Zero, 0x04B0) Field (XIN1, AnyAcc, Lock, Preserve) { DMSG, 8, HDAP, 8, Offset (0x03), AFNM, 8, Offset (0x10), P80B, 8, P81B, 8, P82B, 8, P83B, 8, P84B, 8, P85B, 8, P86B, 8, P87B, 8, Offset (0x20), DTTM, 8, TMP1, 8, LTP1, 8, HTP1, 8, TMP2, 8, LTP2, 8, HTP2, 8, Offset (0x3E8), PMSG, 1600 } The PMSG field is used by AML code to log debug messages when DMSG is true. Since those debug messages are already logged using the standard ACPI Debug object, we set DMSG unconditionally to 0x00 and ignore any writes to PMSG. The TMPx, LTPx, HTPx and AFNM fields are used to inform the driver when the temperature/(presumably) trip points/fan mode changes. This only happens when the DTTM flag is set. Unfortunately we have to implement support for this operation region because the AML codes uses code constructs like this one: If (((\_SB.XINI.PLAV != Zero) && (\_SB.XINI.DTTM != Zero))) The PLAV field gets set to 1 when the driver registers its address space handler, so by default XIN1 should not be accessed. However ACPI does not use short-circuit evaluation when evaluating logical conditions. This causes the DTTM field to be accessed even when PLAV is 0, which results in an ACPI error. Since this check happens inside various thermal-related ACPI control methods, various thermal zone become unusable since any attempt to read their temperature results in an ACPI error. Fix this by providing support for this operation region. I suspect that the problem does not happen under Windows (which seemingly does not use short-circuit evaluation either) because the necessary driver comes preinstalled with the machine. Tested-by: Chris Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240813022903.20567-1-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/lg-laptop.c | 136 +++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 9c7857842caf..55d31d4fefd6 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -8,6 +8,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include +#include +#include #include #include #include @@ -31,6 +34,26 @@ MODULE_AUTHOR("Matan Ziv-Av"); MODULE_DESCRIPTION("LG WMI Hotkey Driver"); MODULE_LICENSE("GPL"); +static bool fw_debug; +module_param(fw_debug, bool, 0); +MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); + +#define LG_ADDRESS_SPACE_ID 0x8F + +#define LG_ADDRESS_SPACE_DEBUG_FLAG_ADR 0x00 +#define LG_ADDRESS_SPACE_FAN_MODE_ADR 0x03 + +#define LG_ADDRESS_SPACE_DTTM_FLAG_ADR 0x20 +#define LG_ADDRESS_SPACE_CPU_TEMP_ADR 0x21 +#define LG_ADDRESS_SPACE_CPU_TRIP_LOW_ADR 0x22 +#define LG_ADDRESS_SPACE_CPU_TRIP_HIGH_ADR 0x23 +#define LG_ADDRESS_SPACE_MB_TEMP_ADR 0x24 +#define LG_ADDRESS_SPACE_MB_TRIP_LOW_ADR 0x25 +#define LG_ADDRESS_SPACE_MB_TRIP_HIGH_ADR 0x26 + +#define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8 +#define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8 + #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248" #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" @@ -646,6 +669,107 @@ static struct platform_driver pf_driver = { } }; +static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address, + size_t size, u64 value) +{ + u8 byte; + + /* Ignore any debug messages */ + if (address >= LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR && + address <= LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR) + return AE_OK; + + if (size != sizeof(byte)) + return AE_BAD_PARAMETER; + + byte = value & 0xFF; + + switch (address) { + case LG_ADDRESS_SPACE_FAN_MODE_ADR: + /* + * The fan mode field is not affected by the DTTM flag, so we + * have to manually check fw_debug. + */ + if (fw_debug) + dev_dbg(dev, "Fan mode set to mode %u\n", byte); + + return AE_OK; + case LG_ADDRESS_SPACE_CPU_TEMP_ADR: + dev_dbg(dev, "CPU temperature is %u °C\n", byte); + return AE_OK; + case LG_ADDRESS_SPACE_CPU_TRIP_LOW_ADR: + dev_dbg(dev, "CPU lower trip point set to %u °C\n", byte); + return AE_OK; + case LG_ADDRESS_SPACE_CPU_TRIP_HIGH_ADR: + dev_dbg(dev, "CPU higher trip point set to %u °C\n", byte); + return AE_OK; + case LG_ADDRESS_SPACE_MB_TEMP_ADR: + dev_dbg(dev, "Motherboard temperature is %u °C\n", byte); + return AE_OK; + case LG_ADDRESS_SPACE_MB_TRIP_LOW_ADR: + dev_dbg(dev, "Motherboard lower trip point set to %u °C\n", byte); + return AE_OK; + case LG_ADDRESS_SPACE_MB_TRIP_HIGH_ADR: + dev_dbg(dev, "Motherboard higher trip point set to %u °C\n", byte); + return AE_OK; + default: + dev_notice_ratelimited(dev, "Ignoring write to unknown opregion address %llu\n", + address); + return AE_OK; + } +} + +static acpi_status lg_laptop_address_space_read(struct device *dev, acpi_physical_address address, + size_t size, u64 *value) +{ + if (size != 1) + return AE_BAD_PARAMETER; + + switch (address) { + case LG_ADDRESS_SPACE_DEBUG_FLAG_ADR: + /* Debug messages are already printed using the standard ACPI Debug object */ + *value = 0x00; + return AE_OK; + case LG_ADDRESS_SPACE_DTTM_FLAG_ADR: + *value = fw_debug; + return AE_OK; + default: + dev_notice_ratelimited(dev, "Attempt to read unknown opregion address %llu\n", + address); + return AE_BAD_PARAMETER; + } +} + +static acpi_status lg_laptop_address_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value, void *handler_context, + void *region_context) +{ + struct device *dev = handler_context; + size_t size; + + if (bits % BITS_PER_BYTE) + return AE_BAD_PARAMETER; + + size = bits / BITS_PER_BYTE; + + switch (function) { + case ACPI_READ: + return lg_laptop_address_space_read(dev, address, size, value); + case ACPI_WRITE: + return lg_laptop_address_space_write(dev, address, size, *value); + default: + return AE_BAD_PARAMETER; + } +} + +static void lg_laptop_remove_address_space_handler(void *data) +{ + struct acpi_device *device = data; + + acpi_remove_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID, + &lg_laptop_address_space_handler); +} + static int acpi_add(struct acpi_device *device) { struct platform_device_info pdev_info = { @@ -653,6 +777,7 @@ static int acpi_add(struct acpi_device *device) .name = PLATFORM_NAME, .id = PLATFORM_DEVID_NONE, }; + acpi_status status; int ret; const char *product; int year = 2017; @@ -660,6 +785,17 @@ static int acpi_add(struct acpi_device *device) if (pf_device) return 0; + status = acpi_install_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID, + &lg_laptop_address_space_handler, + NULL, &device->dev); + if (ACPI_FAILURE(status)) + return -ENODEV; + + ret = devm_add_action_or_reset(&device->dev, lg_laptop_remove_address_space_handler, + device); + if (ret < 0) + return ret; + ret = platform_driver_register(&pf_driver); if (ret) return ret; From ac5ebdad20060a734ba5ae75e20dcfc1b89ef998 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 14 Aug 2024 12:27:25 +0200 Subject: [PATCH 29/65] dt-bindings: serial: Allow embedded-controller as child node There exist some embedded controllers (like Microsoft SAM found on Surface devices or Apple Oscar found on old iPhones) that connect to the host device via serial. Allow that class of devices to exist under serial interface controller nodes. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240814-topic-sam-v3-1-a84588aad233@quicinc.com Signed-off-by: Hans de Goede --- Documentation/devicetree/bindings/serial/serial.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/serial.yaml b/Documentation/devicetree/bindings/serial/serial.yaml index ffc9198ae214..9b2c94796371 100644 --- a/Documentation/devicetree/bindings/serial/serial.yaml +++ b/Documentation/devicetree/bindings/serial/serial.yaml @@ -88,7 +88,7 @@ properties: TX FIFO threshold configuration (in bytes). patternProperties: - "^(bluetooth|bluetooth-gnss|gnss|gps|mcu|onewire)$": + "^(bluetooth|bluetooth-gnss|embedded-controller|gnss|gps|mcu|onewire)$": if: type: object then: From ceccd196e158805a4e9b69d92318dafe7b9d3ea2 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 14 Aug 2024 12:27:26 +0200 Subject: [PATCH 30/65] dt-bindings: platform: Add Surface System Aggregator Module Add bindings for the Surface System Aggregator Module (SAM/SSAM), the Microsoft Surface-standard Embedded Controller, used on both x86- and Qualcomm-based devices. It provides a plethora of functions, depending on what's wired up to it. That includes but is not limited to: fan control, keyboard/touchpad support, thermal sensors, power control, special buttons, tablet mode. Signed-off-by: Konrad Dybcio Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240814-topic-sam-v3-2-a84588aad233@quicinc.com Signed-off-by: Hans de Goede --- .../platform/microsoft,surface-sam.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/platform/microsoft,surface-sam.yaml diff --git a/Documentation/devicetree/bindings/platform/microsoft,surface-sam.yaml b/Documentation/devicetree/bindings/platform/microsoft,surface-sam.yaml new file mode 100644 index 000000000000..b33d26f15b2a --- /dev/null +++ b/Documentation/devicetree/bindings/platform/microsoft,surface-sam.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/platform/microsoft,surface-sam.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Surface System Aggregator Module (SAM, SSAM) + +maintainers: + - Konrad Dybcio + +description: | + Surface devices use a standardized embedded controller to let the + operating system interface with various hardware functions. The + specific functionalities are modeled as subdevices and matched on + five levels: domain, category, target, instance and function. + +properties: + compatible: + const: microsoft,surface-sam + + interrupts: + maxItems: 1 + + current-speed: true + +required: + - compatible + - interrupts + +additionalProperties: false + +examples: + - | + #include + uart { + embedded-controller { + compatible = "microsoft,surface-sam"; + + interrupts-extended = <&tlmm 91 IRQ_TYPE_EDGE_RISING>; + + pinctrl-0 = <&ssam_state>; + pinctrl-names = "default"; + + current-speed = <4000000>; + }; + }; From b27622f1317271b88048ff2d76fead28b72aeccf Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 14 Aug 2024 12:27:27 +0200 Subject: [PATCH 31/65] platform/surface: Add OF support Add basic support for registering the aggregator module on Device Tree- based platforms. These include at least three generations of Qualcomm Snapdragon-based Surface devices: - SC8180X / SQ1 / SQ2: Pro X, - SC8280XP / SQ3: Devkit 2023, Pro 9 - X Elite: Laptop 7 / Pro11 Thankfully, the aggregators on these seem to be configured in an identical way, which allows for using these settings as defaults and no DT properties need to be introduced (until that changes, anyway). Based on the work done by Maximilian Luz, largely rewritten. Signed-off-by: Konrad Dybcio Reviewed-by: Maximilian Luz Tested-by: Maximilian Luz Link: https://lore.kernel.org/r/20240814-topic-sam-v3-3-a84588aad233@quicinc.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/aggregator/bus.c | 2 + .../platform/surface/aggregator/controller.c | 67 +++++++++++---- drivers/platform/surface/aggregator/core.c | 82 ++++++++++++++----- .../surface/surface_aggregator_registry.c | 45 ++++++++-- 4 files changed, 158 insertions(+), 38 deletions(-) diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c index af8d573aae93..d68d231e716e 100644 --- a/drivers/platform/surface/aggregator/bus.c +++ b/drivers/platform/surface/aggregator/bus.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -441,6 +442,7 @@ static int ssam_add_client_device(struct device *parent, struct ssam_controller sdev->dev.parent = parent; sdev->dev.fwnode = fwnode_handle_get(node); + sdev->dev.of_node = to_of_node(node); status = ssam_device_add(sdev); if (status) diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c index 7fc602e01487..27eadf22b292 100644 --- a/drivers/platform/surface/aggregator/controller.c +++ b/drivers/platform/surface/aggregator/controller.c @@ -1104,13 +1104,6 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle, u64 funcs; int status; - /* Set defaults. */ - caps->ssh_power_profile = U32_MAX; - caps->screen_on_sleep_idle_timeout = U32_MAX; - caps->screen_off_sleep_idle_timeout = U32_MAX; - caps->d3_closes_handle = false; - caps->ssh_buffer_size = U32_MAX; - /* Pre-load supported DSM functions. */ status = ssam_dsm_get_functions(handle, &funcs); if (status) @@ -1149,6 +1142,52 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle, return 0; } +/** + * ssam_controller_caps_load_from_of() - Load controller capabilities from OF/DT. + * @dev: A pointer to the controller device + * @caps: Where to store the capabilities in. + * + * Return: Returns zero on success, a negative error code on failure. + */ +static int ssam_controller_caps_load_from_of(struct device *dev, struct ssam_controller_caps *caps) +{ + /* + * Every device starting with Surface Pro X through Laptop 7 uses these + * identical values, which makes them good defaults. + */ + caps->d3_closes_handle = true; + caps->screen_on_sleep_idle_timeout = 5000; + caps->screen_off_sleep_idle_timeout = 30; + caps->ssh_buffer_size = 48; + /* TODO: figure out power profile */ + + return 0; +} + +/** + * ssam_controller_caps_load() - Load controller capabilities + * @dev: A pointer to the controller device + * @caps: Where to store the capabilities in. + * + * Return: Returns zero on success, a negative error code on failure. + */ +static int ssam_controller_caps_load(struct device *dev, struct ssam_controller_caps *caps) +{ + acpi_handle handle = ACPI_HANDLE(dev); + + /* Set defaults. */ + caps->ssh_power_profile = U32_MAX; + caps->screen_on_sleep_idle_timeout = U32_MAX; + caps->screen_off_sleep_idle_timeout = U32_MAX; + caps->d3_closes_handle = false; + caps->ssh_buffer_size = U32_MAX; + + if (handle) + return ssam_controller_caps_load_from_acpi(handle, caps); + else + return ssam_controller_caps_load_from_of(dev, caps); +} + /** * ssam_controller_init() - Initialize SSAM controller. * @ctrl: The controller to initialize. @@ -1165,13 +1204,12 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle, int ssam_controller_init(struct ssam_controller *ctrl, struct serdev_device *serdev) { - acpi_handle handle = ACPI_HANDLE(&serdev->dev); int status; init_rwsem(&ctrl->lock); kref_init(&ctrl->kref); - status = ssam_controller_caps_load_from_acpi(handle, &ctrl->caps); + status = ssam_controller_caps_load(&serdev->dev, &ctrl->caps); if (status) return status; @@ -2715,11 +2753,12 @@ int ssam_irq_setup(struct ssam_controller *ctrl) const int irqf = IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN; gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS); - if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); - - irq = gpiod_to_irq(gpiod); - gpiod_put(gpiod); + if (IS_ERR(gpiod)) { + irq = fwnode_irq_get(dev_fwnode(dev), 0); + } else { + irq = gpiod_to_irq(gpiod); + gpiod_put(gpiod); + } if (irq < 0) return irq; diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c index 797d0645bd77..c58e1fdd1a5f 100644 --- a/drivers/platform/surface/aggregator/core.c +++ b/drivers/platform/surface/aggregator/core.c @@ -17,9 +17,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -299,7 +302,7 @@ static const struct attribute_group ssam_sam_group = { }; -/* -- ACPI based device setup. ---------------------------------------------- */ +/* -- Serial device setup. -------------------------------------------------- */ static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc, void *ctx) @@ -352,13 +355,28 @@ static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc, return AE_CTRL_TERMINATE; } -static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle, - struct serdev_device *serdev) +static int ssam_serdev_setup_via_acpi(struct serdev_device *serdev, acpi_handle handle) { - return acpi_walk_resources(handle, METHOD_NAME__CRS, - ssam_serdev_setup_via_acpi_crs, serdev); + acpi_status status; + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + ssam_serdev_setup_via_acpi_crs, serdev); + + return status ? -ENXIO : 0; } +static int ssam_serdev_setup(struct acpi_device *ssh, struct serdev_device *serdev) +{ + if (ssh) + return ssam_serdev_setup_via_acpi(serdev, ssh->handle); + + /* TODO: these values may differ per board/implementation */ + serdev_device_set_baudrate(serdev, 4 * HZ_PER_MHZ); + serdev_device_set_flow_control(serdev, true); + serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + + return 0; +} /* -- Power management. ----------------------------------------------------- */ @@ -621,16 +639,17 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev) struct device *dev = &serdev->dev; struct acpi_device *ssh = ACPI_COMPANION(dev); struct ssam_controller *ctrl; - acpi_status astatus; int status; - status = gpiod_count(dev, NULL); - if (status < 0) - return dev_err_probe(dev, status, "no GPIO found\n"); + if (ssh) { + status = gpiod_count(dev, NULL); + if (status < 0) + return dev_err_probe(dev, status, "no GPIO found\n"); - status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios); - if (status) - return status; + status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios); + if (status) + return status; + } /* Allocate controller. */ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); @@ -655,9 +674,9 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev) goto err_devopen; } - astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev); - if (ACPI_FAILURE(astatus)) { - status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n"); + status = ssam_serdev_setup(ssh, serdev); + if (status) { + status = dev_err_probe(dev, status, "failed to setup serdev\n"); goto err_devinit; } @@ -717,7 +736,23 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev) * For now let's thus default power/wakeup to false. */ device_set_wakeup_capable(dev, true); - acpi_dev_clear_dependencies(ssh); + + /* + * When using DT, we have to register the platform hub driver manually, + * as it can't be matched based on top-level board compatible (like it + * does the ACPI case). + */ + if (!ssh) { + struct platform_device *ph_pdev = + platform_device_register_simple("surface_aggregator_platform_hub", + 0, NULL, 0); + if (IS_ERR(ph_pdev)) + return dev_err_probe(dev, PTR_ERR(ph_pdev), + "Failed to register the platform hub driver\n"); + } + + if (ssh) + acpi_dev_clear_dependencies(ssh); return 0; @@ -782,18 +817,27 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev) device_set_wakeup_capable(&serdev->dev, false); } -static const struct acpi_device_id ssam_serial_hub_match[] = { +static const struct acpi_device_id ssam_serial_hub_acpi_match[] = { { "MSHW0084", 0 }, { }, }; -MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_match); +MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_acpi_match); + +#ifdef CONFIG_OF +static const struct of_device_id ssam_serial_hub_of_match[] = { + { .compatible = "microsoft,surface-sam", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ssam_serial_hub_of_match); +#endif static struct serdev_device_driver ssam_serial_hub = { .probe = ssam_serial_hub_probe, .remove = ssam_serial_hub_remove, .driver = { .name = "surface_serial_hub", - .acpi_match_table = ssam_serial_hub_match, + .acpi_match_table = ACPI_PTR(ssam_serial_hub_acpi_match), + .of_match_table = of_match_ptr(ssam_serial_hub_of_match), .pm = &ssam_serial_hub_pm_ops, .shutdown = ssam_serial_hub_shutdown, .probe_type = PROBE_PREFER_ASYNCHRONOUS, diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 1c4d74db08c9..ac96e883cb57 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -273,6 +274,18 @@ static const struct software_node *ssam_node_group_sl5[] = { NULL, }; +/* Devices for Surface Laptop 7. */ +static const struct software_node *ssam_node_group_sl7[] = { + &ssam_node_root, + &ssam_node_bat_ac, + &ssam_node_bat_main, + &ssam_node_tmp_perf_profile_with_fan, + &ssam_node_fan_speed, + &ssam_node_hid_sam_keyboard, + /* TODO: evaluate thermal sensors devices when we get a driver for that */ + NULL, +}; + /* Devices for Surface Laptop Studio. */ static const struct software_node *ssam_node_group_sls[] = { &ssam_node_root, @@ -346,7 +359,7 @@ static const struct software_node *ssam_node_group_sp9[] = { /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ -static const struct acpi_device_id ssam_platform_hub_match[] = { +static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */ { "MSHW0081", (unsigned long)ssam_node_group_gen5 }, @@ -400,18 +413,39 @@ static const struct acpi_device_id ssam_platform_hub_match[] = { { }, }; -MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match); +MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_acpi_match); + +static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = { + /* Surface Laptop 7 */ + { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 }, + { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 }, + { }, +}; static int ssam_platform_hub_probe(struct platform_device *pdev) { const struct software_node **nodes; + const struct of_device_id *match; + struct device_node *fdt_root; struct ssam_controller *ctrl; struct fwnode_handle *root; int status; nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev); - if (!nodes) - return -ENODEV; + if (!nodes) { + fdt_root = of_find_node_by_path("/"); + if (!fdt_root) + return -ENODEV; + + match = of_match_node(ssam_platform_hub_of_match, fdt_root); + of_node_put(fdt_root); + if (!match) + return -ENODEV; + + nodes = (const struct software_node **)match->data; + if (!nodes) + return -ENODEV; + } /* * As we're adding the SSAM client devices as children under this device @@ -460,12 +494,13 @@ static struct platform_driver ssam_platform_hub_driver = { .remove_new = ssam_platform_hub_remove, .driver = { .name = "surface_aggregator_platform_hub", - .acpi_match_table = ssam_platform_hub_match, + .acpi_match_table = ssam_platform_hub_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; module_platform_driver(ssam_platform_hub_driver); +MODULE_ALIAS("platform:surface_aggregator_platform_hub"); MODULE_AUTHOR("Maximilian Luz "); MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module"); MODULE_LICENSE("GPL"); From 3900c6ab4129f5e37e95395462688670d117436f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 14 Aug 2024 14:29:39 +0100 Subject: [PATCH 32/65] platform/x86: serial-multi-instantiate: Don't require both I2C and SPI Change the Kconfig dependency so that it doesn't require both I2C and SPI subsystems to be built. Make a few small changes to the code so that the code for a bus is only called if the bus is being built. When SPI support was added to serial-multi-instantiate it created a dependency that both CONFIG_I2C and CONFIG_SPI must be enabled. Typically they are, but there's no reason why this should be a requirement. A specific kernel build could have only I2C devices or only SPI devices. It should be possible to use serial-multi-instantiate if only I2C or only SPI is enabled. The dependency formula used is: depends on (I2C && !SPI) || (!I2C && SPI) || (I2C && SPI) The advantage of this approach is that if I2C=m or SPI=m then SERIAL_MULTI_INSTANTIATE is limited to n/m. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240814132939.308696-1-rf@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 3 +- .../platform/x86/serial-multi-instantiate.c | 32 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ddfccc226751..3875abba5a79 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1000,7 +1000,8 @@ config TOPSTAR_LAPTOP config SERIAL_MULTI_INSTANTIATE tristate "Serial bus multi instantiate pseudo device driver" - depends on I2C && SPI && ACPI + depends on ACPI + depends on (I2C && !SPI) || (!I2C && SPI) || (I2C && SPI) help Some ACPI-based systems list multiple devices in a single ACPI firmware-node. This driver will instantiate separate clients diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 3be016cfe601..7c04cc9e5891 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -83,11 +83,15 @@ static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, static void smi_devs_unregister(struct smi *smi) { +#if IS_REACHABLE(CONFIG_I2C) while (smi->i2c_num--) i2c_unregister_device(smi->i2c_devs[smi->i2c_num]); +#endif - while (smi->spi_num--) - spi_unregister_device(smi->spi_devs[smi->spi_num]); + if (IS_REACHABLE(CONFIG_SPI)) { + while (smi->spi_num--) + spi_unregister_device(smi->spi_devs[smi->spi_num]); + } } /** @@ -258,9 +262,15 @@ static int smi_probe(struct platform_device *pdev) switch (node->bus_type) { case SMI_I2C: - return smi_i2c_probe(pdev, smi, node->instances); + if (IS_REACHABLE(CONFIG_I2C)) + return smi_i2c_probe(pdev, smi, node->instances); + + return -ENODEV; case SMI_SPI: - return smi_spi_probe(pdev, smi, node->instances); + if (IS_REACHABLE(CONFIG_SPI)) + return smi_spi_probe(pdev, smi, node->instances); + + return -ENODEV; case SMI_AUTO_DETECT: /* * For backwards-compatibility with the existing nodes I2C @@ -270,10 +280,16 @@ static int smi_probe(struct platform_device *pdev) * SpiSerialBus nodes that were previously ignored, and this * preserves that behavior. */ - ret = smi_i2c_probe(pdev, smi, node->instances); - if (ret != -ENOENT) - return ret; - return smi_spi_probe(pdev, smi, node->instances); + if (IS_REACHABLE(CONFIG_I2C)) { + ret = smi_i2c_probe(pdev, smi, node->instances); + if (ret != -ENOENT) + return ret; + } + + if (IS_REACHABLE(CONFIG_SPI)) + return smi_spi_probe(pdev, smi, node->instances); + + return -ENODEV; default: return -EINVAL; } From 57d0557dfa4940919ec2971245a6d288e5d85aa8 Mon Sep 17 00:00:00 2001 From: Matthias Fetzer Date: Fri, 16 Aug 2024 16:12:28 +0200 Subject: [PATCH 33/65] platform/x86: thinkpad_acpi: Add Thinkpad Edge E531 fan support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fan control on the E531 is done using the ACPI methods FANG and FANW. The correct parameters and register values were found by analyzing EC firmware as well as DSDT. This has been tested on my Thinkpad Edge E531 (6885CTO, BIOS HEET52WW 1.33). Signed-off-by: Matthias Fetzer Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240816141228.134529-1-kontakt@matthias-fetzer.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/thinkpad_acpi.c | 143 ++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f269ca1ff771..8f7053920884 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -7749,6 +7749,28 @@ static struct ibm_struct volume_driver_data = { * EC 0x2f (HFSP) might be available *for reading*, but do not use * it for writing. * + * TPACPI_FAN_RD_ACPI_FANG: + * ACPI FANG method: returns fan control register + * + * Takes one parameter which is 0x8100 plus the offset to EC memory + * address 0xf500 and returns the byte at this address. + * + * 0xf500: + * When the value is less than 9 automatic mode is enabled + * 0xf502: + * Contains the current fan speed from 0-100% + * 0xf506: + * Bit 7 has to be set in order to enable manual control by + * writing a value >= 9 to 0xf500 + * + * TPACPI_FAN_WR_ACPI_FANW: + * ACPI FANW method: sets fan control registers + * + * Takes 0x8100 plus the offset to EC memory address 0xf500 and the + * value to be written there as parameters. + * + * see TPACPI_FAN_RD_ACPI_FANG + * * TPACPI_FAN_WR_TPEC: * ThinkPad EC register 0x2f (HFSP): fan control loop mode * Supported on almost all ThinkPads @@ -7882,6 +7904,7 @@ enum { /* Fan control constants */ enum fan_status_access_mode { TPACPI_FAN_NONE = 0, /* No fan status or control */ TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */ TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */ }; @@ -7889,6 +7912,7 @@ enum fan_status_access_mode { enum fan_control_access_mode { TPACPI_FAN_WR_NONE = 0, /* No fan control */ TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */ TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ }; @@ -7922,9 +7946,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ ); /* all others */ +TPACPI_HANDLE(fang, ec, "FANG", /* E531 */ + ); /* all others */ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ "JFNS", /* 770x-JL */ ); /* all others */ +TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */ + ); /* all others */ /* * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the @@ -8031,6 +8059,23 @@ static int fan_get_status(u8 *status) break; } + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int mode, speed; + + if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100))) + return -EIO; + if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(status)) { + *status = speed * 7 / 100; + if (mode < 9) + *status |= TP_EC_FAN_AUTO; + } + + break; + } case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_status_offset, &s))) @@ -8145,6 +8190,17 @@ static int fan2_get_speed(unsigned int *speed) if (speed) *speed = lo ? FAN_RPM_CAL_CONST / lo : 0; break; + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int speed_tmp; + + if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(speed)) + *speed = speed_tmp * 65535 / 100; + break; + } default: return -ENXIO; @@ -8204,6 +8260,32 @@ static int fan_set_level(int level) tp_features.fan_ctrl_status_undef = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7)) + return -EINVAL; + if (level & TP_EC_FAN_FULLSPEED) + return -EINVAL; + + if (level & TP_EC_FAN_AUTO) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + return -EIO; + } + } else { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) { + return -EIO; + } + } + break; + default: return -ENXIO; } @@ -8282,6 +8364,19 @@ static int fan_set_enable(void) rc = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + rc = -EIO; + break; + } + + rc = 0; + break; + default: rc = -ENXIO; } @@ -8324,6 +8419,22 @@ static int fan_set_disable(void) fan_control_desired_level = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) { + rc = -EIO; + break; + } + rc = 0; + break; + default: rc = -ENXIO; } @@ -8357,6 +8468,23 @@ static int fan_set_speed(int speed) rc = -EINVAL; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", + 0x8102, speed * 100 / 65535)) + rc = -EIO; + } else + rc = -EINVAL; + break; + default: rc = -ENXIO; } @@ -8699,6 +8827,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(sfan); } + if (tpacpi_is_lenovo()) { + TPACPI_ACPIHANDLE_INIT(fang); + TPACPI_ACPIHANDLE_INIT(fanw); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -8718,6 +8850,9 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else if (fang_handle) { + /* E531 */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG; } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ @@ -8764,6 +8899,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; fan_control_commands |= TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else if (fanw_handle) { + /* E531 */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -8915,6 +9055,7 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_RD_TPEC_NS: case TPACPI_FAN_RD_TPEC: + case TPACPI_FAN_RD_ACPI_FANG: /* all except 570, 600e/x, 770e, 770x */ rc = fan_get_status_safe(&status); if (rc) @@ -8935,7 +9076,7 @@ static int fan_read(struct seq_file *m) * No other levels settings available */ seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto"); - } else { + } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ seq_printf(m, "level:\t\tdisengaged\n"); From 1e701372d7ac1939d5f8a1dc8172de00192394a8 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Fri, 23 Aug 2024 18:43:37 +0000 Subject: [PATCH 34/65] platform/x86/intel/ifs: Fix SBAF title underline length In commit # 0a3e4e94d137 ("platform/x86/intel/ifs: Add SBAF test image loading support"), the documentation for "Structural Based Functional Test at Field (SBAF)" had an incomplete underline. This resulted in the following build warning: Documentation/arch/x86/ifs:2: drivers/platform/x86/intel/ifs/ifs.h:131: WARNING: Title underline too short. Fix it by extending the dotted lines to match the length of the title. Fixes: 0a3e4e94d137 ("platform/x86/intel/ifs: Add SBAF test image loading support") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/lkml/20240820134354.2aec355d@canb.auug.org.au/T/#u Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Jithu Joseph Link: https://lore.kernel.org/r/20240823184337.2923179-1-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/ifs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index b261be46bce8..5c3c0dfa1bf8 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -129,7 +129,7 @@ * * * Structural Based Functional Test at Field (SBAF): - * ------------------------------------------------ + * ------------------------------------------------- * * SBAF is a new type of testing that provides comprehensive core test * coverage complementing Scan at Field (SAF) testing. SBAF mimics the From bb9a9bf2787fbe1a4e003daabc8f7a31c17740d6 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 20 Aug 2024 13:45:58 -0700 Subject: [PATCH 35/65] platform/x86/intel-uncore-freq: Do not present separate package-die domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scope of uncore control is per power domain with TPMI. There are two types of processor topologies can be presented by CPUID extended topology leaf irrespective of the hardware architecture: 1. A die is not enumerated in CPUID. In this case there is only one die in a package is visible. In this case there can be multiple power domains in a single die. 2. A power domain in a package is enumerated as a die in CPUID. So there is one power domain per die. To allow die level controls, the current implementation creates a root domain and aggregates all information from power domains in it. This is well suited for configuration 1 above. But for configuration 2 above, the root domain will present the same information as present by power domain. So, no use of aggregating. To check the configuration, call topology_max_dies_per_package(). If it is more than one, avoid creating root domain. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240820204558.1296319-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../x86/intel/uncore-frequency/uncore-frequency-tpmi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 9fa3037c03d1..6c2e607968f2 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -427,6 +427,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ auxiliary_set_drvdata(auxdev, tpmi_uncore); + if (topology_max_dies_per_package() > 1) + return 0; + tpmi_uncore->root_cluster.root_domain = true; tpmi_uncore->root_cluster.uncore_root = tpmi_uncore; @@ -450,7 +453,9 @@ static void uncore_remove(struct auxiliary_device *auxdev) { struct tpmi_uncore_struct *tpmi_uncore = auxiliary_get_drvdata(auxdev); - uncore_freq_remove_die_entry(&tpmi_uncore->root_cluster.uncore_data); + if (tpmi_uncore->root_cluster.root_domain) + uncore_freq_remove_die_entry(&tpmi_uncore->root_cluster.uncore_data); + remove_cluster_entries(tpmi_uncore); uncore_freq_common_exit(); From c34068c8edad268bbd83feb39ef1b8b47b571dd6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 15:04:57 +0300 Subject: [PATCH 36/65] platform/x86: intel-hid: Use string_choices API instead of ternary operator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use modern string_choices API instead of manually determining the output using ternary operator. Signed-off-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240821120458.3702655-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/hid.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 10cd65497cc1..445e7a59beb4 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "../dual_accel_detect.h" @@ -331,10 +332,8 @@ static int intel_hid_set_enable(struct device *device, bool enable) acpi_handle handle = ACPI_HANDLE(device); /* Enable|disable features - power button is always enabled */ - if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, - enable)) { - dev_warn(device, "failed to %sable hotkeys\n", - enable ? "en" : "dis"); + if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, enable)) { + dev_warn(device, "failed to %s hotkeys\n", str_enable_disable(enable)); return -EIO; } From ab58016c68cc5b8c3622e38b7db64994e4833d9f Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 20 Aug 2024 03:30:05 -0400 Subject: [PATCH 37/65] platform/x86:dell-laptop: Add knobs to change battery charge settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Dell BIOS allows you to set custom charging modes, which is useful in particular for extending battery life. This adds support for tweaking those various settings from Linux via sysfs knobs. One might, for example, have their laptop plugged into power at their desk the vast majority of the time and choose fairly aggressive battery-saving settings (eg, only charging once the battery drops below 50% and only charging up to 80%). When leaving for a trip, it would be more useful to instead switch to a standard charging mode (top off at 100%, charge any time power is available). Rebooting into the BIOS to change the charging mode settings is a hassle. For the Custom charging type mode, we reuse the common charge_control_{start,end}_threshold sysfs power_supply entries. The BIOS also has a bunch of other charging modes (with varying levels of usefulness), so this also adds a 'charge_type' sysfs entry that maps the standard values to Dell-specific ones. This work is based on a patch by Perry Yuan and Limonciello Mario submitted back in 2020. Hans de Goede: s/charge_type/charge_types/ since charge_types_show() used the new charge_types power-supply property output format. Signed-off-by: Andres Salomon Reviewed-by: Pali Rohár Link: https://lore.kernel.org/r/20240820033005.09e03af1@5400 Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/Kconfig | 1 + drivers/platform/x86/dell/dell-laptop.c | 310 ++++++++++++++++++++++++ drivers/platform/x86/dell/dell-smbios.h | 7 + 3 files changed, 318 insertions(+) diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 85a78ef91182..02405793163c 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -49,6 +49,7 @@ config DELL_LAPTOP default m depends on DMI depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_BATTERY depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n depends on DELL_WMI || DELL_WMI = n diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 6552dfe491c6..13428e745796 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -22,11 +22,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "dell-rbtn.h" #include "dell-smbios.h" @@ -99,6 +101,20 @@ static bool force_rfkill; static bool micmute_led_registered; static bool mute_led_registered; +struct battery_mode_info { + int token; + const char *label; +}; + +static const struct battery_mode_info battery_modes[] = { + { BAT_PRI_AC_MODE_TOKEN, "Trickle" }, + { BAT_EXPRESS_MODE_TOKEN, "Fast" }, + { BAT_STANDARD_MODE_TOKEN, "Standard" }, + { BAT_ADAPTIVE_MODE_TOKEN, "Adaptive" }, + { BAT_CUSTOM_MODE_TOKEN, "Custom" }, +}; +static u32 battery_supported_modes; + module_param(force_rfkill, bool, 0444); MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); @@ -353,6 +369,32 @@ static const struct dmi_system_id dell_quirks[] __initconst = { { } }; +/* -1 is a sentinel value, telling us to use token->value */ +#define USE_TVAL ((u32) -1) +static int dell_send_request_for_tokenid(struct calling_interface_buffer *buffer, + u16 class, u16 select, u16 tokenid, + u32 val) +{ + struct calling_interface_token *token; + + token = dell_smbios_find_token(tokenid); + if (!token) + return -ENODEV; + + if (val == USE_TVAL) + val = token->value; + + dell_fill_request(buffer, token->location, val, 0, 0); + return dell_send_request(buffer, class, select); +} + +static inline int dell_set_std_token_value(struct calling_interface_buffer *buffer, + u16 tokenid, u32 value) +{ + return dell_send_request_for_tokenid(buffer, CLASS_TOKEN_WRITE, + SELECT_TOKEN_STD, tokenid, value); +} + /* * Derived from information in smbios-wireless-ctl: * @@ -2183,6 +2225,271 @@ static struct led_classdev mute_led_cdev = { .default_trigger = "audio-mute", }; +static int dell_battery_set_mode(const u16 tokenid) +{ + struct calling_interface_buffer buffer; + + return dell_set_std_token_value(&buffer, tokenid, USE_TVAL); +} + +static int dell_battery_read(const u16 tokenid) +{ + struct calling_interface_buffer buffer; + int err; + + err = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ, + SELECT_TOKEN_STD, tokenid, 0); + if (err) + return err; + + if (buffer.output[1] > INT_MAX) + return -EIO; + + return buffer.output[1]; +} + +static bool dell_battery_mode_is_active(const u16 tokenid) +{ + struct calling_interface_token *token; + int ret; + + ret = dell_battery_read(tokenid); + if (ret < 0) + return false; + + token = dell_smbios_find_token(tokenid); + /* token's already verified by dell_battery_read() */ + + return token->value == (u16) ret; +} + +/* + * The rules: the minimum start charging value is 50%. The maximum + * start charging value is 95%. The minimum end charging value is + * 55%. The maximum end charging value is 100%. And finally, there + * has to be at least a 5% difference between start & end values. + */ +#define CHARGE_START_MIN 50 +#define CHARGE_START_MAX 95 +#define CHARGE_END_MIN 55 +#define CHARGE_END_MAX 100 +#define CHARGE_MIN_DIFF 5 + +static int dell_battery_set_custom_charge_start(int start) +{ + struct calling_interface_buffer buffer; + int end; + + start = clamp(start, CHARGE_START_MIN, CHARGE_START_MAX); + end = dell_battery_read(BAT_CUSTOM_CHARGE_END); + if (end < 0) + return end; + if ((end - start) < CHARGE_MIN_DIFF) + start = end - CHARGE_MIN_DIFF; + + return dell_set_std_token_value(&buffer, BAT_CUSTOM_CHARGE_START, + start); +} + +static int dell_battery_set_custom_charge_end(int end) +{ + struct calling_interface_buffer buffer; + int start; + + end = clamp(end, CHARGE_END_MIN, CHARGE_END_MAX); + start = dell_battery_read(BAT_CUSTOM_CHARGE_START); + if (start < 0) + return start; + if ((end - start) < CHARGE_MIN_DIFF) + end = start + CHARGE_MIN_DIFF; + + return dell_set_std_token_value(&buffer, BAT_CUSTOM_CHARGE_END, end); +} + +static ssize_t charge_types_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { + bool active; + + if (!(battery_supported_modes & BIT(i))) + continue; + + active = dell_battery_mode_is_active(battery_modes[i].token); + count += sysfs_emit_at(buf, count, active ? "[%s] " : "%s ", + battery_modes[i].label); + } + + /* convert the last space to a newline */ + if (count > 0) + count--; + count += sysfs_emit_at(buf, count, "\n"); + + return count; +} + +static ssize_t charge_types_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + bool matched = false; + int err, i; + + for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { + if (!(battery_supported_modes & BIT(i))) + continue; + + if (sysfs_streq(battery_modes[i].label, buf)) { + matched = true; + break; + } + } + if (!matched) + return -EINVAL; + + err = dell_battery_set_mode(battery_modes[i].token); + if (err) + return err; + + return size; +} + +static ssize_t charge_control_start_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int start; + + start = dell_battery_read(BAT_CUSTOM_CHARGE_START); + if (start < 0) + return start; + + if (start > CHARGE_START_MAX) + return -EIO; + + return sysfs_emit(buf, "%d\n", start); +} + +static ssize_t charge_control_start_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret, start; + + ret = kstrtoint(buf, 10, &start); + if (ret) + return ret; + if (start < 0 || start > 100) + return -EINVAL; + + ret = dell_battery_set_custom_charge_start(start); + if (ret) + return ret; + + return size; +} + +static ssize_t charge_control_end_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int end; + + end = dell_battery_read(BAT_CUSTOM_CHARGE_END); + if (end < 0) + return end; + + if (end > CHARGE_END_MAX) + return -EIO; + + return sysfs_emit(buf, "%d\n", end); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret, end; + + ret = kstrtouint(buf, 10, &end); + if (ret) + return ret; + if (end < 0 || end > 100) + return -EINVAL; + + ret = dell_battery_set_custom_charge_end(end); + if (ret) + return ret; + + return size; +} + +static DEVICE_ATTR_RW(charge_control_start_threshold); +static DEVICE_ATTR_RW(charge_control_end_threshold); +static DEVICE_ATTR_RW(charge_types); + +static struct attribute *dell_battery_attrs[] = { + &dev_attr_charge_control_start_threshold.attr, + &dev_attr_charge_control_end_threshold.attr, + &dev_attr_charge_types.attr, + NULL, +}; +ATTRIBUTE_GROUPS(dell_battery); + +static int dell_battery_add(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + /* this currently only supports the primary battery */ + if (strcmp(battery->desc->name, "BAT0") != 0) + return -ENODEV; + + return device_add_groups(&battery->dev, dell_battery_groups); +} + +static int dell_battery_remove(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + device_remove_groups(&battery->dev, dell_battery_groups); + return 0; +} + +static struct acpi_battery_hook dell_battery_hook = { + .add_battery = dell_battery_add, + .remove_battery = dell_battery_remove, + .name = "Dell Primary Battery Extension", +}; + +static u32 __init battery_get_supported_modes(void) +{ + u32 modes = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { + if (dell_smbios_find_token(battery_modes[i].token)) + modes |= BIT(i); + } + + return modes; +} + +static void __init dell_battery_init(struct device *dev) +{ + battery_supported_modes = battery_get_supported_modes(); + + if (battery_supported_modes != 0) + battery_hook_register(&dell_battery_hook); +} + +static void dell_battery_exit(void) +{ + if (battery_supported_modes != 0) + battery_hook_unregister(&dell_battery_hook); +} + static int __init dell_init(void) { struct calling_interface_token *token; @@ -2219,6 +2526,7 @@ static int __init dell_init(void) touchpad_led_init(&platform_device->dev); kbd_led_init(&platform_device->dev); + dell_battery_init(&platform_device->dev); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, @@ -2293,6 +2601,7 @@ fail_backlight: if (mute_led_registered) led_classdev_unregister(&mute_led_cdev); fail_led: + dell_battery_exit(); dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); @@ -2311,6 +2620,7 @@ static void __exit dell_exit(void) if (quirks && quirks->touchpad_led) touchpad_led_exit(); kbd_led_exit(); + dell_battery_exit(); backlight_device_unregister(dell_backlight_device); if (micmute_led_registered) led_classdev_unregister(&micmute_led_cdev); diff --git a/drivers/platform/x86/dell/dell-smbios.h b/drivers/platform/x86/dell/dell-smbios.h index ea0cc38642a2..77baa15eb523 100644 --- a/drivers/platform/x86/dell/dell-smbios.h +++ b/drivers/platform/x86/dell/dell-smbios.h @@ -33,6 +33,13 @@ #define KBD_LED_AUTO_50_TOKEN 0x02EB #define KBD_LED_AUTO_75_TOKEN 0x02EC #define KBD_LED_AUTO_100_TOKEN 0x02F6 +#define BAT_PRI_AC_MODE_TOKEN 0x0341 +#define BAT_ADAPTIVE_MODE_TOKEN 0x0342 +#define BAT_CUSTOM_MODE_TOKEN 0x0343 +#define BAT_STANDARD_MODE_TOKEN 0x0346 +#define BAT_EXPRESS_MODE_TOKEN 0x0347 +#define BAT_CUSTOM_CHARGE_START 0x0349 +#define BAT_CUSTOM_CHARGE_END 0x034A #define GLOBAL_MIC_MUTE_ENABLE 0x0364 #define GLOBAL_MIC_MUTE_DISABLE 0x0365 #define GLOBAL_MUTE_ENABLE 0x058C From 66cb96af8394e016df90f51c637b78b59e3429d7 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 20 Aug 2024 03:33:35 -0400 Subject: [PATCH 38/65] platform/x86:dell-laptop: remove duplicate code w/ battery function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dell battery patch added dell_send_request_for_tokenid() and dell_set_std_token_value(), which encapsulates a very common pattern when SMBIOS queries are addressed to token->location. This calls them in various places outside of the dell laptop code, allowing us to delete a bunch of code. Also some very minor cleanups: - mark the kbd init functions as __init - don't read buffer.output unless dell_send_request() was successful. - actually return errors from micmute_led_set/mute_led_set instead of always returning 0. Only minor behavior changes; the delayed read of buffer.output and actually returning errors for the brightness_set_blocking hooks. Signed-off-by: Andres Salomon Reviewed-by: Pali Rohár Link: https://lore.kernel.org/r/20240820033335.4f68b162@5400 Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-laptop.c | 109 ++++++------------------ 1 file changed, 27 insertions(+), 82 deletions(-) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 13428e745796..a3cd0505f282 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -937,43 +937,24 @@ static void dell_cleanup_rfkill(void) static int dell_send_intensity(struct backlight_device *bd) { struct calling_interface_buffer buffer; - struct calling_interface_token *token; - int ret; + u16 select; - token = dell_smbios_find_token(BRIGHTNESS_TOKEN); - if (!token) - return -ENODEV; - - dell_fill_request(&buffer, - token->location, bd->props.brightness, 0, 0); - if (power_supply_is_system_supplied() > 0) - ret = dell_send_request(&buffer, - CLASS_TOKEN_WRITE, SELECT_TOKEN_AC); - else - ret = dell_send_request(&buffer, - CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT); - - return ret; + select = power_supply_is_system_supplied() > 0 ? + SELECT_TOKEN_AC : SELECT_TOKEN_BAT; + return dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_WRITE, + select, BRIGHTNESS_TOKEN, bd->props.brightness); } static int dell_get_intensity(struct backlight_device *bd) { struct calling_interface_buffer buffer; - struct calling_interface_token *token; int ret; + u16 select; - token = dell_smbios_find_token(BRIGHTNESS_TOKEN); - if (!token) - return -ENODEV; - - dell_fill_request(&buffer, token->location, 0, 0, 0); - if (power_supply_is_system_supplied() > 0) - ret = dell_send_request(&buffer, - CLASS_TOKEN_READ, SELECT_TOKEN_AC); - else - ret = dell_send_request(&buffer, - CLASS_TOKEN_READ, SELECT_TOKEN_BAT); - + select = power_supply_is_system_supplied() > 0 ? + SELECT_TOKEN_AC : SELECT_TOKEN_BAT; + ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ, + select, BRIGHTNESS_TOKEN, 0); if (ret == 0) ret = buffer.output[1]; @@ -1397,20 +1378,11 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) static int kbd_set_token_bit(u8 bit) { struct calling_interface_buffer buffer; - struct calling_interface_token *token; - int ret; if (bit >= ARRAY_SIZE(kbd_tokens)) return -EINVAL; - token = dell_smbios_find_token(kbd_tokens[bit]); - if (!token) - return -EINVAL; - - dell_fill_request(&buffer, token->location, token->value, 0, 0); - ret = dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); - - return ret; + return dell_set_std_token_value(&buffer, kbd_tokens[bit], USE_TVAL); } static int kbd_get_token_bit(u8 bit) @@ -1429,11 +1401,10 @@ static int kbd_get_token_bit(u8 bit) dell_fill_request(&buffer, token->location, 0, 0, 0); ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD); - val = buffer.output[1]; - if (ret) return ret; + val = buffer.output[1]; return (val == token->value); } @@ -1539,7 +1510,7 @@ static inline int kbd_init_info(void) } -static inline void kbd_init_tokens(void) +static inline void __init kbd_init_tokens(void) { int i; @@ -1548,7 +1519,7 @@ static inline void kbd_init_tokens(void) kbd_token_bits |= BIT(i); } -static void kbd_init(void) +static void __init kbd_init(void) { int ret; @@ -2173,21 +2144,11 @@ static int micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct calling_interface_buffer buffer; - struct calling_interface_token *token; - int state = brightness != LED_OFF; + u32 tokenid; - if (state == 0) - token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE); - else - token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE); - - if (!token) - return -ENODEV; - - dell_fill_request(&buffer, token->location, token->value, 0, 0); - dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); - - return 0; + tokenid = brightness == LED_OFF ? + GLOBAL_MIC_MUTE_DISABLE : GLOBAL_MIC_MUTE_ENABLE; + return dell_set_std_token_value(&buffer, tokenid, USE_TVAL); } static struct led_classdev micmute_led_cdev = { @@ -2201,21 +2162,11 @@ static int mute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct calling_interface_buffer buffer; - struct calling_interface_token *token; - int state = brightness != LED_OFF; + u32 tokenid; - if (state == 0) - token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE); - else - token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE); - - if (!token) - return -ENODEV; - - dell_fill_request(&buffer, token->location, token->value, 0, 0); - dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); - - return 0; + tokenid = brightness == LED_OFF ? + GLOBAL_MUTE_DISABLE : GLOBAL_MUTE_ENABLE; + return dell_set_std_token_value(&buffer, tokenid, USE_TVAL); } static struct led_classdev mute_led_cdev = { @@ -2492,7 +2443,7 @@ static void dell_battery_exit(void) static int __init dell_init(void) { - struct calling_interface_token *token; + struct calling_interface_buffer buffer; int max_intensity = 0; int ret; @@ -2554,16 +2505,10 @@ static int __init dell_init(void) if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return 0; - token = dell_smbios_find_token(BRIGHTNESS_TOKEN); - if (token) { - struct calling_interface_buffer buffer; - - dell_fill_request(&buffer, token->location, 0, 0, 0); - ret = dell_send_request(&buffer, - CLASS_TOKEN_READ, SELECT_TOKEN_AC); - if (ret == 0) - max_intensity = buffer.output[3]; - } + ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ, + SELECT_TOKEN_AC, BRIGHTNESS_TOKEN, 0); + if (ret == 0) + max_intensity = buffer.output[3]; if (max_intensity) { struct backlight_properties props; From d2b27d8eb8795736358706c3c5ab3b572f641d3a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Aug 2024 16:05:40 +0300 Subject: [PATCH 39/65] platform/x86: int3472: Use GPIO_LOOKUP() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use GPIO_LOOKUP() macro which provides a compound literal and can be used with dynamic data. Reviewed-by: Ilpo Järvinen Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240822130722.1261891-4-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int3472/discrete.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 07b302e09340..89a97d2cd625 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -69,11 +69,7 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry, if (!adev) return -ENODEV; - table_entry->key = acpi_dev_name(adev); - table_entry->chip_hwnum = agpio->pin_table[0]; - table_entry->con_id = func; - table_entry->idx = 0; - table_entry->flags = polarity; + *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity); return 0; } From 1bda29aef6e19662496244b2b3f7eb2a6edf0cd5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Aug 2024 16:05:41 +0300 Subject: [PATCH 40/65] platform/x86: int3472: Use str_high_low() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use str_high_low() rather than open coding. Reviewed-by: Ilpo Järvinen Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240822130722.1261891-5-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int3472/discrete.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 89a97d2cd625..3de463c3d13b 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "common.h" @@ -230,7 +231,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func, agpio->resource_source.string_ptr, agpio->pin_table[0], - (polarity == GPIO_ACTIVE_HIGH) ? "high" : "low"); + str_high_low(polarity == GPIO_ACTIVE_HIGH)); switch (type) { case INT3472_GPIO_TYPE_RESET: From 298c9babadb83844632144369cc6bb2a43221aea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Aug 2024 22:25:04 -0700 Subject: [PATCH 41/65] x86/platform/geode: switch GPIO buttons and LEDs to software properties Convert GPIO-connected buttons and LEDs in Geode boards to software nodes/properties, so that support for platform data can be removed from gpio-keys driver (which will rely purely on generic device properties for configuration). To avoid repeating the same data structures over and over and over factor them out into a new geode-common.c file. Signed-off-by: Dmitry Torokhov Reviewed-by: Hans de Goede Acked-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/ZsV6MNS_tUPPSffJ@google.com Signed-off-by: Hans de Goede --- arch/x86/Kconfig | 6 + arch/x86/platform/geode/Makefile | 1 + arch/x86/platform/geode/alix.c | 82 ++---------- arch/x86/platform/geode/geode-common.c | 178 +++++++++++++++++++++++++ arch/x86/platform/geode/geode-common.h | 21 +++ arch/x86/platform/geode/geos.c | 80 +---------- arch/x86/platform/geode/net5501.c | 69 +--------- 7 files changed, 228 insertions(+), 209 deletions(-) create mode 100644 arch/x86/platform/geode/geode-common.c create mode 100644 arch/x86/platform/geode/geode-common.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 007bab9f2a0e..ee15748f4f1e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2979,9 +2979,13 @@ config OLPC_XO15_SCI - AC adapter status updates - Battery status updates +config GEODE_COMMON + bool + config ALIX bool "PCEngines ALIX System Support (LED setup)" select GPIOLIB + select GEODE_COMMON help This option enables system support for the PCEngines ALIX. At present this just sets up LEDs for GPIO control on @@ -2996,12 +3000,14 @@ config ALIX config NET5501 bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)" select GPIOLIB + select GEODE_COMMON help This option enables system support for the Soekris Engineering net5501. config GEOS bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)" select GPIOLIB + select GEODE_COMMON depends on DMI help This option enables system support for the Traverse Technologies GEOS. diff --git a/arch/x86/platform/geode/Makefile b/arch/x86/platform/geode/Makefile index a8a6b1dedb01..34b53e97a0ad 100644 --- a/arch/x86/platform/geode/Makefile +++ b/arch/x86/platform/geode/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_GEODE_COMMON) += geode-common.o obj-$(CONFIG_ALIX) += alix.o obj-$(CONFIG_NET5501) += net5501.o obj-$(CONFIG_GEOS) += geos.o diff --git a/arch/x86/platform/geode/alix.c b/arch/x86/platform/geode/alix.c index b39bf3b5e108..be65cd704e21 100644 --- a/arch/x86/platform/geode/alix.c +++ b/arch/x86/platform/geode/alix.c @@ -18,15 +18,12 @@ #include #include #include -#include -#include -#include -#include -#include #include #include +#include "geode-common.h" + #define BIOS_SIGNATURE_TINYBIOS 0xf0000 #define BIOS_SIGNATURE_COREBOOT 0x500 #define BIOS_REGION_SIZE 0x10000 @@ -41,79 +38,16 @@ module_param(force, bool, 0444); /* FIXME: Award bios is not automatically detected as Alix platform */ MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); -static struct gpio_keys_button alix_gpio_buttons[] = { - { - .code = KEY_RESTART, - .gpio = 24, - .active_low = 1, - .desc = "Reset button", - .type = EV_KEY, - .wakeup = 0, - .debounce_interval = 100, - .can_disable = 0, - } -}; -static struct gpio_keys_platform_data alix_buttons_data = { - .buttons = alix_gpio_buttons, - .nbuttons = ARRAY_SIZE(alix_gpio_buttons), - .poll_interval = 20, -}; - -static struct platform_device alix_buttons_dev = { - .name = "gpio-keys-polled", - .id = 1, - .dev = { - .platform_data = &alix_buttons_data, - } -}; - -static struct gpio_led alix_leds[] = { - { - .name = "alix:1", - .default_trigger = "default-on", - }, - { - .name = "alix:2", - .default_trigger = "default-off", - }, - { - .name = "alix:3", - .default_trigger = "default-off", - }, -}; - -static struct gpio_led_platform_data alix_leds_data = { - .num_leds = ARRAY_SIZE(alix_leds), - .leds = alix_leds, -}; - -static struct gpiod_lookup_table alix_leds_gpio_table = { - .dev_id = "leds-gpio", - .table = { - /* The Geode GPIOs should be on the CS5535 companion chip */ - GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct platform_device alix_leds_dev = { - .name = "leds-gpio", - .id = -1, - .dev.platform_data = &alix_leds_data, -}; - -static struct platform_device *alix_devs[] __initdata = { - &alix_buttons_dev, - &alix_leds_dev, +static const struct geode_led alix_leds[] __initconst = { + { 6, true }, + { 25, false }, + { 27, false }, }; static void __init register_alix(void) { - /* Setup LED control through leds-gpio driver */ - gpiod_add_lookup_table(&alix_leds_gpio_table); - platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs)); + geode_create_restart_key(24); + geode_create_leds("alix", alix_leds, ARRAY_SIZE(alix_leds)); } static bool __init alix_present(unsigned long bios_phys, diff --git a/arch/x86/platform/geode/geode-common.c b/arch/x86/platform/geode/geode-common.c new file mode 100644 index 000000000000..8fd78e60bf15 --- /dev/null +++ b/arch/x86/platform/geode/geode-common.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Shared helpers to register GPIO-connected buttons and LEDs + * on AMD Geode boards. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "geode-common.h" + +static const struct software_node geode_gpiochip_node = { + .name = "cs5535-gpio", +}; + +static const struct property_entry geode_gpio_keys_props[] = { + PROPERTY_ENTRY_U32("poll-interval", 20), + { } +}; + +static const struct software_node geode_gpio_keys_node = { + .name = "geode-gpio-keys", + .properties = geode_gpio_keys_props, +}; + +static struct property_entry geode_restart_key_props[] = { + { /* Placeholder for GPIO property */ }, + PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), + PROPERTY_ENTRY_STRING("label", "Reset button"), + PROPERTY_ENTRY_U32("debounce-interval", 100), + { } +}; + +static const struct software_node geode_restart_key_node = { + .parent = &geode_gpio_keys_node, + .properties = geode_restart_key_props, +}; + +static const struct software_node *geode_gpio_keys_swnodes[] __initconst = { + &geode_gpiochip_node, + &geode_gpio_keys_node, + &geode_restart_key_node, + NULL +}; + +/* + * Creates gpio-keys-polled device for the restart key. + * + * Note that it needs to be called first, before geode_create_leds(), + * because it registers gpiochip software node used by both gpio-keys and + * leds-gpio devices. + */ +int __init geode_create_restart_key(unsigned int pin) +{ + struct platform_device_info keys_info = { + .name = "gpio-keys-polled", + .id = 1, + }; + struct platform_device *pd; + int err; + + geode_restart_key_props[0] = PROPERTY_ENTRY_GPIO("gpios", + &geode_gpiochip_node, + pin, GPIO_ACTIVE_LOW); + + err = software_node_register_node_group(geode_gpio_keys_swnodes); + if (err) { + pr_err("failed to register gpio-keys software nodes: %d\n", err); + return err; + } + + keys_info.fwnode = software_node_fwnode(&geode_gpio_keys_node); + + pd = platform_device_register_full(&keys_info); + err = PTR_ERR_OR_ZERO(pd); + if (err) { + pr_err("failed to create gpio-keys device: %d\n", err); + software_node_unregister_node_group(geode_gpio_keys_swnodes); + return err; + } + + return 0; +} + +static const struct software_node geode_gpio_leds_node = { + .name = "geode-leds", +}; + +#define MAX_LEDS 3 + +int __init geode_create_leds(const char *label, const struct geode_led *leds, + unsigned int n_leds) +{ + const struct software_node *group[MAX_LEDS + 2] = { 0 }; + struct software_node *swnodes; + struct property_entry *props; + struct platform_device_info led_info = { + .name = "leds-gpio", + .id = PLATFORM_DEVID_NONE, + }; + struct platform_device *led_dev; + const char *node_name; + int err; + int i; + + if (n_leds > MAX_LEDS) { + pr_err("%s: too many LEDs\n", __func__); + return -EINVAL; + } + + swnodes = kcalloc(n_leds, sizeof(*swnodes), GFP_KERNEL); + if (!swnodes) + return -ENOMEM; + + /* + * Each LED is represented by 3 properties: "gpios", + * "linux,default-trigger", and am empty terminator. + */ + props = kcalloc(n_leds * 3, sizeof(*props), GFP_KERNEL); + if (!props) { + err = -ENOMEM; + goto err_free_swnodes; + } + + group[0] = &geode_gpio_leds_node; + for (i = 0; i < n_leds; i++) { + node_name = kasprintf(GFP_KERNEL, "%s:%d", label, i); + if (!node_name) { + err = -ENOMEM; + goto err_free_names; + } + + props[i * 3 + 0] = + PROPERTY_ENTRY_GPIO("gpios", &geode_gpiochip_node, + leds[i].pin, GPIO_ACTIVE_LOW); + props[i * 3 + 1] = + PROPERTY_ENTRY_STRING("linux,default-trigger", + leds[i].default_on ? + "default-on" : "default-off"); + /* props[i * 3 + 2] is an empty terminator */ + + swnodes[i] = SOFTWARE_NODE(node_name, &props[i * 3], + &geode_gpio_leds_node); + group[i + 1] = &swnodes[i]; + } + + err = software_node_register_node_group(group); + if (err) { + pr_err("failed to register LED software nodes: %d\n", err); + goto err_free_names; + } + + led_info.fwnode = software_node_fwnode(&geode_gpio_leds_node); + + led_dev = platform_device_register_full(&led_info); + err = PTR_ERR_OR_ZERO(led_dev); + if (err) { + pr_err("failed to create LED device: %d\n", err); + goto err_unregister_group; + } + + return 0; + +err_unregister_group: + software_node_unregister_node_group(group); +err_free_names: + while (--i >= 0) + kfree(swnodes[i].name); + kfree(props); +err_free_swnodes: + kfree(swnodes); + return err; +} diff --git a/arch/x86/platform/geode/geode-common.h b/arch/x86/platform/geode/geode-common.h new file mode 100644 index 000000000000..9e0afd34bfad --- /dev/null +++ b/arch/x86/platform/geode/geode-common.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Shared helpers to register GPIO-connected buttons and LEDs + * on AMD Geode boards. + */ + +#ifndef __PLATFORM_GEODE_COMMON_H +#define __PLATFORM_GEODE_COMMON_H + +#include + +struct geode_led { + unsigned int pin; + bool default_on; +}; + +int geode_create_restart_key(unsigned int pin); +int geode_create_leds(const char *label, const struct geode_led *leds, + unsigned int n_leds); + +#endif /* __PLATFORM_GEODE_COMMON_H */ diff --git a/arch/x86/platform/geode/geos.c b/arch/x86/platform/geode/geos.c index d263528c90bb..98027fb1ec32 100644 --- a/arch/x86/platform/geode/geos.c +++ b/arch/x86/platform/geode/geos.c @@ -16,88 +16,22 @@ #include #include #include -#include -#include -#include -#include -#include #include #include -static struct gpio_keys_button geos_gpio_buttons[] = { - { - .code = KEY_RESTART, - .gpio = 3, - .active_low = 1, - .desc = "Reset button", - .type = EV_KEY, - .wakeup = 0, - .debounce_interval = 100, - .can_disable = 0, - } -}; -static struct gpio_keys_platform_data geos_buttons_data = { - .buttons = geos_gpio_buttons, - .nbuttons = ARRAY_SIZE(geos_gpio_buttons), - .poll_interval = 20, -}; +#include "geode-common.h" -static struct platform_device geos_buttons_dev = { - .name = "gpio-keys-polled", - .id = 1, - .dev = { - .platform_data = &geos_buttons_data, - } -}; - -static struct gpio_led geos_leds[] = { - { - .name = "geos:1", - .default_trigger = "default-on", - }, - { - .name = "geos:2", - .default_trigger = "default-off", - }, - { - .name = "geos:3", - .default_trigger = "default-off", - }, -}; - -static struct gpio_led_platform_data geos_leds_data = { - .num_leds = ARRAY_SIZE(geos_leds), - .leds = geos_leds, -}; - -static struct gpiod_lookup_table geos_leds_gpio_table = { - .dev_id = "leds-gpio", - .table = { - /* The Geode GPIOs should be on the CS5535 companion chip */ - GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct platform_device geos_leds_dev = { - .name = "leds-gpio", - .id = -1, - .dev.platform_data = &geos_leds_data, -}; - -static struct platform_device *geos_devs[] __initdata = { - &geos_buttons_dev, - &geos_leds_dev, +static const struct geode_led geos_leds[] __initconst = { + { 6, true }, + { 25, false }, + { 27, false }, }; static void __init register_geos(void) { - /* Setup LED control through leds-gpio driver */ - gpiod_add_lookup_table(&geos_leds_gpio_table); - platform_add_devices(geos_devs, ARRAY_SIZE(geos_devs)); + geode_create_restart_key(3); + geode_create_leds("geos", geos_leds, ARRAY_SIZE(geos_leds)); } static int __init geos_init(void) diff --git a/arch/x86/platform/geode/net5501.c b/arch/x86/platform/geode/net5501.c index 558384acd777..c9cee7dea99b 100644 --- a/arch/x86/platform/geode/net5501.c +++ b/arch/x86/platform/geode/net5501.c @@ -16,80 +16,25 @@ #include #include #include -#include -#include #include -#include #include +#include #include +#include "geode-common.h" + #define BIOS_REGION_BASE 0xffff0000 #define BIOS_REGION_SIZE 0x00010000 -static struct gpio_keys_button net5501_gpio_buttons[] = { - { - .code = KEY_RESTART, - .gpio = 24, - .active_low = 1, - .desc = "Reset button", - .type = EV_KEY, - .wakeup = 0, - .debounce_interval = 100, - .can_disable = 0, - } -}; -static struct gpio_keys_platform_data net5501_buttons_data = { - .buttons = net5501_gpio_buttons, - .nbuttons = ARRAY_SIZE(net5501_gpio_buttons), - .poll_interval = 20, -}; - -static struct platform_device net5501_buttons_dev = { - .name = "gpio-keys-polled", - .id = 1, - .dev = { - .platform_data = &net5501_buttons_data, - } -}; - -static struct gpio_led net5501_leds[] = { - { - .name = "net5501:1", - .default_trigger = "default-on", - }, -}; - -static struct gpio_led_platform_data net5501_leds_data = { - .num_leds = ARRAY_SIZE(net5501_leds), - .leds = net5501_leds, -}; - -static struct gpiod_lookup_table net5501_leds_gpio_table = { - .dev_id = "leds-gpio", - .table = { - /* The Geode GPIOs should be on the CS5535 companion chip */ - GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct platform_device net5501_leds_dev = { - .name = "leds-gpio", - .id = -1, - .dev.platform_data = &net5501_leds_data, -}; - -static struct platform_device *net5501_devs[] __initdata = { - &net5501_buttons_dev, - &net5501_leds_dev, +static const struct geode_led net5501_leds[] __initconst = { + { 6, true }, }; static void __init register_net5501(void) { - /* Setup LED control through leds-gpio driver */ - gpiod_add_lookup_table(&net5501_leds_gpio_table); - platform_add_devices(net5501_devs, ARRAY_SIZE(net5501_devs)); + geode_create_restart_key(24); + geode_create_leds("net5501", net5501_leds, ARRAY_SIZE(net5501_leds)); } struct net5501_board { From d9dca215708d32e7f88ac0591fbb187cbf368adb Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Thu, 22 Aug 2024 17:38:57 +0200 Subject: [PATCH 42/65] platform/x86: lenovo-ymc: Ignore the 0x0 state While booting, Lenovo 14ARB7 reports 'lenovo-ymc: Unknown key 0 pressed' warning. This is caused by lenovo_ymc_probe() calling lenovo_ymc_notify() at probe time to get the initial tablet-mode-switch state and the key-code lenovo_ymc_notify() reads from the firmware is not initialized at probe time yet on the Lenovo 14ARB7. The hardware/firmware does an ACPI notify on the WMI device itself when it initializes the tablet-mode-switch state later on. Add 0x0 YMC state to the sparse keymap to silence the warning. Signed-off-by: Gergo Koteles Link: https://lore.kernel.org/r/08ab73bb74c4ad448409f2ce707b1148874a05ce.1724340562.git.soyer@irl.hu [hdegoede@redhat.com: Reword commit message] Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/lenovo-ymc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/lenovo-ymc.c b/drivers/platform/x86/lenovo-ymc.c index e0bbd6a14a89..bd9f95404c7c 100644 --- a/drivers/platform/x86/lenovo-ymc.c +++ b/drivers/platform/x86/lenovo-ymc.c @@ -43,6 +43,8 @@ struct lenovo_ymc_private { }; static const struct key_entry lenovo_ymc_keymap[] = { + /* Ignore the uninitialized state */ + { KE_IGNORE, 0x00 }, /* Laptop */ { KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } }, /* Tablet */ From 8022ae2c435f09f029a68235e3bb49e1e0b5b686 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 28 Aug 2024 18:34:53 +0300 Subject: [PATCH 43/65] Documentation: admin-guide: pm: Add efficiency vs. latency tradeoff to uncore documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added documentation about the functionality of efficiency vs. latency tradeoff control in intel Xeon processors, and how this is configured via sysfs. Signed-off-by: Tero Kristo Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240828153657.1296410-2-tero.kristo@linux.intel.com Signed-off-by: Hans de Goede --- .../pm/intel_uncore_frequency_scaling.rst | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst index 5ab3440e6cee..5151ec312dc0 100644 --- a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst +++ b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst @@ -113,3 +113,62 @@ to apply at each uncore* level. Support for "current_freq_khz" is available only at each fabric cluster level (i.e., in uncore* directory). + +Efficiency vs. Latency Tradeoff +------------------------------- + +The Efficiency Latency Control (ELC) feature improves performance +per watt. With this feature hardware power management algorithms +optimize trade-off between latency and power consumption. For some +latency sensitive workloads further tuning can be done by SW to +get desired performance. + +The hardware monitors the average CPU utilization across all cores +in a power domain at regular intervals and decides an uncore frequency. +While this may result in the best performance per watt, workload may be +expecting higher performance at the expense of power. Consider an +application that intermittently wakes up to perform memory reads on an +otherwise idle system. In such cases, if hardware lowers uncore +frequency, then there may be delay in ramp up of frequency to meet +target performance. + +The ELC control defines some parameters which can be changed from SW. +If the average CPU utilization is below a user-defined threshold +(elc_low_threshold_percent attribute below), the user-defined uncore +floor frequency will be used (elc_floor_freq_khz attribute below) +instead of hardware calculated minimum. + +Similarly in high load scenario where the CPU utilization goes above +the high threshold value (elc_high_threshold_percent attribute below) +instead of jumping to maximum uncore frequency, frequency is increased +in 100MHz steps. This avoids consuming unnecessarily high power +immediately with CPU utilization spikes. + +Attributes for efficiency latency control: + +``elc_floor_freq_khz`` + This attribute is used to get/set the efficiency latency floor frequency. + If this variable is lower than the 'min_freq_khz', it is ignored by + the firmware. + +``elc_low_threshold_percent`` + This attribute is used to get/set the efficiency latency control low + threshold. This attribute is in percentages of CPU utilization. + +``elc_high_threshold_percent`` + This attribute is used to get/set the efficiency latency control high + threshold. This attribute is in percentages of CPU utilization. + +``elc_high_threshold_enable`` + This attribute is used to enable/disable the efficiency latency control + high threshold. Write '1' to enable, '0' to disable. + +Example system configuration below, which does following: + * when CPU utilization is less than 10%: sets uncore frequency to 800MHz + * when CPU utilization is higher than 95%: increases uncore frequency in + 100MHz steps, until power limit is reached + + elc_floor_freq_khz:800000 + elc_high_threshold_percent:95 + elc_high_threshold_enable:1 + elc_low_threshold_percent:10 From bb516dc79c4a6334d5ef6bdbd0d262cf8be9db8e Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 28 Aug 2024 18:34:54 +0300 Subject: [PATCH 44/65] platform/x86/intel-uncore-freq: Add support for efficiency latency control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add efficiency latency control support to the TPMI uncore driver. This defines two new threshold values for controlling uncore frequency, low threshold and high threshold. When CPU utilization is below low threshold, the user configurable floor latency control frequency can be used by the system. When CPU utilization is above high threshold, the uncore frequency is increased in 100MHz steps until power limit is reached. Signed-off-by: Tero Kristo Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240828153657.1296410-3-tero.kristo@linux.intel.com Signed-off-by: Hans de Goede --- .../uncore-frequency-common.h | 4 + .../uncore-frequency/uncore-frequency-tpmi.c | 158 +++++++++++++++++- 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 4c245b945e4e..b5c7311bfa05 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -70,6 +70,10 @@ enum uncore_index { UNCORE_INDEX_MIN_FREQ, UNCORE_INDEX_MAX_FREQ, UNCORE_INDEX_CURRENT_FREQ, + UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD, + UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD, + UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE, + UNCORE_INDEX_EFF_LAT_CTRL_FREQ, }; int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value, diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 6c2e607968f2..0591053813a2 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -30,6 +30,7 @@ #define UNCORE_MAJOR_VERSION 0 #define UNCORE_MINOR_VERSION 2 +#define UNCORE_ELC_SUPPORTED_VERSION 2 #define UNCORE_HEADER_INDEX 0 #define UNCORE_FABRIC_CLUSTER_OFFSET 8 @@ -46,6 +47,7 @@ struct tpmi_uncore_struct; /* Information for each cluster */ struct tpmi_uncore_cluster_info { bool root_domain; + bool elc_supported; u8 __iomem *cluster_base; struct uncore_data uncore_data; struct tpmi_uncore_struct *uncore_root; @@ -75,6 +77,10 @@ struct tpmi_uncore_struct { /* Bit definitions for CONTROL register */ #define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8) #define UNCORE_MIN_RATIO_MASK GENMASK_ULL(21, 15) +#define UNCORE_EFF_LAT_CTRL_RATIO_MASK GENMASK_ULL(28, 22) +#define UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK GENMASK_ULL(38, 32) +#define UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE BIT(39) +#define UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK GENMASK_ULL(46, 40) /* Helper function to read MMIO offset for max/min control frequency */ static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info, @@ -89,6 +95,48 @@ static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info, *value = FIELD_GET(UNCORE_MIN_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER; } +/* Helper function to read efficiency latency control values over MMIO */ +static int read_eff_lat_ctrl(struct uncore_data *data, unsigned int *val, enum uncore_index index) +{ + struct tpmi_uncore_cluster_info *cluster_info; + u64 ctrl; + + cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data); + if (cluster_info->root_domain) + return -ENODATA; + + if (!cluster_info->elc_supported) + return -EOPNOTSUPP; + + ctrl = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX); + + switch (index) { + case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD: + *val = FIELD_GET(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK, ctrl); + *val *= 100; + *val = DIV_ROUND_UP(*val, FIELD_MAX(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK)); + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD: + *val = FIELD_GET(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK, ctrl); + *val *= 100; + *val = DIV_ROUND_UP(*val, FIELD_MAX(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK)); + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE: + *val = FIELD_GET(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE, ctrl); + break; + case UNCORE_INDEX_EFF_LAT_CTRL_FREQ: + *val = FIELD_GET(UNCORE_EFF_LAT_CTRL_RATIO_MASK, ctrl) * UNCORE_FREQ_KHZ_MULTIPLIER; + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + #define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_MAX_RATIO_MASK) /* Helper for sysfs read for max/min frequencies. Called under mutex locks */ @@ -137,6 +185,82 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *valu return 0; } +/* Helper function for writing efficiency latency control values over MMIO */ +static int write_eff_lat_ctrl(struct uncore_data *data, unsigned int val, enum uncore_index index) +{ + struct tpmi_uncore_cluster_info *cluster_info; + u64 control; + + cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data); + + if (cluster_info->root_domain) + return -ENODATA; + + if (!cluster_info->elc_supported) + return -EOPNOTSUPP; + + switch (index) { + case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD: + if (val > 100) + return -EINVAL; + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD: + if (val > 100) + return -EINVAL; + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE: + if (val > 1) + return -EINVAL; + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_FREQ: + val /= UNCORE_FREQ_KHZ_MULTIPLIER; + if (val > FIELD_MAX(UNCORE_EFF_LAT_CTRL_RATIO_MASK)) + return -EINVAL; + break; + + default: + return -EOPNOTSUPP; + } + + control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX); + + switch (index) { + case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD: + val *= FIELD_MAX(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK); + val /= 100; + control &= ~UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK; + control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK, val); + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD: + val *= FIELD_MAX(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK); + val /= 100; + control &= ~UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK; + control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK, val); + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE: + control &= ~UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE; + control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE, val); + break; + + case UNCORE_INDEX_EFF_LAT_CTRL_FREQ: + control &= ~UNCORE_EFF_LAT_CTRL_RATIO_MASK; + control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_RATIO_MASK, val); + break; + + default: + break; + } + + writeq(control, cluster_info->cluster_base + UNCORE_CONTROL_INDEX); + + return 0; +} + /* Helper function to write MMIO offset for max/min control frequency */ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, unsigned int input, unsigned int index) @@ -156,7 +280,7 @@ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, un writeq(control, (cluster_info->cluster_base + UNCORE_CONTROL_INDEX)); } -/* Callback for sysfs write for max/min frequencies. Called under mutex locks */ +/* Helper for sysfs write for max/min frequencies. Called under mutex locks */ static int uncore_write_control_freq(struct uncore_data *data, unsigned int input, enum uncore_index index) { @@ -234,6 +358,33 @@ static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncor case UNCORE_INDEX_CURRENT_FREQ: return uncore_read_freq(data, value); + case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD: + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD: + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE: + case UNCORE_INDEX_EFF_LAT_CTRL_FREQ: + return read_eff_lat_ctrl(data, value, index); + + default: + break; + } + + return -EOPNOTSUPP; +} + +/* Callback for sysfs write for TPMI uncore data. Called under mutex locks. */ +static int uncore_write(struct uncore_data *data, unsigned int value, enum uncore_index index) +{ + switch (index) { + case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD: + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD: + case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE: + case UNCORE_INDEX_EFF_LAT_CTRL_FREQ: + return write_eff_lat_ctrl(data, value, index); + + case UNCORE_INDEX_MIN_FREQ: + case UNCORE_INDEX_MAX_FREQ: + return uncore_write_control_freq(data, value, index); + default: break; } @@ -291,7 +442,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ return -EINVAL; /* Register callbacks to uncore core */ - ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq); + ret = uncore_freq_common_init(uncore_read, uncore_write); if (ret) return ret; @@ -409,6 +560,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ cluster_info->uncore_root = tpmi_uncore; + if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) >= UNCORE_ELC_SUPPORTED_VERSION) + cluster_info->elc_supported = true; + ret = uncore_freq_add_entry(&cluster_info->uncore_data, 0); if (ret) { cluster_info->cluster_base = NULL; From 24b6616355f7eb2a839776578b104a0348b4f77f Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 28 Aug 2024 18:34:55 +0300 Subject: [PATCH 45/65] platform/x86/intel-uncore-freq: Add efficiency latency control to sysfs interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the TPMI efficiency latency control fields to the sysfs interface. The sysfs files are mapped to the TPMI uncore driver via the registered uncore_read and uncore_write driver callbacks. These fields are not populated on older non TPMI hardware. Signed-off-by: Tero Kristo Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240828153657.1296410-4-tero.kristo@linux.intel.com Signed-off-by: Hans de Goede --- .../uncore-frequency-common.c | 42 ++++++++++++++++--- .../uncore-frequency-common.h | 13 +++++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 4e880585cbe4..e22b683a7a43 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -60,11 +60,16 @@ static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count, enum uncore_index index) { - unsigned int input; + unsigned int input = 0; int ret; - if (kstrtouint(buf, 10, &input)) - return -EINVAL; + if (index == UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE) { + if (kstrtobool(buf, (bool *)&input)) + return -EINVAL; + } else { + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + } mutex_lock(&uncore_lock); ret = uncore_write(data, input, index); @@ -103,6 +108,18 @@ show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ); show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ); +store_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); +store_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD); +store_uncore_attr(elc_high_threshold_enable, + UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE); +store_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ); + +show_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); +show_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD); +show_uncore_attr(elc_high_threshold_enable, + UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE); +show_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ); + #define show_uncore_data(member_name) \ static ssize_t show_##member_name(struct kobject *kobj, \ struct kobj_attribute *attr, char *buf)\ @@ -146,7 +163,8 @@ show_uncore_data(initial_max_freq_khz); static int create_attr_group(struct uncore_data *data, char *name) { - int ret, freq, index = 0; + int ret, index = 0; + unsigned int val; init_attribute_rw(max_freq_khz); init_attribute_rw(min_freq_khz); @@ -168,10 +186,24 @@ static int create_attr_group(struct uncore_data *data, char *name) data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr; data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr; - ret = uncore_read(data, &freq, UNCORE_INDEX_CURRENT_FREQ); + ret = uncore_read(data, &val, UNCORE_INDEX_CURRENT_FREQ); if (!ret) data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr; + ret = uncore_read(data, &val, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); + if (!ret) { + init_attribute_rw(elc_low_threshold_percent); + init_attribute_rw(elc_high_threshold_percent); + init_attribute_rw(elc_high_threshold_enable); + init_attribute_rw(elc_floor_freq_khz); + + data->uncore_attrs[index++] = &data->elc_low_threshold_percent_kobj_attr.attr; + data->uncore_attrs[index++] = &data->elc_high_threshold_percent_kobj_attr.attr; + data->uncore_attrs[index++] = + &data->elc_high_threshold_enable_kobj_attr.attr; + data->uncore_attrs[index++] = &data->elc_floor_freq_khz_kobj_attr.attr; + } + data->uncore_attrs[index] = NULL; data->uncore_attr_group.name = name; diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index b5c7311bfa05..26c854cd5d97 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -34,6 +34,13 @@ * @domain_id_kobj_attr: Storage for kobject attribute domain_id * @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id * @package_id_kobj_attr: Storage for kobject attribute package_id + * @elc_low_threshold_percent_kobj_attr: + Storage for kobject attribute elc_low_threshold_percent + * @elc_high_threshold_percent_kobj_attr: + Storage for kobject attribute elc_high_threshold_percent + * @elc_high_threshold_enable_kobj_attr: + Storage for kobject attribute elc_high_threshold_enable + * @elc_floor_freq_khz_kobj_attr: Storage for kobject attribute elc_floor_freq_khz * @uncore_attrs: Attribute storage for group creation * * This structure is used to encapsulate all data related to uncore sysfs @@ -61,7 +68,11 @@ struct uncore_data { struct kobj_attribute domain_id_kobj_attr; struct kobj_attribute fabric_cluster_id_kobj_attr; struct kobj_attribute package_id_kobj_attr; - struct attribute *uncore_attrs[9]; + struct kobj_attribute elc_low_threshold_percent_kobj_attr; + struct kobj_attribute elc_high_threshold_percent_kobj_attr; + struct kobj_attribute elc_high_threshold_enable_kobj_attr; + struct kobj_attribute elc_floor_freq_khz_kobj_attr; + struct attribute *uncore_attrs[13]; }; #define UNCORE_DOMAIN_ID_INVALID -1 From a093cb667c3ff5eadd4b23ddf996d9ccae9b7ac6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 29 Aug 2024 19:50:32 +0300 Subject: [PATCH 46/65] platform/x86: ideapad-laptop: Make the scope_guard() clear of its scope First of all, it's a bit counterintuitive to have something like int err; ... scoped_guard(...) err = foo(...); if (err) return err; Second, with a particular kernel configuration and compiler version in one of such cases the objtool is not happy: ideapad-laptop.o: warning: objtool: .text.fan_mode_show: unexpected end of section I'm not an expert on all this, but the theory is that compiler and linker in this case can't understand that 'result' variable will be always initialized as long as no error has been returned. Assigning 'result' to a dummy value helps with this. Note, that fixing the scoped_guard() scope (as per above) does not make issue gone. That said, assign dummy value and make the scope_guard() clear of its scope. For the sake of consistency do it in the entire file. Fixes: 7cc06e729460 ("platform/x86: ideapad-laptop: add a mutex to synchronize VPC commands") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202408290219.BrPO8twi-lkp@intel.com/ Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240829165105.1609180-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 48 +++++++++++++++------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 35c75bcff195..c64dfc56651d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -554,13 +554,14 @@ static ssize_t camera_power_show(struct device *dev, char *buf) { struct ideapad_private *priv = dev_get_drvdata(dev); - unsigned long result; + unsigned long result = 0; int err; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result); - if (err) - return err; + if (err) + return err; + } return sysfs_emit(buf, "%d\n", !!result); } @@ -577,10 +578,11 @@ static ssize_t camera_power_store(struct device *dev, if (err) return err; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state); - if (err) - return err; + if (err) + return err; + } return count; } @@ -628,13 +630,14 @@ static ssize_t fan_mode_show(struct device *dev, char *buf) { struct ideapad_private *priv = dev_get_drvdata(dev); - unsigned long result; + unsigned long result = 0; int err; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result); - if (err) - return err; + if (err) + return err; + } return sysfs_emit(buf, "%lu\n", result); } @@ -654,10 +657,11 @@ static ssize_t fan_mode_store(struct device *dev, if (state > 4 || state == 3) return -EINVAL; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state); - if (err) - return err; + if (err) + return err; + } return count; } @@ -737,13 +741,14 @@ static ssize_t touchpad_show(struct device *dev, char *buf) { struct ideapad_private *priv = dev_get_drvdata(dev); - unsigned long result; + unsigned long result = 0; int err; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result); - if (err) - return err; + if (err) + return err; + } priv->r_touchpad_val = result; @@ -762,10 +767,11 @@ static ssize_t touchpad_store(struct device *dev, if (err) return err; - scoped_guard(mutex, &priv->vpc_mutex) + scoped_guard(mutex, &priv->vpc_mutex) { err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state); - if (err) - return err; + if (err) + return err; + } priv->r_touchpad_val = state; From bb9c2e5492a88e255305d0dfae33e34f0e8bbec6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Sep 2024 18:06:25 +0300 Subject: [PATCH 47/65] platform/x86: x86-android-tablets: Fix spelling in the comments Fix spelling across comments (besides obvious grammar issues): - spell words in full, e.g., 'img' --> 'image' - refer to 'gpio-keys' consistently - refer to acpi_power_off() clearly as to function - make sure that the first line is only for the affected model(s) - miscellaneous improvements Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240902150625.2722187-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../platform/x86/x86-android-tablets/Kconfig | 2 +- .../platform/x86/x86-android-tablets/asus.c | 8 ++--- .../platform/x86/x86-android-tablets/core.c | 20 ++++++------- .../platform/x86/x86-android-tablets/dmi.c | 16 +++++----- .../platform/x86/x86-android-tablets/lenovo.c | 22 +++++++------- .../platform/x86/x86-android-tablets/other.c | 30 +++++++++---------- .../x86/x86-android-tablets/shared-psy-info.c | 4 +-- .../x86-android-tablets/x86-android-tablets.h | 2 +- 8 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig index b591419de80c..88d9e8f2ff24 100644 --- a/drivers/platform/x86/x86-android-tablets/Kconfig +++ b/drivers/platform/x86/x86-android-tablets/Kconfig @@ -20,4 +20,4 @@ config X86_ANDROID_TABLETS are missing from the DSDT. If you have a x86 Android tablet say Y or M here, for a generic x86 - distro config say M here. + distro configuration say M here. diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c index 227afbb51078..07fbeab2319a 100644 --- a/drivers/platform/x86/x86-android-tablets/asus.c +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -37,7 +37,7 @@ static const struct x86_gpio_button asus_me176c_tf103c_lid __initconst = { .pin = 12, }; -/* Asus ME176C tablets have an Android factory img with everything hardcoded */ +/* Asus ME176C tablets have an Android factory image with everything hardcoded */ static const char * const asus_me176c_accel_mount_matrix[] = { "-1", "0", "0", "0", "1", "0", @@ -112,7 +112,7 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = }, .adapter_path = "\\_SB_.I2C5", }, { - /* kxtj21009 accel */ + /* kxtj21009 accelerometer */ .board_info = { .type = "kxtj21009", .addr = 0x0f, @@ -181,7 +181,7 @@ const struct x86_dev_info asus_me176c_info __initconst = { .modules = bq24190_modules, }; -/* Asus TF103C tablets have an Android factory img with everything hardcoded */ +/* Asus TF103C tablets have an Android factory image with everything hardcoded */ static const char * const asus_tf103c_accel_mount_matrix[] = { "0", "-1", "0", "-1", "0", "0", @@ -280,7 +280,7 @@ static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = }, .adapter_path = "\\_SB_.I2C5", }, { - /* kxtj21009 accel */ + /* kxtj21009 accelerometer */ .board_info = { .type = "kxtj21009", .addr = 0x0f, diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 919ef4471229..1427a9a39008 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -26,19 +26,19 @@ static struct platform_device *x86_android_tablet_device; /* - * This helper allows getting a gpio_desc *before* the actual device consuming - * the GPIO has been instantiated. This function _must_ only be used to handle - * this special case such as e.g. : + * This helper allows getting a GPIO descriptor *before* the actual device + * consuming it has been instantiated. This function MUST only be used to + * handle this special case such as, e.g.: * * 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to * i2c_client_new() to instantiate i2c_client-s; or - * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio_keys + * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio-keys * platform_data which still uses old style GPIO numbers. * - * Since the consuming device has not been instatiated yet a dynamic lookup - * is generated using the special x86_android_tablet dev for dev_id. + * Since the consuming device has not been instantiated yet a dynamic lookup + * is generated using the special x86_android_tablet device for dev_id. * - * For normal GPIO lookups a standard static gpiod_lookup_table _must_ be used. + * For normal GPIO lookups a standard static struct gpiod_lookup_table MUST be used. */ int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, bool active_low, enum gpiod_flags dflags, @@ -87,7 +87,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) /* * The DSDT may already reference the GSI in a device skipped by * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI - * to avoid EBUSY errors in this case. + * to avoid -EBUSY errors in this case. */ acpi_unregister_gsi(data->index); irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); @@ -379,7 +379,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) } } - /* + 1 to make space for (optional) gpio_keys_button pdev */ + /* + 1 to make space for the (optional) gpio_keys_button platform device */ pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL); if (!pdevs) { x86_android_tablet_remove(pdev); @@ -432,7 +432,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) buttons[i] = dev_info->gpio_button[i].button; buttons[i].gpio = desc_to_gpio(gpiod); - /* Release gpiod so that gpio-keys can request it */ + /* Release GPIO descriptor so that gpio-keys can request it */ devm_gpiod_put(&x86_android_tablet_device->dev, gpiod); } diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c index 141a2d25e83b..afaca19d4388 100644 --- a/drivers/platform/x86/x86-android-tablets/dmi.c +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -99,17 +99,17 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { { /* Lenovo Yoga Book X91F / X91L */ .matches = { - /* Non exact match to match F + L versions */ + /* Inexact match to match F + L versions */ DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), }, .driver_data = (void *)&lenovo_yogabook_x91_info, }, { /* - * Lenovo Yoga Tablet 2 Pro 1380F/L (13") This has more or less - * the same BIOS as the 830F/L or 1050F/L (8" and 10") below, - * but unlike the 8" / 10" models which share the same mainboard - * this model has a different mainboard. + * Lenovo Yoga Tablet 2 Pro 1380F/L (13") + * This has more or less the same BIOS as the 830F/L or 1050F/L + * (8" and 10") below, but unlike the 8"/10" models which share + * the same mainboard this model has a different mainboard. * This match for the 13" model MUST come before the 8" + 10" * match since that one will also match the 13" model! */ @@ -124,8 +124,8 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { }, { /* - * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" - * Lenovo Yoga Tablet 2 use the same mainboard) + * Lenovo Yoga Tablet 2 830F/L or 1050F/L + * The 8" and 10" Lenovo Yoga Tablet 2 use the same mainboard. */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), @@ -164,7 +164,7 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { .driver_data = (void *)&nextbook_ares8_info, }, { - /* Nextbook Ares 8A (CHT version)*/ + /* Nextbook Ares 8A (CHT version) */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 74f39b658d2c..ae087f1471c1 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -59,7 +59,7 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { .initial_brightness = 128, }; -/* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ +/* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */ static const struct property_entry lenovo_yb1_x90_wacom_props[] = { PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001), @@ -262,7 +262,7 @@ const struct x86_dev_info lenovo_yogabook_x90_info __initconst = { .init = lenovo_yb1_x90_init, }; -/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ +/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fuel-gauge client */ static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { { /* BQ27542 fuel-gauge */ @@ -281,7 +281,7 @@ const struct x86_dev_info lenovo_yogabook_x91_info __initconst = { .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients), }; -/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ +/* Lenovo Yoga Tablet 2 1050F/L's Android factory image has everything hardcoded */ static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), @@ -521,9 +521,9 @@ err_put_device: } /* - * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off + * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off() * gets used as pm_power_off handler. This causes "poweroff" on these tablets - * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* + * to hang hard. Requiring pressing the power button for 30 seconds *twice* * followed by a normal 3 second press to recover. Avoid this by doing an EFI * poweroff instead. */ @@ -546,7 +546,7 @@ static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev) if (ret) return ret; - /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ + /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off() */ lenovo_yoga_tab2_830_1050_sys_off_handler = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, lenovo_yoga_tab2_830_1050_power_off, NULL); @@ -742,7 +742,7 @@ static int __init lenovo_yoga_tab2_1380_init(struct device *dev) if (ret) return ret; - /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ + /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off() */ lenovo_yoga_tab2_830_1050_sys_off_handler = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, lenovo_yoga_tab2_830_1050_power_off, NULL); @@ -799,7 +799,7 @@ static const struct software_node fg_bq25890_1_supply_node = { .properties = fg_bq25890_1_supply_props, }; -/* bq25892 charger settings for the flat lipo battery behind the screen */ +/* bq25892 charger settings for the flat LiPo battery behind the screen */ static const struct property_entry lenovo_yt3_bq25892_0_props[] = { PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), @@ -833,7 +833,7 @@ static const struct software_node lenovo_yt3_hideep_ts_node = { static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { { - /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ + /* bq27500 fuel-gauge for the flat LiPo battery behind the screen */ .board_info = { .type = "bq27500", .addr = 0x55, @@ -842,7 +842,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { }, .adapter_path = "\\_SB_.PCI0.I2C1", }, { - /* bq25892 charger for the flat lipo battery behind the screen */ + /* bq25892 charger for the flat LiPo battery behind the screen */ .board_info = { .type = "bq25892", .addr = 0x6b, @@ -859,7 +859,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { .con_id = "bq25892_0_irq", }, }, { - /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ + /* bq27500 fuel-gauge for the round Li-ion cells in the hinge */ .board_info = { .type = "bq27500", .addr = 0x55, diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index eb0e55c69dfe..aad7114c43c3 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -20,7 +20,7 @@ #include "shared-psy-info.h" #include "x86-android-tablets.h" -/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ +/* Acer Iconia One 7 B1-750 has an Android factory image with everything hardcoded */ static const char * const acer_b1_750_mount_matrix[] = { "-1", "0", "0", "0", "1", "0", @@ -98,7 +98,7 @@ const struct x86_dev_info acer_b1_750_info __initconst = { * Advantech MICA-071 * This is a standard Windows tablet, but it has an extra "quick launch" button * which is not described in the ACPI tables in anyway. - * Use the x86-android-tablets infra to create a gpio-button device for this. + * Use the x86-android-tablets infra to create a gpio-keys device for this. */ static const struct x86_gpio_button advantech_mica_071_button __initconst = { .button = { @@ -209,7 +209,7 @@ const struct x86_dev_info chuwi_hi8_info __initconst = { * This comes in both Windows and Android versions and even on Android * the DSDT is mostly sane. This tablet has 2 extra general purpose buttons * in the button row with the power + volume-buttons labeled P and F. - * Use the x86-android-tablets infra to create a gpio-button device for these. + * Use the x86-android-tablets infra to create a gpio-keys device for these. */ static const struct x86_gpio_button cyberbook_t116_buttons[] __initconst = { { @@ -276,7 +276,7 @@ const struct x86_dev_info czc_p10t __initconst = { .init = czc_p10t_init, }; -/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ +/* Medion Lifetab S10346 tablets have an Android factory image with everything hardcoded */ static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { "0", "1", "0", "1", "0", "0", @@ -305,7 +305,7 @@ static const struct software_node medion_lifetab_s10346_touchscreen_node = { static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { { - /* kxtj21009 accel */ + /* kxtj21009 accelerometer */ .board_info = { .type = "kxtj21009", .addr = 0x0f, @@ -359,7 +359,7 @@ const struct x86_dev_info medion_lifetab_s10346_info __initconst = { .gpiod_lookup_tables = medion_lifetab_s10346_gpios, }; -/* Nextbook Ares 8 (BYT) tablets have an Android factory img with everything hardcoded */ +/* Nextbook Ares 8 (BYT) tablets have an Android factory image with everything hardcoded */ static const char * const nextbook_ares8_accel_mount_matrix[] = { "0", "-1", "0", "-1", "0", "0", @@ -387,7 +387,7 @@ static const struct software_node nextbook_ares8_touchscreen_node = { static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { { - /* Freescale MMA8653FC accel */ + /* Freescale MMA8653FC accelerometer */ .board_info = { .type = "mma8653", .addr = 0x1d, @@ -428,7 +428,7 @@ const struct x86_dev_info nextbook_ares8_info __initconst = { .gpiod_lookup_tables = nextbook_ares8_gpios, }; -/* Nextbook Ares 8A (CHT) tablets have an Android factory img with everything hardcoded */ +/* Nextbook Ares 8A (CHT) tablets have an Android factory image with everything hardcoded */ static const char * const nextbook_ares8a_accel_mount_matrix[] = { "1", "0", "0", "0", "-1", "0", @@ -446,7 +446,7 @@ static const struct software_node nextbook_ares8a_accel_node = { static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = { { - /* Freescale MMA8653FC accel */ + /* Freescale MMA8653FC accelerometer */ .board_info = { .type = "mma8653", .addr = 0x1d, @@ -497,7 +497,7 @@ const struct x86_dev_info nextbook_ares8a_info __initconst = { * Peaq C1010 * This is a standard Windows tablet, but it has a special Dolby button. * This button has a WMI interface, but that is broken. Instead of trying to - * use the broken WMI interface, instantiate a gpio_keys device for this. + * use the broken WMI interface, instantiate a gpio-keys device for this. */ static const struct x86_gpio_button peaq_c1010_button __initconst = { .button = { @@ -521,7 +521,7 @@ const struct x86_dev_info peaq_c1010_info __initconst = { * Whitelabel (sold as various brands) TM800A550L tablets. * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices * (removed through acpi_quirk_skip_i2c_client_enumeration()) and - * the touchscreen fwnode has the wrong GPIOs. + * the touchscreen firmware node has the wrong GPIOs. */ static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { "-1", "0", "0", @@ -566,7 +566,7 @@ static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __in .polarity = ACPI_ACTIVE_HIGH, }, }, { - /* kxcj91008 accel */ + /* kxcj91008 accelerometer */ .board_info = { .type = "kxcj91008", .addr = 0x0f, @@ -598,12 +598,12 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { }; /* - * The fwnode for ktd2026 on Xaomi pad2. It composed of a RGB LED node + * The firmware node for ktd2026 on Xaomi pad2. It composed of a RGB LED node * with three subnodes for each color (B/G/R). The RGB LED node is named * "multi-led" to align with the name in the device tree. */ -/* main fwnode for ktd2026 */ +/* Main firmware node for ktd2026 */ static const struct software_node ktd2026_node = { .name = "ktd2026", }; @@ -665,7 +665,7 @@ static const struct software_node *ktd2026_node_group[] = { }; /* - * For the LEDs which backlight the menu / home / back capacitive buttons on + * For the LEDs which backlight the Menu / Home / Back capacitive buttons on * the bottom bezel. These are attached to a TPS61158 LED controller which * is controlled by the "pwm_soc_lpss_2" PWM output. */ diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c index d2d0aa51bc3f..a46fa15acfb1 100644 --- a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c @@ -39,7 +39,7 @@ const struct software_node fg_bq25890_supply_node = { .properties = fg_bq25890_supply_props, }; -/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ +/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */ static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { PROPERTY_ENTRY_STRING("compatible", "simple-battery"), PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), @@ -80,7 +80,7 @@ const char * const bq24190_modules[] __initconst = { NULL }; -/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ +/* Generic platform device array and GPIO lookup table for micro USB ID pin handling */ const struct platform_device_info int3496_pdevs[] __initconst = { { /* For micro USB ID pin handling */ diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 86402b9b46a3..5517e438c7b6 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -61,7 +61,7 @@ struct x86_serdev_info { const char *ctrl_uid; const char *ctrl_devname; /* - * ATM the serdev core only supports of or ACPI matching; and sofar all + * ATM the serdev core only supports of or ACPI matching; and so far all * Android x86 tablets DSDTs have usable serdev nodes, but sometimes * under the wrong controller. So we just tie the existing serdev ACPI * node to the right controller. From 3a1fb526c2482b2f61c9813e9e14fcdab4ffa390 Mon Sep 17 00:00:00 2001 From: Matthias Fetzer Date: Tue, 3 Sep 2024 19:27:56 +0200 Subject: [PATCH 48/65] platform/x86: thinkpad_acpi: Fix uninitialized symbol 's' warning When the TPACPI_FAN_WR_ACPI_FANW branch is taken s stays uninitialized and would be later used in a debug print. Since the registers are always set to the same two static values inside the branch s is initialized to 0. Fixes: 57d0557dfa49 ("platform/x86: thinkpad_acpi: Add Thinkpad Edge E531 fan support") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/platform-driver-x86/f99e558d-c62a-41eb-93b3-cf00c016d907@stanley.mountain/ Signed-off-by: Matthias Fetzer Link: https://lore.kernel.org/r/20240903172756.19235-1-kontakt@matthias-fetzer.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/thinkpad_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 8f7053920884..4c1b0553f872 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8318,7 +8318,7 @@ static int fan_set_level_safe(int level) static int fan_set_enable(void) { - u8 s; + u8 s = 0; int rc; if (!fan_control_allowed) From e04e2b760ddbe3d7b283a05898c3a029085cd8cd Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 1 Sep 2024 05:10:52 +0200 Subject: [PATCH 49/65] platform/x86: wmi: Pass event data directly to legacy notify handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current legacy WMI handlers are susceptible to picking up wrong WMI event data on systems where different WMI devices share some notification IDs. Prevent this by letting the WMI driver core taking care of retrieving the event data. This also simplifies the legacy WMI handlers and their implementation inside the WMI driver core. Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240901031055.3030-3-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/hwmon/hp-wmi-sensors.c | 22 ++++-------- drivers/platform/x86/acer-wmi.c | 16 +-------- drivers/platform/x86/asus-wmi.c | 19 ++--------- drivers/platform/x86/dell/dell-wmi-aio.c | 13 +------ drivers/platform/x86/hp/hp-wmi.c | 16 +-------- drivers/platform/x86/huawei-wmi.c | 14 +------- drivers/platform/x86/lg-laptop.c | 13 +------ drivers/platform/x86/msi-wmi.c | 20 ++--------- drivers/platform/x86/toshiba-wmi.c | 15 +-------- drivers/platform/x86/wmi.c | 43 ++++++++++-------------- include/linux/acpi.h | 2 +- 11 files changed, 37 insertions(+), 156 deletions(-) diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c index dfa1d6926dea..d6bdad26feb1 100644 --- a/drivers/hwmon/hp-wmi-sensors.c +++ b/drivers/hwmon/hp-wmi-sensors.c @@ -1597,15 +1597,13 @@ static void hp_wmi_devm_notify_remove(void *ignored) } /* hp_wmi_notify - WMI event notification handler */ -static void hp_wmi_notify(u32 value, void *context) +static void hp_wmi_notify(union acpi_object *wobj, void *context) { struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {}; - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; struct hp_wmi_sensors *state = context; struct device *dev = &state->wdev->dev; struct hp_wmi_event event = {}; struct hp_wmi_info *fan_info; - union acpi_object *wobj; acpi_status err; int event_type; u8 count; @@ -1630,20 +1628,15 @@ static void hp_wmi_notify(u32 value, void *context) * HPBIOS_BIOSEvent instance. */ - mutex_lock(&state->lock); - - err = wmi_get_event_data(value, &out); - if (ACPI_FAILURE(err)) - goto out_unlock; - - wobj = out.pointer; if (!wobj) - goto out_unlock; + return; + + mutex_lock(&state->lock); err = populate_event_from_wobj(dev, &event, wobj); if (err) { dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type); - goto out_free_wobj; + goto out_free; } event_type = classify_event(event.name, event.category); @@ -1668,13 +1661,10 @@ static void hp_wmi_notify(u32 value, void *context) break; } -out_free_wobj: - kfree(wobj); - +out_free: devm_kfree(dev, event.name); devm_kfree(dev, event.description); -out_unlock: mutex_unlock(&state->lock); } diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 349169d050c5..7169b84ccdb6 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -2223,39 +2223,25 @@ static void acer_rfkill_exit(void) } } -static void acer_wmi_notify(u32 value, void *context) +static void acer_wmi_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; struct event_return_value return_value; - acpi_status status; u16 device_state; const struct key_entry *key; u32 scancode; - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_warn("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; - if (!obj) return; if (obj->type != ACPI_TYPE_BUFFER) { pr_warn("Unknown response received %d\n", obj->type); - kfree(obj); return; } if (obj->buffer.length != 8) { pr_warn("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); return; } return_value = *((struct event_return_value *)obj->buffer.pointer); - kfree(obj); switch (return_value.function) { case WMID_HOTKEY_EVENT: diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 9979c4c67b47..6f56305f9f83 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -4201,28 +4201,15 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus) /* WMI events *****************************************************************/ -static int asus_wmi_get_event_code(u32 value) +static int asus_wmi_get_event_code(union acpi_object *obj) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; int code; - status = wmi_get_event_data(value, &response); - if (ACPI_FAILURE(status)) { - pr_warn("Failed to get WMI notify code: %s\n", - acpi_format_exception(status)); - return -EIO; - } - - obj = (union acpi_object *)response.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) code = (int)(obj->integer.value & WMI_EVENT_MASK); else code = -EIO; - kfree(obj); return code; } @@ -4288,10 +4275,10 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) pr_info("Unknown key code 0x%x\n", code); } -static void asus_wmi_notify(u32 value, void *context) +static void asus_wmi_notify(union acpi_object *obj, void *context) { struct asus_wmi *asus = context; - int code = asus_wmi_get_event_code(value); + int code = asus_wmi_get_event_code(obj); if (code < 0) { pr_warn("Failed to get notify code: %d\n", code); diff --git a/drivers/platform/x86/dell/dell-wmi-aio.c b/drivers/platform/x86/dell/dell-wmi-aio.c index c7b7f1e403fb..54096495719b 100644 --- a/drivers/platform/x86/dell/dell-wmi-aio.c +++ b/drivers/platform/x86/dell/dell-wmi-aio.c @@ -70,20 +70,10 @@ static bool dell_wmi_aio_event_check(u8 *buffer, int length) return false; } -static void dell_wmi_aio_notify(u32 value, void *context) +static void dell_wmi_aio_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; struct dell_wmi_event *event; - acpi_status status; - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; if (obj) { unsigned int scancode = 0; @@ -114,7 +104,6 @@ static void dell_wmi_aio_notify(u32 value, void *context) break; } } - kfree(obj); } static int __init dell_wmi_aio_input_setup(void) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 876e0a97cee1..8c05e0dd2a21 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -834,28 +834,16 @@ static struct attribute *hp_wmi_attrs[] = { }; ATTRIBUTE_GROUPS(hp_wmi); -static void hp_wmi_notify(u32 value, void *context) +static void hp_wmi_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; u32 event_id, event_data; - union acpi_object *obj; - acpi_status status; u32 *location; int key_code; - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; - if (!obj) return; if (obj->type != ACPI_TYPE_BUFFER) { pr_info("Unknown response received %d\n", obj->type); - kfree(obj); return; } @@ -872,10 +860,8 @@ static void hp_wmi_notify(u32 value, void *context) event_data = *(location + 2); } else { pr_info("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); return; } - kfree(obj); switch (event_id) { case HPWMI_DOCK_EVENT: diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 09d476dd832e..d81fd5df4a00 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -734,26 +734,14 @@ static void huawei_wmi_process_key(struct input_dev *idev, int code) sparse_keymap_report_entry(idev, key, 1, true); } -static void huawei_wmi_input_notify(u32 value, void *context) +static void huawei_wmi_input_notify(union acpi_object *obj, void *context) { struct input_dev *idev = (struct input_dev *)context; - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; - status = wmi_get_event_data(value, &response); - if (ACPI_FAILURE(status)) { - dev_err(&idev->dev, "Unable to get event data\n"); - return; - } - - obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) huawei_wmi_process_key(idev, obj->integer.value); else dev_err(&idev->dev, "Bad response type\n"); - - kfree(response.pointer); } static int huawei_wmi_input_setup(struct device *dev, const char *guid) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 55d31d4fefd6..4b57102c7f62 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -205,21 +205,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u return (union acpi_object *)buffer.pointer; } -static void wmi_notify(u32 value, void *context) +static void wmi_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; long data = (long)context; pr_debug("event guid %li\n", data); - status = wmi_get_event_data(value, &response); - if (ACPI_FAILURE(status)) { - pr_err("Bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; if (!obj) return; @@ -241,7 +231,6 @@ static void wmi_notify(u32 value, void *context) pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, obj->integer.value); - kfree(response.pointer); } static void wmi_input_setup(void) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index fd318cdfe313..4a7ac85c4db4 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -170,20 +170,9 @@ static const struct backlight_ops msi_backlight_ops = { .update_status = bl_set_status, }; -static void msi_wmi_notify(u32 value, void *context) +static void msi_wmi_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; struct key_entry *key; - union acpi_object *obj; - acpi_status status; - - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) { int eventcode = obj->integer.value; @@ -192,7 +181,7 @@ static void msi_wmi_notify(u32 value, void *context) eventcode); if (!key) { pr_info("Unknown key pressed - %x\n", eventcode); - goto msi_wmi_notify_exit; + return; } if (event_wmi->quirk_last_pressed) { @@ -204,7 +193,7 @@ static void msi_wmi_notify(u32 value, void *context) pr_debug("Suppressed key event 0x%X - " "Last press was %lld us ago\n", key->code, ktime_to_us(diff)); - goto msi_wmi_notify_exit; + return; } last_pressed = cur; } @@ -221,9 +210,6 @@ static void msi_wmi_notify(u32 value, void *context) } } else pr_info("Unknown event received\n"); - -msi_wmi_notify_exit: - kfree(response.pointer); } static int __init msi_wmi_backlight_setup(void) diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c index 77c35529ab6f..12c46455e8dc 100644 --- a/drivers/platform/x86/toshiba-wmi.c +++ b/drivers/platform/x86/toshiba-wmi.c @@ -32,26 +32,13 @@ static const struct key_entry toshiba_wmi_keymap[] __initconst = { { KE_END, 0 } }; -static void toshiba_wmi_notify(u32 value, void *context) +static void toshiba_wmi_notify(union acpi_object *obj, void *context) { - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; - - status = wmi_get_event_data(value, &response); - if (ACPI_FAILURE(status)) { - pr_err("Bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; if (!obj) return; /* TODO: Add proper checks once we have data */ pr_debug("Unknown event received, obj type %x\n", obj->type); - - kfree(response.pointer); } static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 1d0b2d6040d1..6ab181dd94ab 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1227,40 +1227,33 @@ static int wmi_notify_device(struct device *dev, void *data) if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) return 0; + /* 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. + */ + ret = wmi_get_notify_data(wblock, &obj); + if (ret < 0) + return -EIO; + down_read(&wblock->notify_lock); /* The WMI driver notify handler conflicts with the legacy WMI handler. * Because of this the WMI driver notify handler takes precedence. */ if (wblock->dev.dev.driver && wblock->driver_ready) { - ret = wmi_get_notify_data(wblock, &obj); - if (ret >= 0) { - wmi_notify_driver(wblock, obj); - kfree(obj); - } + wmi_notify_driver(wblock, obj); } else { - 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); - } - + if (wblock->handler) + wblock->handler(obj, wblock->handler_data); } up_read(&wblock->notify_lock); + kfree(obj); + acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0); return -EBUSY; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0687a442fec7..eed105b1fbfb 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -386,7 +386,7 @@ extern bool acpi_is_pnp_device(struct acpi_device *); #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) -typedef void (*wmi_notify_handler) (u32 value, void *context); +typedef void (*wmi_notify_handler) (union acpi_object *data, void *context); int wmi_instance_count(const char *guid); From 79a56f4c8fa629ac79ada8f4b746195bd91f09c7 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 1 Sep 2024 05:10:53 +0200 Subject: [PATCH 50/65] platform/x86: wmi: Remove wmi_get_event_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the WMI driver core now takes care of retrieving the WMI event data even for legacy WMI notify handlers, this function is no longer used. Remove it to prevent WMI drivers from messing up the ACPI firmware on some machines. Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240901031055.3030-4-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 57 -------------------------------------- include/linux/acpi.h | 1 - 2 files changed, 58 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 6ab181dd94ab..c7f0754f74b4 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -199,23 +199,6 @@ static int wmidev_match_guid(struct device *dev, const void *data) return 0; } -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; - - return 0; -} - static const struct bus_type wmi_bus_type; static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) @@ -235,17 +218,6 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) return dev_to_wdev(dev); } -static struct wmi_device *wmi_find_event_by_notify_id(const u32 notify_id) -{ - struct device *dev; - - dev = bus_find_device(&wmi_bus_type, NULL, ¬ify_id, wmidev_match_notify_id); - if (!dev) - return ERR_PTR(-ENODEV); - - return to_wmi_device(dev); -} - static void wmi_device_put(struct wmi_device *wdev) { put_device(&wdev->dev); @@ -649,35 +621,6 @@ acpi_status wmi_remove_notify_handler(const char *guid) } EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); -/** - * wmi_get_event_data - Get WMI data associated with an event (deprecated) - * - * @event: Event to find - * @out: Buffer to hold event data - * - * Get extra data associated with an WMI event, the caller needs to free @out. - * - * Return: acpi_status signaling success or error. - */ -acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) -{ - struct wmi_block *wblock; - struct wmi_device *wdev; - acpi_status status; - - wdev = wmi_find_event_by_notify_id(event); - if (IS_ERR(wdev)) - return AE_NOT_FOUND; - - wblock = container_of(wdev, struct wmi_block, dev); - status = get_event_data(wblock, out); - - wmi_device_put(wdev); - - return status; -} -EXPORT_SYMBOL_GPL(wmi_get_event_data); - /** * wmi_has_guid - Check if a GUID is available * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba diff --git a/include/linux/acpi.h b/include/linux/acpi.h index eed105b1fbfb..3cbe4b57bc73 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -401,7 +401,6 @@ extern acpi_status wmi_set_block(const char *guid, u8 instance, extern acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data); extern acpi_status wmi_remove_notify_handler(const char *guid); -extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out); extern bool wmi_has_guid(const char *guid); extern char *wmi_get_acpi_device_uid(const char *guid); From 6ed2d7e8e74de49f11f9517f32acada5369fd30b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 1 Sep 2024 05:10:54 +0200 Subject: [PATCH 51/65] platform/x86: wmi: Merge get_event_data() with wmi_get_notify_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since get_event_data() is only called by wmi_get_notify_data(), it makes sense to merge both functions. Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240901031055.3030-5-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 43 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index c7f0754f74b4..6b27833ba5d9 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -166,22 +166,6 @@ static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wbloc return ACPI_TYPE_BUFFER; } -static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out) -{ - union acpi_object param = { - .integer = { - .type = ACPI_TYPE_INTEGER, - .value = wblock->gblock.notify_id, - } - }; - struct acpi_object_list input = { - .count = 1, - .pointer = ¶m, - }; - - return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); -} - static int wmidev_match_guid(struct device *dev, const void *data) { struct wmi_block *wblock = dev_to_wblock(dev); @@ -1129,14 +1113,19 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj) { struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object param = { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = wblock->gblock.notify_id, + } + }; + struct acpi_object_list input = { + .count = 1, + .pointer = ¶m, + }; acpi_status status; - if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { - *obj = NULL; - return 0; - } - - status = get_event_data(wblock, &data); + status = acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, &data); if (ACPI_FAILURE(status)) { dev_warn(&wblock->dev.dev, "Failed to get event data\n"); return -EIO; @@ -1163,7 +1152,7 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) static int wmi_notify_device(struct device *dev, void *data) { struct wmi_block *wblock = dev_to_wblock(dev); - union acpi_object *obj; + union acpi_object *obj = NULL; u32 *event = data; int ret; @@ -1179,9 +1168,11 @@ static int wmi_notify_device(struct device *dev, void *data) * WMI driver core stops evaluating _WED due to missing * WMI event consumers. */ - ret = wmi_get_notify_data(wblock, &obj); - if (ret < 0) - return -EIO; + if (!test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { + ret = wmi_get_notify_data(wblock, &obj); + if (ret < 0) + return -EIO; + } down_read(&wblock->notify_lock); /* The WMI driver notify handler conflicts with the legacy WMI handler. From f5dd17e30a59791ba3523aeaab04bafe001b7f67 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 1 Sep 2024 05:10:55 +0200 Subject: [PATCH 52/65] platform/x86: wmi: Call both legacy and WMI driver notify handlers Since the legacy WMI notify handlers are now using the WMI event data provided by the WMI driver core, they can coexist with modern WMI driver notify handlers. Remove the precedence of WMI driver notify handlers and call both when receiving an event. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240901031055.3030-6-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 6b27833ba5d9..3cbe180c3fc0 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1175,15 +1175,13 @@ static int wmi_notify_device(struct device *dev, void *data) } down_read(&wblock->notify_lock); - /* The WMI driver notify handler conflicts with the legacy WMI handler. - * Because of this the WMI driver notify handler takes precedence. - */ - if (wblock->dev.dev.driver && wblock->driver_ready) { + + if (wblock->dev.dev.driver && wblock->driver_ready) wmi_notify_driver(wblock, obj); - } else { - if (wblock->handler) - wblock->handler(obj, wblock->handler_data); - } + + if (wblock->handler) + wblock->handler(obj, wblock->handler_data); + up_read(&wblock->notify_lock); kfree(obj); From cedf233530cc375343c5a0b612fe94392f246c99 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Fri, 6 Sep 2024 11:40:03 -0700 Subject: [PATCH 53/65] platform/x86: intel/pmc: Ignore all LTRs during suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to ignore all LTRs before suspend and restore the previous LTR values after suspend. This feature could be turned off with module parameter ltr_ignore_all_suspend. LTR value is a mechanism for a device to indicate tolerance to access the corresponding resource. When system suspends, the resource is not available and therefore the LTR value could be ignored. Ignoring all LTR values prevents problematic device from blocking the system to get to the deepest package state during suspend. Suggested-by: Rafael J. Wysocki Signed-off-by: Xi Pardee Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240906184016.268153-1-xi.pardee@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 53 +++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 2 + 2 files changed, 55 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 692049ba3925..d6c0f88f8c7b 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -714,6 +714,49 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker); +static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { + struct pmc *pmc; + u32 ltr_ign; + + pmc = pmcdev->pmcs[i]; + if (!pmc) + continue; + + guard(mutex)(&pmcdev->lock); + pmc->ltr_ign = pmc_core_reg_read(pmc, pmc->map->ltr_ignore_offset); + + /* ltr_ignore_max is the max index value for LTR ignore register */ + ltr_ign = pmc->ltr_ign | GENMASK(pmc->map->ltr_ignore_max, 0); + pmc_core_reg_write(pmc, pmc->map->ltr_ignore_offset, ltr_ign); + } + + /* + * Ignoring ME during suspend is blocking platforms with ADL PCH to get to + * deeper S0ix substate. + */ + pmc_core_send_ltr_ignore(pmcdev, 6, 0); +} + +static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { + struct pmc *pmc; + + pmc = pmcdev->pmcs[i]; + if (!pmc) + continue; + + guard(mutex)(&pmcdev->lock); + pmc_core_reg_write(pmc, pmc->map->ltr_ignore_offset, pmc->ltr_ign); + } +} + static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset, const int lpm_adj_x2) { @@ -1485,6 +1528,10 @@ static bool warn_on_s0ix_failures; module_param(warn_on_s0ix_failures, bool, 0644); MODULE_PARM_DESC(warn_on_s0ix_failures, "Check and warn for S0ix failures"); +static bool ltr_ignore_all_suspend = true; +module_param(ltr_ignore_all_suspend, bool, 0644); +MODULE_PARM_DESC(ltr_ignore_all_suspend, "Ignore all LTRs during suspend"); + static __maybe_unused int pmc_core_suspend(struct device *dev) { struct pmc_dev *pmcdev = dev_get_drvdata(dev); @@ -1494,6 +1541,9 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) if (pmcdev->suspend) pmcdev->suspend(pmcdev); + if (ltr_ignore_all_suspend) + pmc_core_ltr_ignore_all(pmcdev); + /* Check if the syspend will actually use S0ix */ if (pm_suspend_via_firmware()) return 0; @@ -1600,6 +1650,9 @@ static __maybe_unused int pmc_core_resume(struct device *dev) { struct pmc_dev *pmcdev = dev_get_drvdata(dev); + if (ltr_ignore_all_suspend) + pmc_core_ltr_restore_all(pmcdev); + if (pmcdev->resume) return pmcdev->resume(pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index c8851f128adc..b9d3291d0bf2 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -372,6 +372,7 @@ struct pmc_info { * @map: pointer to pmc_reg_map struct that contains platform * specific attributes * @lpm_req_regs: List of substate requirements + * @ltr_ign: Holds LTR ignore data while suspended * * pmc contains info about one power management controller device. */ @@ -380,6 +381,7 @@ struct pmc { void __iomem *regbase; const struct pmc_reg_map *map; u32 *lpm_req_regs; + u32 ltr_ign; }; /** From f80d7100f091e96b54beb32dc6d136520453582f Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Sat, 7 Sep 2024 11:09:59 +0800 Subject: [PATCH 54/65] platform/olpc: Remove redundant null pointer checks in olpc_ec_setup_debugfs() Since the debugfs_create_dir() never returns a null pointer, checking the return value for a null pointer is redundant. Since debugfs_create_file() can deal with a ERR_PTR() style pointer, drop the check. Signed-off-by: Li Zetao Link: https://lore.kernel.org/r/20240907031009.3591057-2-lizetao1@huawei.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/olpc/olpc-ec.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 921520475ff6..48e9861bb571 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -332,9 +332,6 @@ static struct dentry *olpc_ec_setup_debugfs(void) struct dentry *dbgfs_dir; dbgfs_dir = debugfs_create_dir("olpc-ec", NULL); - if (IS_ERR_OR_NULL(dbgfs_dir)) - return NULL; - debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops); return dbgfs_dir; From b522dd730b9680fdbd525ba2a1e781ccba5b69cb Mon Sep 17 00:00:00 2001 From: James Harmison Date: Mon, 9 Sep 2024 13:32:27 +0200 Subject: [PATCH 55/65] platform/x86: panasonic-laptop: Add support for programmable buttons The value returned by "HINF" contains press/release information in bit 7 and a keycode in bits 0-6. Change the code to retrieve the keycode to use all 7 keycode bits instead of only using bits 0-3 and add mappings for the higher keycodes used by the programmable buttons found on newer panasonic toughbook models. Tested-by: James Harmison Signed-off-by: James Harmison Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240909113227.254470-3-hdegoede@redhat.com --- drivers/platform/x86/panasonic-laptop.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index cf845ee1c7b1..1c88636649a1 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -121,6 +121,7 @@ #include #include +#include #include #include #include @@ -224,6 +225,17 @@ static const struct key_entry panasonic_keymap[] = { { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ { KE_KEY, 9, { KEY_BATTERY } }, { KE_KEY, 10, { KEY_SUSPEND } }, + { KE_KEY, 21, { KEY_MACRO1 } }, + { KE_KEY, 22, { KEY_MACRO2 } }, + { KE_KEY, 24, { KEY_MACRO3 } }, + { KE_KEY, 25, { KEY_MACRO4 } }, + { KE_KEY, 34, { KEY_MACRO5 } }, + { KE_KEY, 35, { KEY_MACRO6 } }, + { KE_KEY, 36, { KEY_MACRO7 } }, + { KE_KEY, 37, { KEY_MACRO8 } }, + { KE_KEY, 41, { KEY_MACRO9 } }, + { KE_KEY, 42, { KEY_MACRO10 } }, + { KE_KEY, 43, { KEY_MACRO11 } }, { KE_END, 0 } }; @@ -810,8 +822,8 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) return; } - key = result & 0xf; - updown = result & 0x80; /* 0x80 == key down; 0x00 = key up */ + key = result & GENMASK(6, 0); + updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */ /* hack: some firmware sends no key down for sleep / hibernate */ if (key == 7 || key == 10) { From d35b0b0e1445492e746a6999dde9bfa82b7949e5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Sep 2024 15:41:04 +0300 Subject: [PATCH 56/65] MAINTAINERS: Add Intel MID section The different drivers are spread over the kernel. I would like to be informed about the changes in them, which are done not by me. Also, most of them I indeed support. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240909124952.1152017-2-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fac6f8ea9ad6..2ba2877c095d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11466,6 +11466,24 @@ S: Maintained F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update F: drivers/fpga/intel-m10-bmc-sec-update.c +INTEL MID (Mobile Internet Device) PLATFORM +M: Andy Shevchenko +L: linux-kernel@vger.kernel.org +S: Supported +F: arch/x86/include/asm/intel-mid.h +F: arch/x86/include/asm/intel_scu_ipc.h +F: arch/x86/pci/intel_mid_pci.c +F: arch/x86/platform/intel-mid/ +F: drivers/extcon/extcon-intel-mrfld.c +F: drivers/iio/adc/intel_mrfld_adc.c +F: drivers/mfd/intel_soc_pmic_mrfld.c +F: drivers/platform/x86/intel/mrfld_pwrbtn.c +F: drivers/platform/x86/intel_scu_* +F: drivers/staging/media/atomisp/ +F: drivers/watchdog/intel-mid_wdt.c +F: include/linux/mfd/intel_soc_pmic_mrfld.h +F: include/linux/platform_data/intel-mid_wdt.h + INTEL P-Unit IPC DRIVER M: Zha Qipeng L: platform-driver-x86@vger.kernel.org From c912ac66b3fc92acb0079fff7d030e9be0756c36 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 9 Sep 2024 15:41:05 +0300 Subject: [PATCH 57/65] platform/x86: intel_scu_ipc: Move intel_scu_ipc.h out of arch/x86/include/asm This is a platform/x86 library that is mostly being used by other drivers not directly under arch/x86 anyway (with the exception of the Intel MID setup code) so it makes sense that it lives under the platform_data/x86/ directory instead. No functional changes intended. Suggested-by: Andy Shevchenko Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240909124952.1152017-3-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 4 ++-- arch/x86/include/asm/intel_telemetry.h | 2 +- arch/x86/platform/intel-mid/intel-mid.c | 3 ++- drivers/mfd/intel_pmc_bxt.c | 3 +-- drivers/mfd/intel_soc_pmic_bxtwc.c | 3 +-- drivers/mfd/intel_soc_pmic_mrfld.c | 3 +-- drivers/platform/x86/intel_scu_ipc.c | 2 +- drivers/platform/x86/intel_scu_ipcutil.c | 2 +- drivers/platform/x86/intel_scu_pcidrv.c | 2 +- drivers/platform/x86/intel_scu_pltdrv.c | 2 +- drivers/usb/typec/mux/intel_pmc_mux.c | 3 +-- drivers/watchdog/intel-mid_wdt.c | 3 +-- .../asm => include/linux/platform_data/x86}/intel_scu_ipc.h | 4 ++-- 13 files changed, 16 insertions(+), 20 deletions(-) rename {arch/x86/include/asm => include/linux/platform_data/x86}/intel_scu_ipc.h (96%) diff --git a/MAINTAINERS b/MAINTAINERS index 2ba2877c095d..3f3a8d8abf87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11471,7 +11471,6 @@ M: Andy Shevchenko L: linux-kernel@vger.kernel.org S: Supported F: arch/x86/include/asm/intel-mid.h -F: arch/x86/include/asm/intel_scu_ipc.h F: arch/x86/pci/intel_mid_pci.c F: arch/x86/platform/intel-mid/ F: drivers/extcon/extcon-intel-mrfld.c @@ -11483,6 +11482,7 @@ F: drivers/staging/media/atomisp/ F: drivers/watchdog/intel-mid_wdt.c F: include/linux/mfd/intel_soc_pmic_mrfld.h F: include/linux/platform_data/intel-mid_wdt.h +F: include/linux/platform_data/x86/intel_scu_ipc.h INTEL P-Unit IPC DRIVER M: Zha Qipeng @@ -11546,8 +11546,8 @@ F: drivers/counter/intel-qep.c INTEL SCU DRIVERS M: Mika Westerberg S: Maintained -F: arch/x86/include/asm/intel_scu_ipc.h F: drivers/platform/x86/intel_scu_* +F: include/linux/platform_data/x86/intel_scu_ipc.h INTEL SDSI DRIVER M: David E. Box diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h index 8046e70dfd7c..43b7657febca 100644 --- a/arch/x86/include/asm/intel_telemetry.h +++ b/arch/x86/include/asm/intel_telemetry.h @@ -10,7 +10,7 @@ #define TELEM_MAX_EVENTS_SRAM 28 #define TELEM_MAX_OS_ALLOCATED_EVENTS 20 -#include +#include enum telemetry_unit { TELEM_PSS = 0, diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index f83bbe0acd4a..a8e75f8c14fd 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -27,9 +27,10 @@ #include #include #include -#include #include +#include + #define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */ #define IPCMSG_COLD_RESET 0xF1 diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c index 9f01d38acc7f..e405d7513ca1 100644 --- a/drivers/mfd/intel_pmc_bxt.c +++ b/drivers/mfd/intel_pmc_bxt.c @@ -23,8 +23,7 @@ #include #include #include - -#include +#include /* Residency with clock rate at 19.2MHz to usecs */ #define S0IX_RESIDENCY_IN_USECS(d, s) \ diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index ba32cacfc499..ab3c94224dd1 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -15,8 +15,7 @@ #include #include #include - -#include +#include /* PMIC device registers */ #define REG_ADDR_MASK GENMASK(15, 8) diff --git a/drivers/mfd/intel_soc_pmic_mrfld.c b/drivers/mfd/intel_soc_pmic_mrfld.c index 71da861e8c27..77121775c1a3 100644 --- a/drivers/mfd/intel_soc_pmic_mrfld.c +++ b/drivers/mfd/intel_soc_pmic_mrfld.c @@ -12,11 +12,10 @@ #include #include #include +#include #include #include -#include - /* * Level 2 IRQs * diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index a68df4133403..5b16d29c93d7 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -23,7 +23,7 @@ #include #include -#include +#include /* IPC defines the following message types */ #define IPCMSG_PCNTRL 0xff /* Power controller unit read/write */ diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 7d87cbd4b9c6..69b36ce41fa2 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -18,7 +18,7 @@ #include #include -#include +#include static int major; diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c index dbf0310448da..d7f72d6deb44 100644 --- a/drivers/platform/x86/intel_scu_pcidrv.c +++ b/drivers/platform/x86/intel_scu_pcidrv.c @@ -11,7 +11,7 @@ #include #include -#include +#include static int intel_scu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/platform/x86/intel_scu_pltdrv.c b/drivers/platform/x86/intel_scu_pltdrv.c index 56ec6ae4c824..0892362acd7b 100644 --- a/drivers/platform/x86/intel_scu_pltdrv.c +++ b/drivers/platform/x86/intel_scu_pltdrv.c @@ -15,7 +15,7 @@ #include #include -#include +#include static int intel_scu_platform_probe(struct platform_device *pdev) { diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 56989a0d0f43..46b4c5c3a6be 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -18,8 +19,6 @@ #include #include -#include - #define PMC_USBC_CMD 0xa7 /* Response status bits */ diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 8d71f6a2236b..91c9c3950e44 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -21,8 +21,7 @@ #include #include - -#include +#include #define IPC_WATCHDOG 0xf8 diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/include/linux/platform_data/x86/intel_scu_ipc.h similarity index 96% rename from arch/x86/include/asm/intel_scu_ipc.h rename to include/linux/platform_data/x86/intel_scu_ipc.h index 8537f597d20a..0ca9962e97f2 100644 --- a/arch/x86/include/asm/intel_scu_ipc.h +++ b/include/linux/platform_data/x86/intel_scu_ipc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_X86_INTEL_SCU_IPC_H_ -#define _ASM_X86_INTEL_SCU_IPC_H_ +#ifndef __PLATFORM_X86_INTEL_SCU_IPC_H_ +#define __PLATFORM_X86_INTEL_SCU_IPC_H_ #include From 5f1cda51107fd5e53cd012bd1f6f39aede96bdbe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Sep 2024 15:41:06 +0300 Subject: [PATCH 58/65] platform/x86: intel_scu_wdt: Move intel_scu_wdt.h to x86 subfolder This is a platform/x86 library that can only be used on x86 devices. so it makes sense that it lives under the platform_data/x86/ directory instead. No functional changes intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240909124952.1152017-4-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel_scu_wdt.c | 3 ++- drivers/watchdog/intel-mid_wdt.c | 2 +- include/linux/platform_data/{ => x86}/intel-mid_wdt.h | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) rename include/linux/platform_data/{ => x86}/intel-mid_wdt.h (74%) diff --git a/drivers/platform/x86/intel_scu_wdt.c b/drivers/platform/x86/intel_scu_wdt.c index d0b6637861d3..746d47d33406 100644 --- a/drivers/platform/x86/intel_scu_wdt.c +++ b/drivers/platform/x86/intel_scu_wdt.c @@ -9,13 +9,14 @@ #include #include #include -#include #include #include #include #include +#include + #define TANGIER_EXT_TIMER0_MSI 12 static struct platform_device wdt_dev = { diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 91c9c3950e44..756d262dc580 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include #define IPC_WATCHDOG 0xf8 diff --git a/include/linux/platform_data/intel-mid_wdt.h b/include/linux/platform_data/x86/intel-mid_wdt.h similarity index 74% rename from include/linux/platform_data/intel-mid_wdt.h rename to include/linux/platform_data/x86/intel-mid_wdt.h index 8dba70b4b020..e5c0210d0fec 100644 --- a/include/linux/platform_data/intel-mid_wdt.h +++ b/include/linux/platform_data/x86/intel-mid_wdt.h @@ -6,8 +6,8 @@ * Contact: David Cohen */ -#ifndef __INTEL_MID_WDT_H__ -#define __INTEL_MID_WDT_H__ +#ifndef __PLATFORM_X86_INTEL_MID_WDT_H_ +#define __PLATFORM_X86_INTEL_MID_WDT_H_ #include @@ -16,4 +16,4 @@ struct intel_mid_wdt_pdata { int (*probe)(struct platform_device *pdev); }; -#endif /*__INTEL_MID_WDT_H__*/ +#endif /* __PLATFORM_X86_INTEL_MID_WDT_H_ */ From f965e5bf656984c594e9010f1bb54a6de4c48e7b Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Tue, 10 Sep 2024 17:05:07 +1200 Subject: [PATCH 59/65] platform/x86: asus-wmi: add debug print in more key places Add more verbose debug print in the WMI method calls. This helps a lot with debugging various issues working with regular users as the WMI methods can be traced now. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240910050507.685069-1-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 58 +++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6f56305f9f83..ed9d5d7fd144 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -354,20 +354,29 @@ static int asus_wmi_evaluate_method3(u32 method_id, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, &input, &output); - if (ACPI_FAILURE(status)) + pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x\n", + __func__, method_id, arg0, arg1, arg2); + if (ACPI_FAILURE(status)) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, -EIO); return -EIO; + } obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) tmp = (u32) obj->integer.value; + pr_debug("Result: 0x%08x\n", tmp); if (retval) *retval = tmp; kfree(obj); - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) + if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, -ENODEV); return -ENODEV; + } return 0; } @@ -397,20 +406,29 @@ static int asus_wmi_evaluate_method5(u32 method_id, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, &input, &output); - if (ACPI_FAILURE(status)) + pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + __func__, method_id, arg0, arg1, arg2, arg3, arg4); + if (ACPI_FAILURE(status)) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, -EIO); return -EIO; + } obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) tmp = (u32) obj->integer.value; + pr_debug("Result: %x\n", tmp); if (retval) *retval = tmp; kfree(obj); - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) + if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, -ENODEV); return -ENODEV; + } return 0; } @@ -436,8 +454,13 @@ static int asus_wmi_evaluate_method_buf(u32 method_id, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, &input, &output); - if (ACPI_FAILURE(status)) + pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x\n", + __func__, method_id, arg0, arg1); + if (ACPI_FAILURE(status)) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, -EIO); return -EIO; + } obj = (union acpi_object *)output.pointer; @@ -473,8 +496,11 @@ static int asus_wmi_evaluate_method_buf(u32 method_id, kfree(obj); - if (err) + if (err) { + pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n", + __func__, method_id, arg0, err); return err; + } return 0; } @@ -562,6 +588,7 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) { u32 retval; int status = asus_wmi_get_devstate(asus, dev_id, &retval); + pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval); return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); } @@ -3617,18 +3644,27 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available, ASUS_WMI_DEVID_CPU_FAN_CURVE); - if (err) + if (err) { + pr_debug("%s, checked 0x%08x, failed: %d\n", + __func__, ASUS_WMI_DEVID_CPU_FAN_CURVE, err); return err; + } err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available, ASUS_WMI_DEVID_GPU_FAN_CURVE); - if (err) + if (err) { + pr_debug("%s, checked 0x%08x, failed: %d\n", + __func__, ASUS_WMI_DEVID_GPU_FAN_CURVE, err); return err; + } err = fan_curve_check_present(asus, &asus->mid_fan_curve_available, ASUS_WMI_DEVID_MID_FAN_CURVE); - if (err) + if (err) { + pr_debug("%s, checked 0x%08x, failed: %d\n", + __func__, ASUS_WMI_DEVID_MID_FAN_CURVE, err); return err; + } if (!asus->cpu_fan_curve_available && !asus->gpu_fan_curve_available @@ -4459,8 +4495,10 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, else if (attr == &dev_attr_available_mini_led_mode.attr) ok = asus->mini_led_dev_id != 0; - if (devid != -1) + if (devid != -1) { ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); + pr_debug("%s called 0x%08x, ok: %x\n", __func__, devid, ok); + } return ok ? attr->mode : 0; } From 8e8895c9dc81a7fdbeb15adb394139e79f5281b6 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Tue, 10 Sep 2024 16:54:43 +1200 Subject: [PATCH 60/65] platform/x86: asus-wmi: don't fail if platform_profile already registered On some newer laptops ASUS laptops SPS support is advertised but not actually used, causing the AMD driver to register as a platform_profile handler. If this happens then the asus_wmi driver would error with -EEXIST when trying to register its own handler leaving the user with a possibly unusable system. This is especially true for laptops with an MCU that emit a stream of HID packets, some of which can be misinterpreted as shutdown signals. We can safely continue loading the driver instead of bombing out. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240910045443.678145-1-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ed9d5d7fd144..6ae282088408 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -3910,8 +3910,13 @@ static int platform_profile_setup(struct asus_wmi *asus) asus->platform_profile_handler.choices); err = platform_profile_register(&asus->platform_profile_handler); - if (err) + if (err == -EEXIST) { + pr_warn("%s, a platform_profile handler is already registered\n", __func__); + return 0; + } else if (err) { + pr_err("%s, failed at platform_profile_register: %d\n", __func__, err); return err; + } asus->platform_profile_support = true; return 0; From c11619af35bae5884029bd14170c3e4b55ddf6f3 Mon Sep 17 00:00:00 2001 From: Ckath Date: Wed, 11 Sep 2024 21:12:40 +0200 Subject: [PATCH 61/65] platform/x86: touchscreen_dmi: add nanote-next quirk Add touschscreen info for the nanote next (UMPC-03-SR). After checking with multiple owners the DMI info really is this generic. Signed-off-by: Ckath Link: https://lore.kernel.org/r/e8dda83a-10ae-42cf-a061-5d29be0d193a@yandex.ru Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/touchscreen_dmi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index f74af0a689f2..0a39f68c641d 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -840,6 +840,21 @@ static const struct ts_dmi_data rwc_nanote_p8_data = { .properties = rwc_nanote_p8_props, }; +static const struct property_entry rwc_nanote_next_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 5), + PROPERTY_ENTRY_U32("touchscreen-min-y", 5), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1785), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1145), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-next.fw"), + { } +}; + +static const struct ts_dmi_data rwc_nanote_next_data = { + .acpi_name = "MSSL1680:00", + .properties = rwc_nanote_next_props, +}; + static const struct property_entry schneider_sct101ctm_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1715), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), @@ -1589,6 +1604,17 @@ const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_SKU, "0001") }, }, + { + /* RWC NANOTE NEXT */ + .driver_data = (void *)&rwc_nanote_next_data, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."), + DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."), + DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), + /* Above matches are too generic, add bios-version match */ + DMI_MATCH(DMI_BIOS_VERSION, "S8A70R100-V005"), + }, + }, { /* Schneider SCT101CTM */ .driver_data = (void *)&schneider_sct101ctm_data, From 06369503d644068abd9e90918c6611274d94c126 Mon Sep 17 00:00:00 2001 From: aln8 Date: Thu, 12 Sep 2024 15:36:01 +0800 Subject: [PATCH 62/65] platform/x86/amd: pmf: Add quirk for TUF Gaming A14 The ASUS TUF Gaming A14 has the same issue as the ROG Zephyrus G14 where it advertises SPS support but doesn't use it. Signed-off-by: aln8 Acked-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20240912073601.65656-1-aln8un@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf-quirks.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c index 460444cda1b2..6ee219a81537 100644 --- a/drivers/platform/x86/amd/pmf/pmf-quirks.c +++ b/drivers/platform/x86/amd/pmf/pmf-quirks.c @@ -37,6 +37,14 @@ static const struct dmi_system_id fwbug_list[] = { }, .driver_data = &quirk_no_sps_bug, }, + { + .ident = "ASUS TUF Gaming A14", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"), + }, + .driver_data = &quirk_no_sps_bug, + }, {} }; From 305790dd91057a3f7497c9d128614a4f8486b62b Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 12 Sep 2024 15:05:32 -0400 Subject: [PATCH 63/65] platform/mellanox: mlxbf-pmc: fix lockdep warning It seems the mlxbf-pmc driver is missing initializing sysfs attributes which causes the warning below when CONFIG_LOCKDEP and CONFIG_DEBUG_LOCK_ALLOC are enabled. This commit fixes it. [ 155.380843] BUG: key ffff470f45dfa6d8 has not been registered! [ 155.386749] ------------[ cut here ]------------ [ 155.391361] DEBUG_LOCKS_WARN_ON(1) [ 155.391381] WARNING: CPU: 4 PID: 1828 at kernel/locking/lockdep.c:4894 lockdep_init_map_type+0x1d0/0x288 [ 155.404254] Modules linked in: mlxbf_pmc(+) xfs libcrc32c mmc_block mlx5_core crct10dif_ce mlxfw ghash_ce virtio_net tls net_failover sha2 _ce failover psample sha256_arm64 dw_mmc_bluefield pci_hyperv_intf sha1_ce dw_mmc_pltfm sbsa_gwdt dw_mmc micrel mmc_core nfit i2c_mlxbf pwr_m lxbf gpio_generic libnvdimm mlxbf_tmfifo mlxbf_gige dm_mirror dm_region_hash dm_log dm_mod [ 155.436786] CPU: 4 UID: 0 PID: 1828 Comm: modprobe Kdump: loaded Not tainted 6.11.0-rc7-rep1+ #1 [ 155.445562] Hardware name: https://www.mellanox.com BlueField SoC/BlueField SoC, BIOS 4.8.0.13249 Aug 7 2024 [ 155.455463] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 155.462413] pc : lockdep_init_map_type+0x1d0/0x288 [ 155.467196] lr : lockdep_init_map_type+0x1d0/0x288 [ 155.471976] sp : ffff80008a1734e0 [ 155.475279] x29: ffff80008a1734e0 x28: ffff470f45df0240 x27: 00000000ffffee4b [ 155.482406] x26: 00000000000011b4 x25: 0000000000000000 x24: 0000000000000000 [ 155.489532] x23: ffff470f45dfa6d8 x22: 0000000000000000 x21: ffffd54ef6bea000 [ 155.496659] x20: ffff470f45dfa6d8 x19: ffff470f49cdc638 x18: ffffffffffffffff [ 155.503784] x17: 2f30303a31444642 x16: ffffd54ef48a65e8 x15: ffff80010a172fe7 [ 155.510911] x14: 0000000000000000 x13: 284e4f5f4e524157 x12: 5f534b434f4c5f47 [ 155.518037] x11: 0000000000000001 x10: 0000000000000001 x9 : ffffd54ef3f48a14 [ 155.525163] x8 : 00000000000bffe8 x7 : c0000000ffff7fff x6 : 00000000002bffa8 [ 155.532289] x5 : ffff4712bdcb6088 x4 : 0000000000000000 x3 : 0000000000000027 [ 155.539416] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff470f43e5be00 [ 155.546542] Call trace: [ 155.548976] lockdep_init_map_type+0x1d0/0x288 [ 155.553410] __kernfs_create_file+0x80/0x138 [ 155.557673] sysfs_add_file_mode_ns+0x94/0x150 [ 155.562106] create_files+0xb0/0x248 [ 155.565672] internal_create_group+0x10c/0x328 [ 155.570105] internal_create_groups.part.0+0x50/0xc8 [ 155.575060] sysfs_create_groups+0x20/0x38 [ 155.579146] device_add_attrs+0x1b8/0x228 [ 155.583146] device_add+0x2a4/0x690 [ 155.586625] device_register+0x24/0x38 [ 155.590362] __hwmon_device_register+0x1e0/0x3c8 [ 155.594969] devm_hwmon_device_register_with_groups+0x78/0xe0 [ 155.600703] mlxbf_pmc_probe+0x224/0x3a0 [mlxbf_pmc] [ 155.605669] platform_probe+0x6c/0xe0 [ 155.609320] really_probe+0xc4/0x398 [ 155.612887] __driver_probe_device+0x80/0x168 [ 155.617233] driver_probe_device+0x44/0x120 [ 155.621405] __driver_attach+0xf4/0x200 [ 155.625230] bus_for_each_dev+0x7c/0xe8 [ 155.629055] driver_attach+0x28/0x38 [ 155.632619] bus_add_driver+0x110/0x238 [ 155.636445] driver_register+0x64/0x128 [ 155.640270] __platform_driver_register+0x2c/0x40 [ 155.644965] pmc_driver_init+0x24/0xff8 [mlxbf_pmc] [ 155.649833] do_one_initcall+0x70/0x3d0 [ 155.653660] do_init_module+0x64/0x220 [ 155.657400] load_module+0x628/0x6a8 [ 155.660964] init_module_from_file+0x8c/0xd8 [ 155.665222] idempotent_init_module+0x194/0x290 [ 155.669742] __arm64_sys_finit_module+0x6c/0xd8 [ 155.674261] invoke_syscall.constprop.0+0x74/0xd0 [ 155.678957] do_el0_svc+0xb4/0xd0 [ 155.682262] el0_svc+0x5c/0x248 [ 155.685394] el0t_64_sync_handler+0x134/0x150 [ 155.689739] el0t_64_sync+0x17c/0x180 [ 155.693390] irq event stamp: 6407 [ 155.696693] hardirqs last enabled at (6407): [] console_unlock+0x154/0x1b8 [ 155.705207] hardirqs last disabled at (6406): [] console_unlock+0x19c/0x1b8 [ 155.713719] softirqs last enabled at (6404): [] handle_softirqs+0x4f4/0x518 [ 155.722320] softirqs last disabled at (6395): [] __do_softirq+0x18/0x20 [ 155.730484] ---[ end trace 0000000000000000 ]--- Signed-off-by: Luiz Capitulino Link: https://lore.kernel.org/r/20240912190532.377097-1-luizcap@redhat.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-pmc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 4ed9c7fd2b62..9d18dfca6a67 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1774,6 +1774,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ /* "event_list" sysfs to list events supported by the block */ attr = &pmc->block[blk_num].attr_event_list; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.mode = 0444; attr->dev_attr.show = mlxbf_pmc_event_list_show; attr->nr = blk_num; @@ -1787,6 +1788,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ if (strstr(pmc->block_name[blk_num], "l3cache") || ((pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE))) { attr = &pmc->block[blk_num].attr_enable; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_enable_show; attr->dev_attr.store = mlxbf_pmc_enable_store; @@ -1814,6 +1816,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ /* "eventX" and "counterX" sysfs to program and read counter values */ for (j = 0; j < pmc->block[blk_num].counters; ++j) { attr = &pmc->block[blk_num].attr_counter[j]; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_counter_show; attr->dev_attr.store = mlxbf_pmc_counter_store; @@ -1826,6 +1829,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ attr = NULL; attr = &pmc->block[blk_num].attr_event[j]; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_event_show; attr->dev_attr.store = mlxbf_pmc_event_store; @@ -1861,6 +1865,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num) while (count > 0) { --count; attr = &pmc->block[blk_num].attr_event[count]; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_counter_show; attr->dev_attr.store = mlxbf_pmc_counter_store; From df40a23cc34c200cfde559eda7ca540f3ae7bd9e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Sep 2024 11:02:55 +0200 Subject: [PATCH 64/65] platform/x86: x86-android-tablets: Adjust Xiaomi Pad 2 bottom bezel touch buttons LED The "input-events" LED trigger used to turn on the backlight LEDs had to be rewritten to use led_trigger_register_simple() + led_trigger_event() to fix a serious locking issue. This means it no longer supports using blink_brightness to set a per LED brightness for the trigger and it no longer sets LED_CORE_SUSPENDRESUME. Adjust the MiPad 2 bottom bezel touch buttons LED class device to match: 1. Make LED_FULL the maximum brightness to fix the LED brightness being very low when on. 2. Set flags = LED_CORE_SUSPENDRESUME. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240916090255.35548-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/other.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index aad7114c43c3..7db8aa58b907 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -670,7 +670,7 @@ static const struct software_node *ktd2026_node_group[] = { * is controlled by the "pwm_soc_lpss_2" PWM output. */ #define XIAOMI_MIPAD2_LED_PERIOD_NS 19200 -#define XIAOMI_MIPAD2_LED_DEFAULT_DUTY 6000 /* From Android kernel */ +#define XIAOMI_MIPAD2_LED_MAX_DUTY_NS 6000 /* From Android kernel */ static struct pwm_device *xiaomi_mipad2_led_pwm; @@ -679,7 +679,7 @@ static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev, { struct pwm_state state = { .period = XIAOMI_MIPAD2_LED_PERIOD_NS, - .duty_cycle = val, + .duty_cycle = XIAOMI_MIPAD2_LED_MAX_DUTY_NS * val / LED_FULL, /* Always set PWM enabled to avoid the pin floating */ .enabled = true, }; @@ -701,11 +701,11 @@ static int __init xiaomi_mipad2_init(struct device *dev) return -ENOMEM; led_cdev->name = "mipad2:white:touch-buttons-backlight"; - led_cdev->max_brightness = XIAOMI_MIPAD2_LED_PERIOD_NS; - /* "input-events" trigger uses blink_brightness */ - led_cdev->blink_brightness = XIAOMI_MIPAD2_LED_DEFAULT_DUTY; + led_cdev->max_brightness = LED_FULL; led_cdev->default_trigger = "input-events"; led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set; + /* Turn LED off during suspend */ + led_cdev->flags = LED_CORE_SUSPENDRESUME; ret = devm_led_classdev_register(dev, led_cdev); if (ret) From 837acb691c844d0525f4ac86f2a2ce55a9706908 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 17 Sep 2024 12:39:55 +0200 Subject: [PATCH 65/65] MAINTAINERS: adjust file entry in INTEL MID PLATFORM Commit 5f1cda51107f ("platform/x86: intel_scu_wdt: Move intel_scu_wdt.h to x86 subfolder") moves intel-mid_wdt.h in ./include/linux/platform_data into the x86 subdirectory, but misses to adjust the INTEL MID PLATFORM section, which is referring to this file. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken reference. Adjust the file entry to this header file movement. Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20240917103955.102921-1-lukas.bulwahn@redhat.com Signed-off-by: Hans de Goede --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3f3a8d8abf87..5df809ed3605 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11481,7 +11481,7 @@ F: drivers/platform/x86/intel_scu_* F: drivers/staging/media/atomisp/ F: drivers/watchdog/intel-mid_wdt.c F: include/linux/mfd/intel_soc_pmic_mrfld.h -F: include/linux/platform_data/intel-mid_wdt.h +F: include/linux/platform_data/x86/intel-mid_wdt.h F: include/linux/platform_data/x86/intel_scu_ipc.h INTEL P-Unit IPC DRIVER