]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwmon: (pmbus) Fix passing events to regulator core
authorGuenter Roeck <linux@roeck-us.net>
Mon, 29 Jun 2026 23:08:00 +0000 (16:08 -0700)
committerGuenter Roeck <linux@roeck-us.net>
Mon, 29 Jun 2026 23:25:27 +0000 (16:25 -0700)
Sashiko reports:

Commit 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with
mutex") introduced a worker to batch regulator events over time using
atomic_or(). The delayed worker then passes the combined bitmask unmodified
to regulator_notifier_call_chain().

The core regulator subsystem's regulator_handle_critical() function
evaluates the event parameter using a strict switch statement. If
multiple distinct faults occur before the worker runs (e.g.,
REGULATOR_EVENT_UNDER_VOLTAGE | REGULATOR_EVENT_OVER_CURRENT), the combined
bitmask fails to match any case. This leaves the reason as NULL and
completely bypasses the critical hw_protection_trigger().

Fix the problem by passing events bit by bit to the regulator event
handler.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with mutex")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/pmbus/pmbus_core.c

index 8123a568af40d5845fc2382601f55a5618c748d3..3143b9e0316c46b22468e69a544d4c16ba6ffa35 100644 (file)
@@ -3347,18 +3347,23 @@ static void pmbus_regulator_notify_worker(struct work_struct *work)
        int i, j;
 
        for (i = 0; i < data->info->pages; i++) {
-               int event;
+               unsigned int event;
 
                event = atomic_xchg(&data->regulator_events[i], 0);
                if (!event)
                        continue;
 
                for (j = 0; j < data->info->num_regulators; j++) {
-                       if (i == rdev_get_id(data->rdevs[j])) {
+                       if (i != rdev_get_id(data->rdevs[j]))
+                               continue;
+                       while (event) {
+                               unsigned int _event = BIT(__ffs(event));
+
                                regulator_notifier_call_chain(data->rdevs[j],
-                                                             event, NULL);
-                               break;
+                                                             _event, NULL);
+                               event &= ~_event;
                        }
+                       break;
                }
        }
 }