diff options
Diffstat (limited to 'drivers/pwm/pwm-tiehrpwm.c')
-rw-r--r-- | drivers/pwm/pwm-tiehrpwm.c | 93 |
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 5a1399580533..8b4c86fa99c8 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c | |||
@@ -113,6 +113,17 @@ | |||
113 | 113 | ||
114 | #define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ | 114 | #define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ |
115 | 115 | ||
116 | struct ehrpwm_context { | ||
117 | u16 tbctl; | ||
118 | u16 tbprd; | ||
119 | u16 cmpa; | ||
120 | u16 cmpb; | ||
121 | u16 aqctla; | ||
122 | u16 aqctlb; | ||
123 | u16 aqsfrc; | ||
124 | u16 aqcsfrc; | ||
125 | }; | ||
126 | |||
116 | struct ehrpwm_pwm_chip { | 127 | struct ehrpwm_pwm_chip { |
117 | struct pwm_chip chip; | 128 | struct pwm_chip chip; |
118 | unsigned int clk_rate; | 129 | unsigned int clk_rate; |
@@ -120,6 +131,7 @@ struct ehrpwm_pwm_chip { | |||
120 | unsigned long period_cycles[NUM_PWM_CHANNEL]; | 131 | unsigned long period_cycles[NUM_PWM_CHANNEL]; |
121 | enum pwm_polarity polarity[NUM_PWM_CHANNEL]; | 132 | enum pwm_polarity polarity[NUM_PWM_CHANNEL]; |
122 | struct clk *tbclk; | 133 | struct clk *tbclk; |
134 | struct ehrpwm_context ctx; | ||
123 | }; | 135 | }; |
124 | 136 | ||
125 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) | 137 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) |
@@ -127,6 +139,11 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) | |||
127 | return container_of(chip, struct ehrpwm_pwm_chip, chip); | 139 | return container_of(chip, struct ehrpwm_pwm_chip, chip); |
128 | } | 140 | } |
129 | 141 | ||
142 | static u16 ehrpwm_read(void *base, int offset) | ||
143 | { | ||
144 | return readw(base + offset); | ||
145 | } | ||
146 | |||
130 | static void ehrpwm_write(void *base, int offset, unsigned int val) | 147 | static void ehrpwm_write(void *base, int offset, unsigned int val) |
131 | { | 148 | { |
132 | writew(val & 0xFFFF, base + offset); | 149 | writew(val & 0xFFFF, base + offset); |
@@ -318,6 +335,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
318 | { | 335 | { |
319 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | 336 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); |
320 | unsigned short aqcsfrc_val, aqcsfrc_mask; | 337 | unsigned short aqcsfrc_val, aqcsfrc_mask; |
338 | int ret; | ||
321 | 339 | ||
322 | /* Leave clock enabled on enabling PWM */ | 340 | /* Leave clock enabled on enabling PWM */ |
323 | pm_runtime_get_sync(chip->dev); | 341 | pm_runtime_get_sync(chip->dev); |
@@ -341,7 +359,12 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
341 | configure_polarity(pc, pwm->hwpwm); | 359 | configure_polarity(pc, pwm->hwpwm); |
342 | 360 | ||
343 | /* Enable TBCLK before enabling PWM device */ | 361 | /* Enable TBCLK before enabling PWM device */ |
344 | clk_enable(pc->tbclk); | 362 | ret = clk_prepare_enable(pc->tbclk); |
363 | if (ret) { | ||
364 | pr_err("Failed to enable TBCLK for %s\n", | ||
365 | dev_name(pc->chip.dev)); | ||
366 | return ret; | ||
367 | } | ||
345 | 368 | ||
346 | /* Enable time counter for free_run */ | 369 | /* Enable time counter for free_run */ |
347 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); | 370 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); |
@@ -372,7 +395,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
372 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); | 395 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); |
373 | 396 | ||
374 | /* Disabling TBCLK on PWM disable */ | 397 | /* Disabling TBCLK on PWM disable */ |
375 | clk_disable(pc->tbclk); | 398 | clk_disable_unprepare(pc->tbclk); |
376 | 399 | ||
377 | /* Stop Time base counter */ | 400 | /* Stop Time base counter */ |
378 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); | 401 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); |
@@ -510,11 +533,77 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) | |||
510 | return pwmchip_remove(&pc->chip); | 533 | return pwmchip_remove(&pc->chip); |
511 | } | 534 | } |
512 | 535 | ||
536 | void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) | ||
537 | { | ||
538 | pm_runtime_get_sync(pc->chip.dev); | ||
539 | pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); | ||
540 | pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); | ||
541 | pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); | ||
542 | pc->ctx.cmpb = ehrpwm_read(pc->mmio_base, CMPB); | ||
543 | pc->ctx.aqctla = ehrpwm_read(pc->mmio_base, AQCTLA); | ||
544 | pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB); | ||
545 | pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); | ||
546 | pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); | ||
547 | pm_runtime_put_sync(pc->chip.dev); | ||
548 | } | ||
549 | |||
550 | void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) | ||
551 | { | ||
552 | ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd); | ||
553 | ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa); | ||
554 | ehrpwm_write(pc->mmio_base, CMPB, pc->ctx.cmpb); | ||
555 | ehrpwm_write(pc->mmio_base, AQCTLA, pc->ctx.aqctla); | ||
556 | ehrpwm_write(pc->mmio_base, AQCTLB, pc->ctx.aqctlb); | ||
557 | ehrpwm_write(pc->mmio_base, AQSFRC, pc->ctx.aqsfrc); | ||
558 | ehrpwm_write(pc->mmio_base, AQCSFRC, pc->ctx.aqcsfrc); | ||
559 | ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl); | ||
560 | } | ||
561 | |||
562 | static int ehrpwm_pwm_suspend(struct device *dev) | ||
563 | { | ||
564 | struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); | ||
565 | int i; | ||
566 | |||
567 | ehrpwm_pwm_save_context(pc); | ||
568 | for (i = 0; i < pc->chip.npwm; i++) { | ||
569 | struct pwm_device *pwm = &pc->chip.pwms[i]; | ||
570 | |||
571 | if (!test_bit(PWMF_ENABLED, &pwm->flags)) | ||
572 | continue; | ||
573 | |||
574 | /* Disable explicitly if PWM is running */ | ||
575 | pm_runtime_put_sync(dev); | ||
576 | } | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static int ehrpwm_pwm_resume(struct device *dev) | ||
581 | { | ||
582 | struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); | ||
583 | int i; | ||
584 | |||
585 | for (i = 0; i < pc->chip.npwm; i++) { | ||
586 | struct pwm_device *pwm = &pc->chip.pwms[i]; | ||
587 | |||
588 | if (!test_bit(PWMF_ENABLED, &pwm->flags)) | ||
589 | continue; | ||
590 | |||
591 | /* Enable explicitly if PWM was running */ | ||
592 | pm_runtime_get_sync(dev); | ||
593 | } | ||
594 | ehrpwm_pwm_restore_context(pc); | ||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, | ||
599 | ehrpwm_pwm_resume); | ||
600 | |||
513 | static struct platform_driver ehrpwm_pwm_driver = { | 601 | static struct platform_driver ehrpwm_pwm_driver = { |
514 | .driver = { | 602 | .driver = { |
515 | .name = "ehrpwm", | 603 | .name = "ehrpwm", |
516 | .owner = THIS_MODULE, | 604 | .owner = THIS_MODULE, |
517 | .of_match_table = ehrpwm_of_match, | 605 | .of_match_table = ehrpwm_of_match, |
606 | .pm = &ehrpwm_pwm_pm_ops, | ||
518 | }, | 607 | }, |
519 | .probe = ehrpwm_pwm_probe, | 608 | .probe = ehrpwm_pwm_probe, |
520 | .remove = ehrpwm_pwm_remove, | 609 | .remove = ehrpwm_pwm_remove, |