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: 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: 9e37a53eb051 (pwm: sifive: Add a driver for SiFive SoC PWM)
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Tested-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Uwe Kleine-König and committed by
Thierry Reding
ace41d75 1695b421

+37 -9
+37 -9
drivers/pwm/pwm-sifive.c
··· 216 216 struct pwm_sifive_ddata *ddata; 217 217 struct pwm_chip *chip; 218 218 int ret; 219 + u32 val; 220 + unsigned int enabled_pwms = 0, enabled_clks = 1; 219 221 220 222 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); 221 223 if (!ddata) ··· 244 242 return ret; 245 243 } 246 244 245 + val = readl(ddata->regs + PWM_SIFIVE_PWMCFG); 246 + if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) { 247 + unsigned int i; 248 + 249 + for (i = 0; i < chip->npwm; ++i) { 250 + val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i)); 251 + if (val > 0) 252 + ++enabled_pwms; 253 + } 254 + } 255 + 256 + /* The clk should be on once for each running PWM. */ 257 + if (enabled_pwms) { 258 + while (enabled_clks < enabled_pwms) { 259 + /* This is not expected to fail as the clk is already on */ 260 + ret = clk_enable(ddata->clk); 261 + if (unlikely(ret)) { 262 + dev_err_probe(dev, ret, "Failed to enable clk\n"); 263 + goto disable_clk; 264 + } 265 + ++enabled_clks; 266 + } 267 + } else { 268 + clk_disable(ddata->clk); 269 + enabled_clks = 0; 270 + } 271 + 247 272 /* Watch for changes to underlying clock frequency */ 248 273 ddata->notifier.notifier_call = pwm_sifive_clock_notifier; 249 274 ret = clk_notifier_register(ddata->clk, &ddata->notifier); ··· 293 264 unregister_clk: 294 265 clk_notifier_unregister(ddata->clk, &ddata->notifier); 295 266 disable_clk: 296 - clk_disable_unprepare(ddata->clk); 267 + while (enabled_clks) { 268 + clk_disable(ddata->clk); 269 + --enabled_clks; 270 + } 271 + clk_unprepare(ddata->clk); 297 272 298 273 return ret; 299 274 } ··· 305 272 static int pwm_sifive_remove(struct platform_device *dev) 306 273 { 307 274 struct pwm_sifive_ddata *ddata = platform_get_drvdata(dev); 308 - bool is_enabled = false; 309 275 struct pwm_device *pwm; 310 276 int ch; 311 277 312 278 for (ch = 0; ch < ddata->chip.npwm; ch++) { 313 279 pwm = &ddata->chip.pwms[ch]; 314 - if (pwm->state.enabled) { 315 - is_enabled = true; 316 - break; 317 - } 280 + if (pwm->state.enabled) 281 + clk_disable(ddata->clk); 318 282 } 319 - if (is_enabled) 320 - clk_disable(ddata->clk); 321 283 322 - clk_disable_unprepare(ddata->clk); 284 + clk_unprepare(ddata->clk); 323 285 pwmchip_remove(&ddata->chip); 324 286 clk_notifier_unregister(ddata->clk, &ddata->notifier); 325 287