From 451b1c19dc7cbbad194a6e717b6a732c443d7dd5 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Fri, 11 Sep 2026 02:39:25 +0800 Subject: [PATCH 01/12] hwmon: (k10temp) Fix model id range of Zen5 Turin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model 20h-2Fh are mobile processors with single CCD. For example, model 24h is Strix Point, i.e., Ryzen AI 7 (PRO) (H/HX) 360/365/370. Including mobile processors in the model id range of Zen5 Turin processors leads to bogus reporting: k10temp-pci-00c3 Adapter: PCI adapter Tctl: +54.1°C Tccd4: +148.6°C Tccd6: +148.4°C Tccd7: +149.1°C Tccd8: +149.2°C Tccd9: +149.2°C Tccd12: +149.1°C Tccd14: +22.0°C Tccd15: +22.0°C Tccd16: +22.0°C Fix it by removing the said range. Fixes: 8440d5aca227 ("hwmon: (k10temp) Add per-CCD temperature monitoring for Zen5 Turin") Signed-off-by: Rong Zhang Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260911-k10temp-fix-zen5-epyc-v1-1-643f5a248ae1@rong.moe Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 75a45010d687..3e7e63edc6a3 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -523,7 +523,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) } } else if (boot_cpu_data.x86 == 0x1a) { switch (boot_cpu_data.x86_model) { - case 0x00 ... 0x2f: /* Zen5 Turin */ + case 0x00 ... 0x1f: /* Zen5 Turin */ data->ccd_offset = 0x1F0; k10temp_get_ccd_support(data, 16); break; From 26d5ff79768548efb1e604bb6e8697c101e06269 Mon Sep 17 00:00:00 2001 From: Yibo Tan Date: Fri, 11 Sep 2026 15:18:09 +0800 Subject: [PATCH 02/12] hwmon: (pwm-fan) Stop RPM timer before freeing tach data sample_timer() rearms the RPM timer and accesses the devm-managed ctx->tachs and ctx->pulses_per_revolution arrays. The cleanup action which stops the timer is registered before those arrays are allocated. Since devres releases entries in reverse order, driver detach can free the arrays before pwm_fan_cleanup() shuts down the timer. A timer expiry in that window accesses the freed tach data. With a KASAN kernel, a test-only kprobe delayed entry to pwm_fan_cleanup() while normal sysfs unbind ran. Each of three runs reported three four-byte reads and two four-byte writes in sample_timer() after its backing devm allocations had been freed. The helper did not invoke the timer callback, cleanup actions or free functions. With the fix, three matching unbind runs completed without KASAN, BUG, WARNING, Oops or panic. Instrumentation confirmed that timer retirement completed before the first timer backing allocation was released. Split timer retirement from the power cleanup and register its devres action after the timer backing data and IRQ actions are installed. This preserves the early power rollback action while ensuring the timer is retired before its backing data is released. Use timer_shutdown_sync() because the callback can rearm itself. Fixes: 01695410d452 ("hwmon: (pwm-fan) Store tach data separately") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Yibo Tan Link: https://patch.msgid.link/20260911071809.130151-1-lhfff@tju.edu.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 3b87f65bae05..c633d7f6464c 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -483,7 +483,6 @@ static void pwm_fan_cleanup(void *__ctx) { struct pwm_fan_ctx *ctx = __ctx; - timer_delete_sync(&ctx->rpm_timer); if (ctx->pwm_shutdown) { ctx->enable_mode = pwm_enable_reg_enable; __set_pwm(ctx, ctx->pwm_shutdown); @@ -494,6 +493,13 @@ static void pwm_fan_cleanup(void *__ctx) } } +static void pwm_fan_timer_cleanup(void *__ctx) +{ + struct pwm_fan_ctx *ctx = __ctx; + + timer_shutdown_sync(&ctx->rpm_timer); +} + static int pwm_fan_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev; @@ -644,6 +650,10 @@ static int pwm_fan_probe(struct platform_device *pdev) } if (ctx->tach_count > 0) { + ret = devm_add_action_or_reset(dev, pwm_fan_timer_cleanup, ctx); + if (ret) + return ret; + ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); @@ -700,6 +710,7 @@ static void pwm_fan_shutdown(struct platform_device *pdev) { struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); + pwm_fan_timer_cleanup(ctx); pwm_fan_cleanup(ctx); } From 7bae83ffb133bc373d098fa6828cef2ef4da49fe Mon Sep 17 00:00:00 2001 From: "Thomas Richard (congatec GmbH)" Date: Fri, 11 Sep 2026 19:31:58 +0200 Subject: [PATCH 03/12] hwmon: (cgbc-hwmon) Fix current sensors ID lookup Current sensors on the Congatec Board Controller don't use consecutive IDs, unlike other sensor types (voltage, temperature, fan). The driver assumed consecutive IDs and performed a simple lookup, which caused an unknown sensor warning. Define current sensor IDs explicitly. Changes the warning on conga-SA7 (type and channel are correct now). Before: Board Controller returned an unknown sensor (type=2, channel=17), ignore it After: Board Controller returned an unknown sensor (bc_type=1, bc_id=11), ignore it Cc: stable@kernel.org Fixes: 08ebc9def79f ("hwmon: Add Congatec Board Controller monitoring driver") Signed-off-by: Thomas Richard (congatec GmbH) Link: https://patch.msgid.link/20260911-cgbc-hwmon-fix-and-new-sensors-v2-1-0c6bf078d173@bootlin.com Signed-off-by: Guenter Roeck --- drivers/hwmon/cgbc-hwmon.c | 95 ++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c index 3aff4e092132..2effa0b56286 100644 --- a/drivers/hwmon/cgbc-hwmon.c +++ b/drivers/hwmon/cgbc-hwmon.c @@ -52,30 +52,35 @@ static const char * const cgbc_hwmon_labels_temp[] = { "BOTTOMDIM Temperature", }; -static const struct { - enum hwmon_sensor_types type; - const char *label; -} cgbc_hwmon_labels_in[] = { - { hwmon_in, "CPU Voltage" }, - { hwmon_in, "DC Runtime Voltage" }, - { hwmon_in, "DC Standby Voltage" }, - { hwmon_in, "CMOS Battery Voltage" }, - { hwmon_in, "Battery Voltage" }, - { hwmon_in, "AC Voltage" }, - { hwmon_in, "Other Voltage" }, - { hwmon_in, "5V Voltage" }, - { hwmon_in, "5V Standby Voltage" }, - { hwmon_in, "3V3 Voltage" }, - { hwmon_in, "3V3 Standby Voltage" }, - { hwmon_in, "VCore A Voltage" }, - { hwmon_in, "VCore B Voltage" }, - { hwmon_in, "12V Voltage" }, - { hwmon_curr, "DC Current" }, - { hwmon_curr, "5V Current" }, - { hwmon_curr, "12V Current" }, +static const char * const cgbc_hwmon_labels_in[] = { + "CPU Voltage", + "DC Runtime Voltage", + "DC Standby Voltage", + "CMOS Battery Voltage", + "Battery Voltage", + "AC Voltage", + "Other Voltage", + "5V Voltage", + "5V Standby Voltage", + "3V3 Voltage", + "3V3 Standby Voltage", + "VCore A Voltage", + "VCore B Voltage", + "12V Voltage", }; -#define CGBC_HWMON_NB_IN_SENSORS 14 +/* + * Current sensors are a bit special, they don't have consecutive IDs like + * other types of sensors. So they need to be defined explicitly. + */ +static const struct { + const char *label; + int id; +} cgbc_hwmon_labels_curr[] = { + { "DC Current", 0x12 }, + { "5V Current", 0x18 }, + { "12V Current", 0x1E }, +}; static const char * const cgbc_hwmon_labels_fan[] = { "CPU Fan", @@ -114,7 +119,8 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data * for (i = 0; i < nb_sensors; i++) { enum cgbc_sensor_types type; - unsigned int channel; + unsigned int channel, id; + int j; /* * No need to request data for the first sensor. @@ -128,32 +134,49 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data * } type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]); - channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1; + id = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]); + channel = id - 1; if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) { sensor->type = hwmon_temp; sensor->label = cgbc_hwmon_labels_temp[channel]; - } else if (type == CGBC_HWMON_TYPE_IN && - channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) { + } else if (type == CGBC_HWMON_TYPE_IN) { /* * The Board Controller doesn't differentiate current and voltage sensors. - * Get the sensor type from cgbc_hwmon_labels_in[channel].type instead. + * First check if it is a current sensor. */ - sensor->type = cgbc_hwmon_labels_in[channel].type; - sensor->label = cgbc_hwmon_labels_in[channel].label; + for (j = 0; j < ARRAY_SIZE(cgbc_hwmon_labels_curr); j++) { + if (id == cgbc_hwmon_labels_curr[j].id) { + sensor->type = hwmon_curr; + sensor->label = cgbc_hwmon_labels_curr[j].label; + channel = j; + } + } + + /* If it's not a current sensor, it may be a voltage sensor. */ + if (!sensor->label && channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) { + sensor->type = hwmon_in; + sensor->label = cgbc_hwmon_labels_in[channel]; + } } else if (type == CGBC_HWMON_TYPE_FAN && channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) { sensor->type = hwmon_fan; sensor->label = cgbc_hwmon_labels_fan[channel]; - } else { - dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it", - type, channel); + } + + if (!sensor->label) { + dev_warn(dev, "Board Controller returned an unknown sensor (bc_type=%d, bc_id=%d), ignore it", + type, id); continue; } sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]); sensor->channel = channel; sensor->index = i; + + dev_dbg(dev, "Found sensor: bc_type=%d, bc_id=%d, hwmon_type=%d, hwmon_channel=%d, hwmon_label='%s', active=%d\n", + type, id, sensor->type, sensor->channel, sensor->label, sensor->active); + sensor++; hwmon->nb_sensors++; } @@ -167,14 +190,6 @@ static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data * struct cgbc_hwmon_sensor *sensor = NULL; int i; - /* - * The Board Controller doesn't differentiate current and voltage sensors. - * The channel value (from the Board Controller point of view) shall be computed for current - * sensors. - */ - if (type == hwmon_curr) - channel += CGBC_HWMON_NB_IN_SENSORS; - for (i = 0; i < hwmon->nb_sensors; i++) { if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) { sensor = &hwmon->sensors[i]; From 3550d1dbbcb9f51b77e077e8958423ee2c401c6c Mon Sep 17 00:00:00 2001 From: "Thomas Richard (congatec GmbH)" Date: Fri, 11 Sep 2026 19:31:59 +0200 Subject: [PATCH 04/12] hwmon: (cgbc-hwmon) Add missing sensors Add the following sensors: - Alternate Board Temperature (temp11_input) - Top DIMM 1-7 Temperature (temp12_input to temp18_input) - Bottom DIMM 1 Temperature (temp19_input) - 12V Standby Voltage (in14_input) This fixes the following warning on conga-SA7: Board Controller returned an unknown sensor (bc_type=1, bc_id=11), ignore it Also update existing labels to match Congatec documentation. Cc: stable@kernel.org Fixes: 08ebc9def79f ("hwmon: Add Congatec Board Controller monitoring driver") Signed-off-by: Thomas Richard (congatec GmbH) Link: https://patch.msgid.link/20260911-cgbc-hwmon-fix-and-new-sensors-v2-2-0c6bf078d173@bootlin.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/cgbc-hwmon.rst | 42 +++++++++++++++--------- drivers/hwmon/cgbc-hwmon.c | 52 +++++++++++++++++++----------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/Documentation/hwmon/cgbc-hwmon.rst b/Documentation/hwmon/cgbc-hwmon.rst index 3a5e6e6e8639..c6d09232392a 100644 --- a/Documentation/hwmon/cgbc-hwmon.rst +++ b/Documentation/hwmon/cgbc-hwmon.rst @@ -28,34 +28,44 @@ system. Name Description ============= ====================== temp1_input CPU temperature -temp2_input Box temperature +temp2_input Case temperature temp3_input Ambient temperature -temp4_input Board temperature -temp5_input Carrier temperature -temp6_input Chipset temperature -temp7_input Video temperature +temp4_input CPU Board temperature +temp5_input Carrier Board temperature +temp6_input System Chipset temperature +temp7_input Video Controller/Board temperature temp8_input Other temperature -temp9_input TOPDIM temperature -temp10_input BOTTOMDIM temperature -in0_input CPU voltage +temp9_input Top DIMM 0 temperature +temp10_input Bottom DIMM 0 temperature +temp11_input Alternate Board temperature +temp12_input Top DIMM 1 temperature +temp13_input Top DIMM 2 temperature +temp14_input Top DIMM 3 temperature +temp15_input Top DIMM 4 temperature +temp16_input Top DIMM 5 temperature +temp17_input Top DIMM 6 temperature +temp18_input Top DIMM 7 temperature +temp19_input Bottom DIMM 1 temperature +in0_input CPU Core voltage in1_input DC Runtime voltage in2_input DC Standby voltage in3_input CMOS Battery voltage -in4_input Battery voltage +in4_input Battery Supply voltage in5_input AC voltage in6_input Other voltage -in7_input 5V voltage +in7_input 5V Runtime voltage in8_input 5V Standby voltage -in9_input 3V3 voltage +in9_input 3V3 Runtime voltage in10_input 3V3 Standby voltage in11_input VCore A voltage in12_input VCore B voltage -in13_input 12V voltage -curr1_input DC current -curr2_input 5V current -curr3_input 12V current +in13_input 12V Runtime voltage +in14_input 12V Standby voltage +curr1_input DC Runtime current +curr2_input 5V Runtime current +curr3_input 12V Runtime current fan1_input CPU fan -fan2_input Box fan +fan2_input Case fan fan3_input Ambient fan fan4_input Chiptset fan fan5_input Video fan diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c index 2effa0b56286..230062a46907 100644 --- a/drivers/hwmon/cgbc-hwmon.c +++ b/drivers/hwmon/cgbc-hwmon.c @@ -41,32 +41,42 @@ enum cgbc_sensor_types { static const char * const cgbc_hwmon_labels_temp[] = { "CPU Temperature", - "Box Temperature", + "Case Temperature", "Ambient Temperature", - "Board Temperature", - "Carrier Temperature", - "Chipset Temperature", - "Video Temperature", + "CPU Board Temperature", + "Carrier Board Temperature", + "System Chipset Temperature", + "Video Controller/Board Temperature", "Other Temperature", - "TOPDIM Temperature", - "BOTTOMDIM Temperature", + "Top DIMM 0 Temperature", + "Bottom DIMM 0 Temperature", + "Alternate Board Temperature", + "Top DIMM 1 Temperature", + "Top DIMM 2 Temperature", + "Top DIMM 3 Temperature", + "Top DIMM 4 Temperature", + "Top DIMM 5 Temperature", + "Top DIMM 6 Temperature", + "Top DIMM 7 Temperature", + "Bottom DIMM 1 Temperature", }; static const char * const cgbc_hwmon_labels_in[] = { - "CPU Voltage", + "CPU Core Voltage", "DC Runtime Voltage", "DC Standby Voltage", "CMOS Battery Voltage", - "Battery Voltage", + "Battery Supply Voltage", "AC Voltage", "Other Voltage", - "5V Voltage", + "5V Runtime Voltage", "5V Standby Voltage", - "3V3 Voltage", + "3V3 Runtime Voltage", "3V3 Standby Voltage", "VCore A Voltage", "VCore B Voltage", - "12V Voltage", + "12V Runtime Voltage", + "12V Standby Voltage", }; /* @@ -77,14 +87,14 @@ static const struct { const char *label; int id; } cgbc_hwmon_labels_curr[] = { - { "DC Current", 0x12 }, - { "5V Current", 0x18 }, - { "12V Current", 0x1E }, + { "DC Runtime Current", 0x12 }, + { "5V Runtime Current", 0x18 }, + { "12V Runtime Current", 0x1E }, }; static const char * const cgbc_hwmon_labels_fan[] = { "CPU Fan", - "Box Fan", + "Case Fan", "Ambient Fan", "Chipset Fan", "Video Fan", @@ -255,7 +265,12 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = { HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, - HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, @@ -263,7 +278,8 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = { HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, - HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL), From 06bd6794b5fd2163880ac3bfe973d4cc61f359f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 11 Sep 2026 14:53:37 +0100 Subject: [PATCH 05/12] hwmon: (pmbus/core) increase number of phases and add new mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase the number of phases to 16 as a new upcoming device supports such a number. While at it, add a new mask for controlling the source of the output voltage. Note (groeck): This patch was meant to prepare for support of MAX20826 and compatible devices, which support more than 10 phases per page. However, Sashiko reports that the mp2975 driver already supports up to 14 phases, and the mp2856 driver supports up to 12 phases. This already has the potential for out-of-bounds writes when probing the affected chips, making this patch a bug fix. Fixes: 2c6fcbb21149 ("hwmon: (pmbus) Add support for MPS Multi-phase mp2975 controller") Fixes: f9e5f289b686 ("hwmon: (pmbus) Add support for MPS Multi-phase mp2856/mp2857 controller") Signed-off-by: Nuno Sá Link: https://patch.msgid.link/20260911-hwmon-max20826-support-v2-1-5e30cbd97d84@analog.com Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 2cd3216b3cd9..920c1102ab6d 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -242,6 +242,7 @@ enum pmbus_regs { /* * OPERATION */ +#define PB_OPERATION_CONTROL_V_SRC GENMASK(5, 4) #define PB_OPERATION_CONTROL_ON BIT(7) /* @@ -386,7 +387,7 @@ enum pmbus_sensor_classes { }; #define PMBUS_PAGES 32 /* Per PMBus specification */ -#define PMBUS_PHASES 10 /* Maximum number of phases per page */ +#define PMBUS_PHASES 16 /* Maximum number of phases per page */ /* Functionality bit mask */ #define PMBUS_HAVE_VIN BIT(0) From bdf5f731957de48acada392f28e82bc019713adb Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Mon, 14 Sep 2026 17:41:36 +0700 Subject: [PATCH 06/12] hwmon: (gpio-fan) return IRQ_HANDLED from the shared alarm IRQ handler fan_alarm_irq_handler() always schedules alarm_work but returns IRQ_NONE, so the kernel treats every alarm interrupt as unhandled. On a shared line that risks the whole line being disabled as spurious. v1 just fixed that, but it was still IRQF_SHARED, and always returning IRQ_HANDLED there defeats spurious-interrupt detection for the line -- if the interrupt ever fires without a real event, nothing catches it, and a fault could spin the CPU in the handler. Sashiko flagged this in v1, and Guenter confirmed: this interrupt must not be shared. So v2 drops IRQF_SHARED too. Fixes: d6fe1360f42e ("hwmon: add generic GPIO fan driver") Reported-by: Sashiko AI review Link: https://lore.kernel.org/r/20260901160931.DD3811F00A3D@smtp.kernel.org Assisted-by: Claude:claude-opus-4 Signed-off-by: Cong Nguyen Link: https://patch.msgid.link/20260914104136.1797979-1-congnt264@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index df8bd9707605..3f78375eeb44 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -68,7 +68,7 @@ static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) schedule_work(&fan_data->alarm_work); - return IRQ_NONE; + return IRQ_HANDLED; } static ssize_t fan1_alarm_show(struct device *dev, @@ -103,7 +103,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data) irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, - IRQF_SHARED, "GPIO fan alarm", fan_data); + 0, "GPIO fan alarm", fan_data); } /* From 0ff9c7775e51ac6d47b1bb5c46f06b1434fe58a8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 14 Sep 2026 14:28:09 +0800 Subject: [PATCH 07/12] hwmon: (w83791d) remove fan/pwm 4-5 sysfs group on remove When the fan/pwm 4-5 pins are not used as GPIO, w83791d_probe() creates the w83791d_group_fanpwm45 sysfs group on the I2C client device. The probe error path removes this group when a later initialization step fails, but the normal remove path only removes w83791d_group. As a result, the optional fan/pwm 4-5 sysfs files can remain after the driver is unbound. The callbacks associated with these files access the driver data, which is devm allocated and released after driver unbind. Leaving the sysfs files behind can therefore result in accesses to stale driver data. Remove w83791d_group_fanpwm45 during normal teardown as well. This issue was found by manual code inspection. Fixes: 6e1ecd9b8f13 ("hwmon: (w83791d) fan 4/5 pins can also be used for gpio") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260914062809.1650538-1-lgs201920130244@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83791d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 4a777430af5c..4b07a25ae59e 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1415,6 +1415,7 @@ static void w83791d_remove(struct i2c_client *client) struct w83791d_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); sysfs_remove_group(&client->dev.kobj, &w83791d_group); } From c702a5f18b780e477eccbbab558e590e9673e4cb Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 14 Sep 2026 15:36:38 +0800 Subject: [PATCH 08/12] hwmon: (w83793) release probe data through kref w83793_probe() initializes data->kref to manage the lifetime of the driver data. The normal remove path drops the driver-owned reference with kref_put(), while watchdog users take and release additional references through the same kref. However, the probe error path still frees data directly with kfree(). This bypasses the kref-managed lifetime and discards the initial reference without a matching kref_put(), leaving the reference accounting unbalanced. Drop the probe-owned reference with kref_put() instead and let w83793_release_resources() perform the final free, matching the normal remove path. This issue was found by manual code inspection. Fixes: 5852f9609d21 ("hwmon: (w83793) Add watchdog functionality") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260914073638.1662500-1-lgs201920130244@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83793.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index a548586369e1..c6ef04c69856 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1928,7 +1928,9 @@ static int w83793_probe(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); free_mem: - kfree(data); + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, w83793_release_resources); + mutex_unlock(&watchdog_data_mutex); exit: return err; } From e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Wed, 16 Sep 2026 05:29:26 +0500 Subject: [PATCH 09/12] hwmon: (hp-wmi-sensors) Fix use-after-free in fungible_show() nsensor->current_state is dynamically replaced as the sensor's state changes. update_numeric_sensor_from_wobj() does this by freeing the old string and installing a new one: if (strcmp(trimmed, nsensor->current_state)) { new_string = hp_wmi_strdup(dev, trimmed); if (new_string) { devm_kfree(dev, nsensor->current_state); nsensor->current_state = new_string; } } This function is only ever called from hp_wmi_update_info() while state->lock is held, so the free-and-replace itself is properly serialized against concurrent updates. fungible_show(), however, reads the same pointer after the lock has already been dropped: err = hp_wmi_update_info(state, info); if (err) return err; switch (prop) { ... case HP_WMI_PROPERTY_CURRENT_STATE: seq_printf(seqf, "%s\n", nsensor->current_state); break; hp_wmi_update_info() takes state->lock internally and releases it before returning, so by the time fungible_show() dereferences nsensor->current_state in seq_printf(), no lock is held. Two processes reading a sensor's current_state debugfs entry at overlapping times (or one reading it while another read of the same sensor triggers a refresh) can race: one thread's seq_printf() can be part-way through printing the string at the moment another thread's call into update_numeric_sensor_from_wobj() frees it with devm_kfree() and installs a new pointer, causing a use-after-free read. Take state->lock around the read in fungible_show() as well, so it can never run concurrently with the free-and-replace in update_numeric_sensor_from_wobj(). Fixes: 23902f98f8d4 ("hwmon: add HP WMI Sensors driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Acked-by: James Seo Link: https://patch.msgid.link/20260916002926.161595-1-meatuni001@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/hp-wmi-sensors.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c index 03c684ba83bd..55aee16df57d 100644 --- a/drivers/hwmon/hp-wmi-sensors.c +++ b/drivers/hwmon/hp-wmi-sensors.c @@ -1247,7 +1247,9 @@ static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop) break; case HP_WMI_PROPERTY_CURRENT_STATE: + mutex_lock(&state->lock); seq_printf(seqf, "%s\n", nsensor->current_state); + mutex_unlock(&state->lock); break; case HP_WMI_PROPERTY_UNIT_MODIFIER: From 1d12fb94ac0975566545871dda100df34df5f845 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Tue, 15 Sep 2026 16:48:35 +0000 Subject: [PATCH 10/12] hwmon: (pmbus/tps53679) Fix TPS53676 phase page decoding tps53676_identify() reads the USER_DATA_03 phase configuration to count the phases assigned to each channel and derive the number of PMBus pages. In each 16-bit phase descriptor the channel (PAGE) is encoded in bit 4 and the firing order in bits 3:0, but the code tested bit 3 (0x08), which is part of the firing-order field. TPS53676 supports up to seven phases, so firing-order bit 3 is never set. As a result the existing test classifies every enabled phase as channel A. On a dual-channel configuration the phases assigned to channel B are therefore miscounted as channel A and page 1 is not exposed. Test the PAGE field (bit 4) instead. Fixes: cb3d37b59012 ("hwmon: (pmbus/tps53679) Add support for TI TPS53676") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Link: https://patch.msgid.link/20260915164823.160977-2-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 31e54608b3c9..9f27832703ff 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -187,7 +187,7 @@ static int tps53676_identify(struct i2c_client *client, return -EIO; for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { if (buf[i + 1] & 0x80) { - if (buf[i] & 0x08) + if (buf[i] & BIT(4)) phases_b++; else phases_a++; From 089070b51ccbac411462a30a454690274c6e4270 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Wed, 16 Sep 2026 23:54:17 +0000 Subject: [PATCH 11/12] hwmon: (pmbus/tps53679) Select page 0 for single-page TPS53676 tps53676_identify() derives the number of PMBus pages but does not ensure that page 0 is selected for single-page configurations. pmbus_set_page() does not update the PAGE register when info->pages is 1, so if boot firmware leaves PAGE set to another value subsequent register accesses may target the wrong page. For single-page devices, select page 0 explicitly. Fixes: cb3d37b59012 ("hwmon: (pmbus/tps53679) Add support for TI TPS53676") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Link: https://patch.msgid.link/20260916235406.681131-2-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 9f27832703ff..6c25701b36fc 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -200,6 +200,15 @@ static int tps53676_identify(struct i2c_client *client, if (phases_b > 0) { info->pages = 2; info->phases[1] = phases_b; + } else { + /* + * pmbus_set_page() does not update the PAGE register on + * single-page devices, so select page 0 explicitly in case + * the boot firmware left the device on another page. + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; } return 0; } From 92b68492eae701e5b0e9d142ffe229921af7b1fa Mon Sep 17 00:00:00 2001 From: James Seo Date: Wed, 16 Sep 2026 15:19:15 -0700 Subject: [PATCH 12/12] hwmon: (hp-wmi-sensors) Improve raw WMI string handling Commit c9ba59258094 ("hwmon: (hp-wmi-sensors) Fix failure to load on EliteDesk 800 G6") left out some logic for recognizing raw WMI strings in check_numeric_sensor_wobj(). This issue was reported by a user along with an incomplete and unsuitable proposed solution [1]. Add the missing logic and properly remedy the issue. Also slightly refactor how raw WMI strings are recognized elsewhere to make the intent that they should be treated as regular ACPI strings clearer. Reported-by: Muhammad Bilal Link: https://lore.kernel.org/linux-hwmon/20260916002907.161210-1-meatuni001@gmail.com/ [1] Fixes: c9ba59258094 ("hwmon: (hp-wmi-sensors) Fix failure to load on EliteDesk 800 G6") Signed-off-by: James Seo Link: https://patch.msgid.link/20260916221912.434119-5-james@equiv.tech Signed-off-by: Guenter Roeck --- drivers/hwmon/hp-wmi-sensors.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c index 55aee16df57d..cfa0d6ad0fb3 100644 --- a/drivers/hwmon/hp-wmi-sensors.c +++ b/drivers/hwmon/hp-wmi-sensors.c @@ -526,14 +526,12 @@ static int check_wobj(const union acpi_object *wobj, for (prop = 0; prop <= last_prop; prop++) { type = elements[prop].type; valid_type = property_map[prop]; - if (type != valid_type) { - if (type == ACPI_TYPE_BUFFER && - valid_type == ACPI_TYPE_STRING && - is_raw_wmi_string(elements[prop].buffer.pointer, - elements[prop].buffer.length)) - continue; + if (type == ACPI_TYPE_BUFFER && + is_raw_wmi_string(elements[prop].buffer.pointer, + elements[prop].buffer.length)) + type = ACPI_TYPE_STRING; + if (type != valid_type) return -EINVAL; - } } return 0; @@ -579,6 +577,7 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj, int prop = HP_WMI_PROPERTY_NAME; acpi_object_type valid_type; union acpi_object *elements; + union acpi_object *element; u32 elem_count; int last_prop; bool is_new; @@ -602,13 +601,20 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj, elem_count > HP_WMI_MAX_PROPERTIES) return -EINVAL; - type = elements[HP_WMI_PROPERTY_SIZE].type; + element = &elements[HP_WMI_PROPERTY_SIZE]; + type = element->type; switch (type) { case ACPI_TYPE_INTEGER: is_new = true; last_prop = HP_WMI_PROPERTY_RATE_UNITS; break; + case ACPI_TYPE_BUFFER: + if (!is_raw_wmi_string(element->buffer.pointer, + element->buffer.length)) + return -EINVAL; + fallthrough; + case ACPI_TYPE_STRING: is_new = false; last_prop = HP_WMI_PROPERTY_CURRENT_READING; @@ -631,6 +637,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj, for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) { type = elements[i].type; valid_type = hp_wmi_property_map[prop]; + if (type == ACPI_TYPE_BUFFER && + is_raw_wmi_string(elements[i].buffer.pointer, + elements[i].buffer.length)) + type = ACPI_TYPE_STRING; if (type != valid_type) return -EINVAL; @@ -651,6 +661,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj, /* PossibleStates[0] has already been type-checked. */ for (j = 0; i + 1 < elem_count && j + 1 < count; j++) { type = elements[++i].type; + if (type == ACPI_TYPE_BUFFER && + is_raw_wmi_string(elements[i].buffer.pointer, + elements[i].buffer.length)) + type = ACPI_TYPE_STRING; if (type != valid_type) return -EINVAL; }