[PATCH] pwm: atmel-tcb: Fix sleeping function called from invalid context

Sangyun Kim
2026-04-15T09:34:48Z

atmel_tcb_pwm_apply() holds tcbpwmc->lock as a spinlock via
guard(spinlock)() and then calls atmel_tcb_pwm_config(), which calls
clk_get_rate() twice. clk_get_rate() acquires clk_prepare_lock (a
mutex), so this is a sleep-in-atomic-context violation.

On CONFIG_DEBUG_ATOMIC_SLEEP kernels every pwm_apply_state() that
enables or reconfigures the PWM triggers a "BUG: sleeping function
called from invalid context" warning.

All callers of tcbpwmc->lock (the .request and .apply callbacks) run in
process context and only need mutual exclusion against each other, so
use a mutex instead of a spinlock and allow the sleeping calls inside
atmel_tcb_pwm_config().

Fixes: 37f7707077f5 ("pwm: atmel-tcb: Fix race condition and convert to guards")
Signed-off-by: Sangyun Kim <sangyun.kim@snu.ac.kr>
---
drivers/pwm/pwm-atmel-tcb.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index f9ff78ba122d..6405e82d9f10 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -17,6 +17,7 @@
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/of.h>
@@ -47,7 +48,7 @@ struct atmel_tcb_channel {
};

struct atmel_tcb_pwm_chip {
- spinlock_t lock;
+ struct mutex lock;
u8 channel;
u8 width;
struct regmap *regmap;
@@ -81,7 +82,7 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,
tcbpwm->period = 0;
tcbpwm->div = 0;

- guard(spinlock)(&tcbpwmc->lock);
+ guard(mutex)(&tcbpwmc->lock);

regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
/*
@@ -335,7 +336,7 @@ static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_cycle, period;
int ret;

- guard(spinlock)(&tcbpwmc->lock);
+ guard(mutex)(&tcbpwmc->lock);

if (!state->enabled) {
atmel_tcb_pwm_disable(chip, pwm, state->polarity);
@@ -438,7 +439,7 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
if (err)
goto err_gclk;

- spin_lock_init(&tcbpwmc->lock);
+ mutex_init(&tcbpwmc->lock);

err = pwmchip_add(chip);
if (err < 0)
--
2.34.1