[PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs

From: Lee Jones

Date: Wed Oct 21 2026 - 08:58:01 EST


aw2013_probe() holds chip->mutex across aw2013_probe_dt(), which calls
devm_led_classdev_register_ext(). When a default trigger such as
"timer" is configured on an LED in the Device Tree, the LED core
synchronously activates the trigger during registration and invokes
aw2013_blink_set() on the same thread, which attempts to acquire
chip->mutex and deadlocks.

Reset the chip, disable the regulators, and release chip->mutex in
aw2013_probe() before calling aw2013_probe_dt(), and update
chip->num_leds before each LED is registered so trigger callbacks can
enable the chip cleanly during registration.

Fixes: 59ea3c9faf32 ("leds: add aw2013 driver")
Closes: https://sashiko.dev/#/bug/linux-5e2c9083-b95b-4aa4-aa0a-b0be8865ff12
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
drivers/leds/leds-aw2013.c | 54 ++++++++++++++++++++++----------------
1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
index dfeac8b2993f..c947cd068963 100644
--- a/drivers/leds/leds-aw2013.c
+++ b/drivers/leds/leds-aw2013.c
@@ -271,8 +271,6 @@ static int aw2013_probe_dt(struct aw2013 *chip)
if (!count || count > AW2013_MAX_LEDS)
return -EINVAL;

- regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
-
for_each_available_child_of_node_scoped(np, child) {
struct led_init_data init_data = {};
u32 source;
@@ -305,19 +303,29 @@ static int aw2013_probe_dt(struct aw2013 *chip)
led->cdev.brightness_set_blocking = aw2013_brightness_set;
led->cdev.blink_set = aw2013_blink_set;

+ mutex_lock(&chip->mutex);
+ chip->num_leds = ++i;
+ if (chip->enabled) {
+ ret = regmap_update_bits(chip->regmap,
+ AW2013_LCFG(led->num),
+ AW2013_LCFG_IMAX_MASK,
+ led->imax);
+ if (ret) {
+ mutex_unlock(&chip->mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&chip->mutex);
+
ret = devm_led_classdev_register_ext(&chip->client->dev,
&led->cdev, &init_data);
if (ret < 0)
return ret;
-
- i++;
}

if (!count)
return -EINVAL;

- chip->num_leds = i;
-
return 0;
}

@@ -393,25 +401,27 @@ static int aw2013_probe(struct i2c_client *client)
goto error_reg;
}

+ ret = regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
+ if (ret) {
+ dev_err(&client->dev, "Failed to reset chip: %d\n", ret);
+ goto error_reg;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
+ chip->regulators);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to disable regulators: %d\n", ret);
+ goto error;
+ }
+
+ mutex_unlock(&chip->mutex);
+
ret = devm_add_action(&client->dev, aw2013_chip_disable_action, chip);
if (ret)
- goto error_reg;
+ return ret;

- ret = aw2013_probe_dt(chip);
- if (ret < 0)
- goto error_reg;
-
- ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
- chip->regulators);
- if (ret) {
- dev_err(&client->dev,
- "Failed to disable regulators: %d\n", ret);
- goto error;
- }
-
- mutex_unlock(&chip->mutex);
-
- return 0;
+ return aw2013_probe_dt(chip);

error_reg:
regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
--
2.56.0.218.g1ec904b173-goog