diff options
author | Ed Blake <ed.blake@sondrel.com> | 2017-10-02 05:51:48 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2017-11-15 04:38:37 -0500 |
commit | e690ae526216b913c61977193eb7e4b803511e75 (patch) | |
tree | adcd6ab6102c737a2593155233558c54798574cc /drivers/pwm/pwm-img.c | |
parent | a18afce522e8c469132e740f840f2b95b2008987 (diff) |
pwm: img: Add runtime PM
Add runtime PM to disable the clocks when the h/w is not in use.
Signed-off-by: Ed Blake <ed.blake@sondrel.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-img.c')
-rw-r--r-- | drivers/pwm/pwm-img.c | 132 |
1 files changed, 95 insertions, 37 deletions
diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index b54daf2917bf..815f5333bb8f 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/of.h> | 18 | #include <linux/of.h> |
19 | #include <linux/of_device.h> | 19 | #include <linux/of_device.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/pm_runtime.h> | ||
21 | #include <linux/pwm.h> | 22 | #include <linux/pwm.h> |
22 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
23 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
@@ -39,6 +40,8 @@ | |||
39 | #define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1 | 40 | #define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1 |
40 | #define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4) | 41 | #define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4) |
41 | 42 | ||
43 | #define IMG_PWM_PM_TIMEOUT 1000 /* ms */ | ||
44 | |||
42 | /* | 45 | /* |
43 | * PWM period is specified with a timebase register, | 46 | * PWM period is specified with a timebase register, |
44 | * in number of step periods. The PWM duty cycle is also | 47 | * in number of step periods. The PWM duty cycle is also |
@@ -96,6 +99,7 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
96 | unsigned long mul, output_clk_hz, input_clk_hz; | 99 | unsigned long mul, output_clk_hz, input_clk_hz; |
97 | struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); | 100 | struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); |
98 | unsigned int max_timebase = pwm_chip->data->max_timebase; | 101 | unsigned int max_timebase = pwm_chip->data->max_timebase; |
102 | int ret; | ||
99 | 103 | ||
100 | if (period_ns < pwm_chip->min_period_ns || | 104 | if (period_ns < pwm_chip->min_period_ns || |
101 | period_ns > pwm_chip->max_period_ns) { | 105 | period_ns > pwm_chip->max_period_ns) { |
@@ -127,6 +131,10 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
127 | 131 | ||
128 | duty = DIV_ROUND_UP(timebase * duty_ns, period_ns); | 132 | duty = DIV_ROUND_UP(timebase * duty_ns, period_ns); |
129 | 133 | ||
134 | ret = pm_runtime_get_sync(chip->dev); | ||
135 | if (ret < 0) | ||
136 | return ret; | ||
137 | |||
130 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); | 138 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); |
131 | val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm)); | 139 | val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm)); |
132 | val |= (div & PWM_CTRL_CFG_DIV_MASK) << | 140 | val |= (div & PWM_CTRL_CFG_DIV_MASK) << |
@@ -137,6 +145,9 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
137 | (timebase << PWM_CH_CFG_TMBASE_SHIFT); | 145 | (timebase << PWM_CH_CFG_TMBASE_SHIFT); |
138 | img_pwm_writel(pwm_chip, PWM_CH_CFG(pwm->hwpwm), val); | 146 | img_pwm_writel(pwm_chip, PWM_CH_CFG(pwm->hwpwm), val); |
139 | 147 | ||
148 | pm_runtime_mark_last_busy(chip->dev); | ||
149 | pm_runtime_put_autosuspend(chip->dev); | ||
150 | |||
140 | return 0; | 151 | return 0; |
141 | } | 152 | } |
142 | 153 | ||
@@ -144,6 +155,11 @@ static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
144 | { | 155 | { |
145 | u32 val; | 156 | u32 val; |
146 | struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); | 157 | struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); |
158 | int ret; | ||
159 | |||
160 | ret = pm_runtime_get_sync(chip->dev); | ||
161 | if (ret < 0) | ||
162 | return ret; | ||
147 | 163 | ||
148 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); | 164 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); |
149 | val |= BIT(pwm->hwpwm); | 165 | val |= BIT(pwm->hwpwm); |
@@ -164,6 +180,9 @@ static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
164 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); | 180 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); |
165 | val &= ~BIT(pwm->hwpwm); | 181 | val &= ~BIT(pwm->hwpwm); |
166 | img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); | 182 | img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); |
183 | |||
184 | pm_runtime_mark_last_busy(chip->dev); | ||
185 | pm_runtime_put_autosuspend(chip->dev); | ||
167 | } | 186 | } |
168 | 187 | ||
169 | static const struct pwm_ops img_pwm_ops = { | 188 | static const struct pwm_ops img_pwm_ops = { |
@@ -186,6 +205,37 @@ static const struct of_device_id img_pwm_of_match[] = { | |||
186 | }; | 205 | }; |
187 | MODULE_DEVICE_TABLE(of, img_pwm_of_match); | 206 | MODULE_DEVICE_TABLE(of, img_pwm_of_match); |
188 | 207 | ||
208 | static int img_pwm_runtime_suspend(struct device *dev) | ||
209 | { | ||
210 | struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); | ||
211 | |||
212 | clk_disable_unprepare(pwm_chip->pwm_clk); | ||
213 | clk_disable_unprepare(pwm_chip->sys_clk); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int img_pwm_runtime_resume(struct device *dev) | ||
219 | { | ||
220 | struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); | ||
221 | int ret; | ||
222 | |||
223 | ret = clk_prepare_enable(pwm_chip->sys_clk); | ||
224 | if (ret < 0) { | ||
225 | dev_err(dev, "could not prepare or enable sys clock\n"); | ||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | ret = clk_prepare_enable(pwm_chip->pwm_clk); | ||
230 | if (ret < 0) { | ||
231 | dev_err(dev, "could not prepare or enable pwm clock\n"); | ||
232 | clk_disable_unprepare(pwm_chip->sys_clk); | ||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
189 | static int img_pwm_probe(struct platform_device *pdev) | 239 | static int img_pwm_probe(struct platform_device *pdev) |
190 | { | 240 | { |
191 | int ret; | 241 | int ret; |
@@ -228,23 +278,20 @@ static int img_pwm_probe(struct platform_device *pdev) | |||
228 | return PTR_ERR(pwm->pwm_clk); | 278 | return PTR_ERR(pwm->pwm_clk); |
229 | } | 279 | } |
230 | 280 | ||
231 | ret = clk_prepare_enable(pwm->sys_clk); | 281 | pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_PWM_PM_TIMEOUT); |
232 | if (ret < 0) { | 282 | pm_runtime_use_autosuspend(&pdev->dev); |
233 | dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); | 283 | pm_runtime_enable(&pdev->dev); |
234 | return ret; | 284 | if (!pm_runtime_enabled(&pdev->dev)) { |
235 | } | 285 | ret = img_pwm_runtime_resume(&pdev->dev); |
236 | 286 | if (ret) | |
237 | ret = clk_prepare_enable(pwm->pwm_clk); | 287 | goto err_pm_disable; |
238 | if (ret < 0) { | ||
239 | dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); | ||
240 | goto disable_sysclk; | ||
241 | } | 288 | } |
242 | 289 | ||
243 | clk_rate = clk_get_rate(pwm->pwm_clk); | 290 | clk_rate = clk_get_rate(pwm->pwm_clk); |
244 | if (!clk_rate) { | 291 | if (!clk_rate) { |
245 | dev_err(&pdev->dev, "pwm clock has no frequency\n"); | 292 | dev_err(&pdev->dev, "pwm clock has no frequency\n"); |
246 | ret = -EINVAL; | 293 | ret = -EINVAL; |
247 | goto disable_pwmclk; | 294 | goto err_suspend; |
248 | } | 295 | } |
249 | 296 | ||
250 | /* The maximum input clock divider is 512 */ | 297 | /* The maximum input clock divider is 512 */ |
@@ -264,16 +311,18 @@ static int img_pwm_probe(struct platform_device *pdev) | |||
264 | ret = pwmchip_add(&pwm->chip); | 311 | ret = pwmchip_add(&pwm->chip); |
265 | if (ret < 0) { | 312 | if (ret < 0) { |
266 | dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); | 313 | dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); |
267 | goto disable_pwmclk; | 314 | goto err_suspend; |
268 | } | 315 | } |
269 | 316 | ||
270 | platform_set_drvdata(pdev, pwm); | 317 | platform_set_drvdata(pdev, pwm); |
271 | return 0; | 318 | return 0; |
272 | 319 | ||
273 | disable_pwmclk: | 320 | err_suspend: |
274 | clk_disable_unprepare(pwm->pwm_clk); | 321 | if (!pm_runtime_enabled(&pdev->dev)) |
275 | disable_sysclk: | 322 | img_pwm_runtime_suspend(&pdev->dev); |
276 | clk_disable_unprepare(pwm->sys_clk); | 323 | err_pm_disable: |
324 | pm_runtime_disable(&pdev->dev); | ||
325 | pm_runtime_dont_use_autosuspend(&pdev->dev); | ||
277 | return ret; | 326 | return ret; |
278 | } | 327 | } |
279 | 328 | ||
@@ -282,6 +331,11 @@ static int img_pwm_remove(struct platform_device *pdev) | |||
282 | struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); | 331 | struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); |
283 | u32 val; | 332 | u32 val; |
284 | unsigned int i; | 333 | unsigned int i; |
334 | int ret; | ||
335 | |||
336 | ret = pm_runtime_get_sync(&pdev->dev); | ||
337 | if (ret < 0) | ||
338 | return ret; | ||
285 | 339 | ||
286 | for (i = 0; i < pwm_chip->chip.npwm; i++) { | 340 | for (i = 0; i < pwm_chip->chip.npwm; i++) { |
287 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); | 341 | val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); |
@@ -289,8 +343,10 @@ static int img_pwm_remove(struct platform_device *pdev) | |||
289 | img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); | 343 | img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); |
290 | } | 344 | } |
291 | 345 | ||
292 | clk_disable_unprepare(pwm_chip->pwm_clk); | 346 | pm_runtime_put(&pdev->dev); |
293 | clk_disable_unprepare(pwm_chip->sys_clk); | 347 | pm_runtime_disable(&pdev->dev); |
348 | if (!pm_runtime_status_suspended(&pdev->dev)) | ||
349 | img_pwm_runtime_suspend(&pdev->dev); | ||
294 | 350 | ||
295 | return pwmchip_remove(&pwm_chip->chip); | 351 | return pwmchip_remove(&pwm_chip->chip); |
296 | } | 352 | } |
@@ -298,9 +354,14 @@ static int img_pwm_remove(struct platform_device *pdev) | |||
298 | #ifdef CONFIG_PM_SLEEP | 354 | #ifdef CONFIG_PM_SLEEP |
299 | static int img_pwm_suspend(struct device *dev) | 355 | static int img_pwm_suspend(struct device *dev) |
300 | { | 356 | { |
301 | struct platform_device *pdev = to_platform_device(dev); | 357 | struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); |
302 | struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); | 358 | int i, ret; |
303 | int i; | 359 | |
360 | if (pm_runtime_status_suspended(dev)) { | ||
361 | ret = img_pwm_runtime_resume(dev); | ||
362 | if (ret) | ||
363 | return ret; | ||
364 | } | ||
304 | 365 | ||
305 | for (i = 0; i < pwm_chip->chip.npwm; i++) | 366 | for (i = 0; i < pwm_chip->chip.npwm; i++) |
306 | pwm_chip->suspend_ch_cfg[i] = img_pwm_readl(pwm_chip, | 367 | pwm_chip->suspend_ch_cfg[i] = img_pwm_readl(pwm_chip, |
@@ -308,31 +369,20 @@ static int img_pwm_suspend(struct device *dev) | |||
308 | 369 | ||
309 | pwm_chip->suspend_ctrl_cfg = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); | 370 | pwm_chip->suspend_ctrl_cfg = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); |
310 | 371 | ||
311 | clk_disable_unprepare(pwm_chip->pwm_clk); | 372 | img_pwm_runtime_suspend(dev); |
312 | clk_disable_unprepare(pwm_chip->sys_clk); | ||
313 | 373 | ||
314 | return 0; | 374 | return 0; |
315 | } | 375 | } |
316 | 376 | ||
317 | static int img_pwm_resume(struct device *dev) | 377 | static int img_pwm_resume(struct device *dev) |
318 | { | 378 | { |
319 | struct platform_device *pdev = to_platform_device(dev); | 379 | struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); |
320 | struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); | ||
321 | int ret; | 380 | int ret; |
322 | int i; | 381 | int i; |
323 | 382 | ||
324 | ret = clk_prepare_enable(pwm_chip->sys_clk); | 383 | ret = img_pwm_runtime_resume(dev); |
325 | if (ret < 0) { | 384 | if (ret) |
326 | dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); | ||
327 | return ret; | 385 | return ret; |
328 | } | ||
329 | |||
330 | ret = clk_prepare_enable(pwm_chip->pwm_clk); | ||
331 | if (ret < 0) { | ||
332 | dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); | ||
333 | clk_disable_unprepare(pwm_chip->sys_clk); | ||
334 | return ret; | ||
335 | } | ||
336 | 386 | ||
337 | for (i = 0; i < pwm_chip->chip.npwm; i++) | 387 | for (i = 0; i < pwm_chip->chip.npwm; i++) |
338 | img_pwm_writel(pwm_chip, PWM_CH_CFG(i), | 388 | img_pwm_writel(pwm_chip, PWM_CH_CFG(i), |
@@ -348,11 +398,19 @@ static int img_pwm_resume(struct device *dev) | |||
348 | PERIP_PWM_PDM_CONTROL_CH_SHIFT(i), | 398 | PERIP_PWM_PDM_CONTROL_CH_SHIFT(i), |
349 | 0); | 399 | 0); |
350 | 400 | ||
401 | if (pm_runtime_status_suspended(dev)) | ||
402 | img_pwm_runtime_suspend(dev); | ||
403 | |||
351 | return 0; | 404 | return 0; |
352 | } | 405 | } |
353 | #endif /* CONFIG_PM */ | 406 | #endif /* CONFIG_PM */ |
354 | 407 | ||
355 | SIMPLE_DEV_PM_OPS(img_pwm_pm_ops, img_pwm_suspend, img_pwm_resume); | 408 | static const struct dev_pm_ops img_pwm_pm_ops = { |
409 | SET_RUNTIME_PM_OPS(img_pwm_runtime_suspend, | ||
410 | img_pwm_runtime_resume, | ||
411 | NULL) | ||
412 | SET_SYSTEM_SLEEP_PM_OPS(img_pwm_suspend, img_pwm_resume) | ||
413 | }; | ||
356 | 414 | ||
357 | static struct platform_driver img_pwm_driver = { | 415 | static struct platform_driver img_pwm_driver = { |
358 | .driver = { | 416 | .driver = { |