Skip to content

Commit 68637b6

Browse files
sangyun0110Uwe Kleine-König
authored andcommitted
pwm: atmel-tcb: Cache clock rates and mark chip as atomic
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. Acquire exclusive control over the clock rates with clk_rate_exclusive_get() at probe time and cache the rates in struct atmel_tcb_pwm_chip, then read the cached rates from atmel_tcb_pwm_config(). This keeps the spinlock-based mutual exclusion introduced in commit 37f7707 ("pwm: atmel-tcb: Fix race condition and convert to guards") and removes the sleeping calls from the atomic section. With no sleeping calls left in .apply() and the regmap-mmio bus already running with fast_io=true, also mark the chip as atomic so consumers can use pwm_apply_atomic() from atomic context. Fixes: 37f7707 ("pwm: atmel-tcb: Fix race condition and convert to guards") Signed-off-by: Sangyun Kim <[email protected]> Link: https://patch.msgid.link/[email protected] [ukleinek: Ensure .clk is enabled before calling clk_get_rate on it.] Signed-off-by: Uwe Kleine-König <[email protected]>
1 parent 5d087c4 commit 68637b6

1 file changed

Lines changed: 34 additions & 4 deletions

File tree

drivers/pwm/pwm-atmel-tcb.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ struct atmel_tcb_pwm_chip {
5050
spinlock_t lock;
5151
u8 channel;
5252
u8 width;
53+
unsigned long rate;
54+
unsigned long slow_rate;
5355
struct regmap *regmap;
5456
struct clk *clk;
5557
struct clk *gclk;
@@ -266,7 +268,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
266268
int slowclk = 0;
267269
unsigned period;
268270
unsigned duty;
269-
unsigned rate = clk_get_rate(tcbpwmc->clk);
271+
unsigned long rate = tcbpwmc->rate;
270272
unsigned long long min;
271273
unsigned long long max;
272274

@@ -294,7 +296,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
294296
*/
295297
if (i == ARRAY_SIZE(atmel_tcb_divisors)) {
296298
i = slowclk;
297-
rate = clk_get_rate(tcbpwmc->slow_clk);
299+
rate = tcbpwmc->slow_rate;
298300
min = div_u64(NSEC_PER_SEC, rate);
299301
max = min << tcbpwmc->width;
300302

@@ -431,24 +433,49 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
431433
}
432434

433435
chip->ops = &atmel_tcb_pwm_ops;
436+
chip->atomic = true;
434437
tcbpwmc->channel = channel;
435438
tcbpwmc->width = config->counter_width;
436439

437-
err = clk_prepare_enable(tcbpwmc->slow_clk);
440+
err = clk_prepare_enable(tcbpwmc->clk);
438441
if (err)
439442
goto err_gclk;
440443

444+
err = clk_prepare_enable(tcbpwmc->slow_clk);
445+
if (err)
446+
goto err_disable_clk;;
447+
448+
err = clk_rate_exclusive_get(tcbpwmc->clk);
449+
if (err)
450+
goto err_disable_slow_clk;
451+
452+
err = clk_rate_exclusive_get(tcbpwmc->slow_clk);
453+
if (err)
454+
goto err_clk_unlock;
455+
456+
tcbpwmc->rate = clk_get_rate(tcbpwmc->clk);
457+
tcbpwmc->slow_rate = clk_get_rate(tcbpwmc->slow_clk);
458+
441459
spin_lock_init(&tcbpwmc->lock);
442460

443461
err = pwmchip_add(chip);
444462
if (err < 0)
445-
goto err_disable_clk;
463+
goto err_slow_clk_unlock;
446464

447465
platform_set_drvdata(pdev, chip);
448466

449467
return 0;
450468

469+
err_slow_clk_unlock:
470+
clk_rate_exclusive_put(tcbpwmc->slow_clk);
471+
472+
err_clk_unlock:
473+
clk_rate_exclusive_put(tcbpwmc->clk);
474+
451475
err_disable_clk:
476+
clk_disable_unprepare(tcbpwmc->clk);
477+
478+
err_disable_slow_clk:
452479
clk_disable_unprepare(tcbpwmc->slow_clk);
453480

454481
err_gclk:
@@ -470,6 +497,9 @@ static void atmel_tcb_pwm_remove(struct platform_device *pdev)
470497

471498
pwmchip_remove(chip);
472499

500+
clk_rate_exclusive_put(tcbpwmc->slow_clk);
501+
clk_rate_exclusive_put(tcbpwmc->clk);
502+
clk_disable_unprepare(tcbpwmc->clk);
473503
clk_disable_unprepare(tcbpwmc->slow_clk);
474504
clk_put(tcbpwmc->gclk);
475505
clk_put(tcbpwmc->clk);

0 commit comments

Comments
 (0)