Skip to content

Commit d7541cb

Browse files
xdarklightgregkh
authored andcommitted
pwm: meson: Use the spin-lock only to protect register modifications
[ Upstream commit f173747 ] Holding the spin-lock for all of the code in meson_pwm_apply() can result in a "BUG: scheduling while atomic". This can happen because clk_get_rate() (which is called from meson_pwm_calc()) may sleep. Only hold the spin-lock when modifying registers to solve this. The reason why we need a spin-lock in the driver is because the REG_MISC_AB register is shared between the two channels provided by one PWM controller. The only functions where REG_MISC_AB is modified are meson_pwm_enable() and meson_pwm_disable() so the register reads/writes in there need to be protected by the spin-lock. The original code also used the spin-lock to protect the values in struct meson_pwm_channel. This could be necessary if two consumers can use the same PWM channel. However, PWM core doesn't allow this so we don't need to protect the values in struct meson_pwm_channel with a lock. Fixes: 211ed63 ("pwm: Add support for Meson PWM Controller") Signed-off-by: Martin Blumenstingl <[email protected]> Reviewed-by: Uwe Kleine-König <[email protected]> Reviewed-by: Neil Armstrong <[email protected]> Signed-off-by: Thierry Reding <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 4ba76bf commit d7541cb

File tree

1 file changed

+17
-8
lines changed

1 file changed

+17
-8
lines changed

drivers/pwm/pwm-meson.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ struct meson_pwm {
110110
const struct meson_pwm_data *data;
111111
void __iomem *base;
112112
u8 inverter_mask;
113+
/*
114+
* Protects register (write) access to the REG_MISC_AB register
115+
* that is shared between the two PWMs.
116+
*/
113117
spinlock_t lock;
114118
};
115119

@@ -230,6 +234,7 @@ static void meson_pwm_enable(struct meson_pwm *meson,
230234
{
231235
u32 value, clk_shift, clk_enable, enable;
232236
unsigned int offset;
237+
unsigned long flags;
233238

234239
switch (id) {
235240
case 0:
@@ -250,6 +255,8 @@ static void meson_pwm_enable(struct meson_pwm *meson,
250255
return;
251256
}
252257

258+
spin_lock_irqsave(&meson->lock, flags);
259+
253260
value = readl(meson->base + REG_MISC_AB);
254261
value &= ~(MISC_CLK_DIV_MASK << clk_shift);
255262
value |= channel->pre_div << clk_shift;
@@ -262,11 +269,14 @@ static void meson_pwm_enable(struct meson_pwm *meson,
262269
value = readl(meson->base + REG_MISC_AB);
263270
value |= enable;
264271
writel(value, meson->base + REG_MISC_AB);
272+
273+
spin_unlock_irqrestore(&meson->lock, flags);
265274
}
266275

267276
static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
268277
{
269278
u32 value, enable;
279+
unsigned long flags;
270280

271281
switch (id) {
272282
case 0:
@@ -281,29 +291,30 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
281291
return;
282292
}
283293

294+
spin_lock_irqsave(&meson->lock, flags);
295+
284296
value = readl(meson->base + REG_MISC_AB);
285297
value &= ~enable;
286298
writel(value, meson->base + REG_MISC_AB);
299+
300+
spin_unlock_irqrestore(&meson->lock, flags);
287301
}
288302

289303
static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
290304
struct pwm_state *state)
291305
{
292306
struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
293307
struct meson_pwm *meson = to_meson_pwm(chip);
294-
unsigned long flags;
295308
int err = 0;
296309

297310
if (!state)
298311
return -EINVAL;
299312

300-
spin_lock_irqsave(&meson->lock, flags);
301-
302313
if (!state->enabled) {
303314
meson_pwm_disable(meson, pwm->hwpwm);
304315
channel->state.enabled = false;
305316

306-
goto unlock;
317+
return 0;
307318
}
308319

309320
if (state->period != channel->state.period ||
@@ -324,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
324335
err = meson_pwm_calc(meson, channel, pwm->hwpwm,
325336
state->duty_cycle, state->period);
326337
if (err < 0)
327-
goto unlock;
338+
return err;
328339

329340
channel->state.polarity = state->polarity;
330341
channel->state.period = state->period;
@@ -336,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
336347
channel->state.enabled = true;
337348
}
338349

339-
unlock:
340-
spin_unlock_irqrestore(&meson->lock, flags);
341-
return err;
350+
return 0;
342351
}
343352

344353
static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,

0 commit comments

Comments
 (0)