Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

pwm: atmel-tcb: Fix race condition and convert to guards

The hardware only supports a single period length for both PWM outputs. So
atmel_tcb_pwm_config() checks the configuration of the other output if it's
compatible with the currently requested setting. The register values are
then actually updated in atmel_tcb_pwm_enable(). To make this race free
the lock must be held during the whole process, so grab the lock in
.apply() instead of individually in atmel_tcb_pwm_disable() and
atmel_tcb_pwm_enable() which then also covers atmel_tcb_pwm_config().

To simplify handling, use the guard helper to let the compiler care for
unlocking. Otherwise unlocking would be more difficult as there is more
than one exit path in atmel_tcb_pwm_apply().

Fixes: 9421bade0765 ("pwm: atmel: add Timer Counter Block PWM driver")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Link: https://lore.kernel.org/r/20240709101806.52394-3-u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Uwe Kleine-König and committed by
Uwe Kleine-König
37f77070 32207e9d

+5 -7
+5 -7
drivers/pwm/pwm-atmel-tcb.c
··· 81 81 tcbpwm->period = 0; 82 82 tcbpwm->div = 0; 83 83 84 - spin_lock(&tcbpwmc->lock); 84 + guard(spinlock)(&tcbpwmc->lock); 85 + 85 86 regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); 86 87 /* 87 88 * Get init config from Timer Counter registers if ··· 108 107 109 108 cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; 110 109 regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); 111 - spin_unlock(&tcbpwmc->lock); 112 110 113 111 return 0; 114 112 } ··· 137 137 if (tcbpwm->duty == 0) 138 138 polarity = !polarity; 139 139 140 - spin_lock(&tcbpwmc->lock); 141 140 regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); 142 141 143 142 /* flush old setting and set the new one */ ··· 171 172 ATMEL_TC_SWTRG); 172 173 tcbpwmc->bkup.enabled = 0; 173 174 } 174 - 175 - spin_unlock(&tcbpwmc->lock); 176 175 } 177 176 178 177 static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, ··· 191 194 if (tcbpwm->duty == 0) 192 195 polarity = !polarity; 193 196 194 - spin_lock(&tcbpwmc->lock); 195 197 regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); 196 198 197 199 /* flush old setting and set the new one */ ··· 252 256 regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR), 253 257 ATMEL_TC_SWTRG | ATMEL_TC_CLKEN); 254 258 tcbpwmc->bkup.enabled = 1; 255 - spin_unlock(&tcbpwmc->lock); 256 259 return 0; 257 260 } 258 261 ··· 336 341 static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 337 342 const struct pwm_state *state) 338 343 { 344 + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); 339 345 int duty_cycle, period; 340 346 int ret; 347 + 348 + guard(spinlock)(&tcbpwmc->lock); 341 349 342 350 if (!state->enabled) { 343 351 atmel_tcb_pwm_disable(chip, pwm, state->polarity);