Skip to content

Commit 53d7848

Browse files
Uwe Kleine-Königgregkh
authored andcommitted
pwm: sifive: Ensure the clk is enabled exactly once per running PWM
[ Upstream commit ace41d7 ] .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]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 5c86cf4 commit 53d7848

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
@@ -229,6 +229,8 @@ static int pwm_sifive_probe(struct platform_device *pdev)
229229
struct pwm_sifive_ddata *ddata;
230230
struct pwm_chip *chip;
231231
int ret;
232+
u32 val;
233+
unsigned int enabled_pwms = 0, enabled_clks = 1;
232234

233235
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
234236
if (!ddata)
@@ -255,6 +257,33 @@ static int pwm_sifive_probe(struct platform_device *pdev)
255257
return ret;
256258
}
257259

260+
val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
261+
if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
262+
unsigned int i;
263+
264+
for (i = 0; i < chip->npwm; ++i) {
265+
val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
266+
if (val > 0)
267+
++enabled_pwms;
268+
}
269+
}
270+
271+
/* The clk should be on once for each running PWM. */
272+
if (enabled_pwms) {
273+
while (enabled_clks < enabled_pwms) {
274+
/* This is not expected to fail as the clk is already on */
275+
ret = clk_enable(ddata->clk);
276+
if (unlikely(ret)) {
277+
dev_err_probe(dev, ret, "Failed to enable clk\n");
278+
goto disable_clk;
279+
}
280+
++enabled_clks;
281+
}
282+
} else {
283+
clk_disable(ddata->clk);
284+
enabled_clks = 0;
285+
}
286+
258287
/* Watch for changes to underlying clock frequency */
259288
ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
260289
ret = clk_notifier_register(ddata->clk, &ddata->notifier);
@@ -277,29 +306,28 @@ static int pwm_sifive_probe(struct platform_device *pdev)
277306
unregister_clk:
278307
clk_notifier_unregister(ddata->clk, &ddata->notifier);
279308
disable_clk:
280-
clk_disable_unprepare(ddata->clk);
309+
while (enabled_clks) {
310+
clk_disable(ddata->clk);
311+
--enabled_clks;
312+
}
313+
clk_unprepare(ddata->clk);
281314

282315
return ret;
283316
}
284317

285318
static int pwm_sifive_remove(struct platform_device *dev)
286319
{
287320
struct pwm_sifive_ddata *ddata = platform_get_drvdata(dev);
288-
bool is_enabled = false;
289321
struct pwm_device *pwm;
290322
int ch;
291323

292324
for (ch = 0; ch < ddata->chip.npwm; ch++) {
293325
pwm = &ddata->chip.pwms[ch];
294-
if (pwm->state.enabled) {
295-
is_enabled = true;
296-
break;
297-
}
326+
if (pwm->state.enabled)
327+
clk_disable(ddata->clk);
298328
}
299-
if (is_enabled)
300-
clk_disable(ddata->clk);
301329

302-
clk_disable_unprepare(ddata->clk);
330+
clk_unprepare(ddata->clk);
303331
pwmchip_remove(&ddata->chip);
304332
clk_notifier_unregister(ddata->clk, &ddata->notifier);
305333

0 commit comments

Comments
 (0)