[PATCH 08/21] leds: sgm3140: Replace powerdown timer with delayed work

From: Lee Jones

Date: Wed Oct 21 2026 - 08:42:28 EST


sgm3140_powerdown_timer() runs in atomic timer softirq context and calls
regulator_disable(), which acquires sleepable mutexes, as well as
gpiod_set_value() on GPIOs that are otherwise driven via
gpiod_set_value_cansleep().

Replace powerdown_timer with a delayed_work item so that the powerdown
sequence executes in process context and can safely sleep when disabling
the regulator and driving the GPIOs.

Fixes: cef8ec8cbd21 ("leds: add sgm3140 driver")
Closes: https://sashiko.dev/#/bug/linux-8c3788a3-17f9-49dc-89be-c9f5844b0b95
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
drivers/leds/flash/leds-sgm3140.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c
index dc6840357370..478573da1d61 100644
--- a/drivers/leds/flash/leds-sgm3140.c
+++ b/drivers/leds/flash/leds-sgm3140.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>

#include <media/v4l2-flash-led-class.h>

@@ -16,7 +17,7 @@ struct sgm3140 {
struct led_classdev_flash fled_cdev;
struct v4l2_flash *v4l2_flash;

- struct timer_list powerdown_timer;
+ struct delayed_work powerdown_work;

struct gpio_desc *flash_gpio;
struct gpio_desc *enable_gpio;
@@ -52,10 +53,10 @@ static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
}
gpiod_set_value_cansleep(priv->flash_gpio, 1);
gpiod_set_value_cansleep(priv->enable_gpio, 1);
- mod_timer(&priv->powerdown_timer,
- jiffies + usecs_to_jiffies(priv->timeout));
+ schedule_delayed_work(&priv->powerdown_work,
+ usecs_to_jiffies(priv->timeout));
} else {
- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);
gpiod_set_value_cansleep(priv->enable_gpio, 0);
gpiod_set_value_cansleep(priv->flash_gpio, 0);
ret = regulator_disable(priv->vin_regulator);
@@ -75,7 +76,7 @@ static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
{
struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);

- *state = timer_pending(&priv->powerdown_timer);
+ *state = delayed_work_pending(&priv->powerdown_work);

return 0;
}
@@ -117,7 +118,7 @@ static int sgm3140_brightness_set(struct led_classdev *led_cdev,
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 1);
} else {
- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 0);
ret = regulator_disable(priv->vin_regulator);
@@ -133,12 +134,13 @@ static int sgm3140_brightness_set(struct led_classdev *led_cdev,
return 0;
}

-static void sgm3140_powerdown_timer(struct timer_list *t)
+static void sgm3140_powerdown_work(struct work_struct *work)
{
- struct sgm3140 *priv = timer_container_of(priv, t, powerdown_timer);
+ struct sgm3140 *priv = container_of(work, struct sgm3140,
+ powerdown_work.work);

- gpiod_set_value(priv->enable_gpio, 0);
- gpiod_set_value(priv->flash_gpio, 0);
+ gpiod_set_value_cansleep(priv->enable_gpio, 0);
+ gpiod_set_value_cansleep(priv->flash_gpio, 0);
regulator_disable(priv->vin_regulator);

priv->enabled = false;
@@ -235,7 +237,7 @@ static int sgm3140_probe(struct platform_device *pdev)
*/
priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT);

- timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0);
+ INIT_DELAYED_WORK(&priv->powerdown_work, sgm3140_powerdown_work);

fled_cdev = &priv->fled_cdev;
led_cdev = &fled_cdev->led_cdev;
@@ -284,7 +286,7 @@ static void sgm3140_remove(struct platform_device *pdev)
{
struct sgm3140 *priv = platform_get_drvdata(pdev);

- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);

v4l2_flash_release(priv->v4l2_flash);
}
--
2.56.0.218.g1ec904b173-goog