Skip to content

Commit ace41d7

Browse files
Uwe Kleine-Königthierryreding
authored andcommitted
pwm: sifive: Ensure the clk is enabled exactly once per running PWM
.apply() assumes the clk to be for a given PWM iff the PWM is enabled. So make sure this is the case when .probe() completes. And in .remove() disable the according number of times. This fixes a clk enable/disable imbalance, if some PWMs are already running at probe time. Fixes: 9e37a53 (pwm: sifive: Add a driver for SiFive SoC PWM) Signed-off-by: Uwe Kleine-König <[email protected]> Tested-by: Emil Renner Berthing <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 1695b42 commit ace41d7

File tree

1 file changed

+37
-9
lines changed

1 file changed

+37
-9
lines changed

drivers/pwm/pwm-sifive.c

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ static int pwm_sifive_probe(struct platform_device *pdev)
216216
struct pwm_sifive_ddata *ddata;
217217
struct pwm_chip *chip;
218218
int ret;
219+
u32 val;
220+
unsigned int enabled_pwms = 0, enabled_clks = 1;
219221

220222
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
221223
if (!ddata)
@@ -242,6 +244,33 @@ static int pwm_sifive_probe(struct platform_device *pdev)
242244
return ret;
243245
}
244246

247+
val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
248+
if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
249+
unsigned int i;
250+
251+
for (i = 0; i < chip->npwm; ++i) {
252+
val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
253+
if (val > 0)
254+
++enabled_pwms;
255+
}
256+
}
257+
258+
/* The clk should be on once for each running PWM. */
259+
if (enabled_pwms) {
260+
while (enabled_clks < enabled_pwms) {
261+
/* This is not expected to fail as the clk is already on */
262+
ret = clk_enable(ddata->clk);
263+
if (unlikely(ret)) {
264+
dev_err_probe(dev, ret, "Failed to enable clk\n");
265+
goto disable_clk;
266+
}
267+
++enabled_clks;
268+
}
269+
} else {
270+
clk_disable(ddata->clk);
271+
enabled_clks = 0;
272+
}
273+
245274
/* Watch for changes to underlying clock frequency */
246275
ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
247276
ret = clk_notifier_register(ddata->clk, &ddata->notifier);
@@ -264,29 +293,28 @@ static int pwm_sifive_probe(struct platform_device *pdev)
264293
unregister_clk:
265294
clk_notifier_unregister(ddata->clk, &ddata->notifier);
266295
disable_clk:
267-
clk_disable_unprepare(ddata->clk);
296+
while (enabled_clks) {
297+
clk_disable(ddata->clk);
298+
--enabled_clks;
299+
}
300+
clk_unprepare(ddata->clk);
268301

269302
return ret;
270303
}
271304

272305
static int pwm_sifive_remove(struct platform_device *dev)
273306
{
274307
struct pwm_sifive_ddata *ddata = platform_get_drvdata(dev);
275-
bool is_enabled = false;
276308
struct pwm_device *pwm;
277309
int ch;
278310

279311
for (ch = 0; ch < ddata->chip.npwm; ch++) {
280312
pwm = &ddata->chip.pwms[ch];
281-
if (pwm->state.enabled) {
282-
is_enabled = true;
283-
break;
284-
}
313+
if (pwm->state.enabled)
314+
clk_disable(ddata->clk);
285315
}
286-
if (is_enabled)
287-
clk_disable(ddata->clk);
288316

289-
clk_disable_unprepare(ddata->clk);
317+
clk_unprepare(ddata->clk);
290318
pwmchip_remove(&ddata->chip);
291319
clk_notifier_unregister(ddata->clk, &ddata->notifier);
292320

0 commit comments

Comments
 (0)