diff options
author | Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> | 2017-04-24 06:01:08 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2017-08-21 04:39:12 -0400 |
commit | 08a4d8ec4262c636010eaac99b5e5ea07f579643 (patch) | |
tree | 7a4c6cc2a4949f78eb9c65915df5734ea0878e1b /drivers/pwm/pwm-samsung.c | |
parent | 23aa19a22e7fcb49ebe198517d0a15b46810243f (diff) |
pwm: pwm-samsung: fix suspend/resume support
Fix suspend/resume support:
- add disabled_mask to struct samsung_pwm_chip to track PWM
disabled state information in pwm_samsung_{disable,enable}()
- rename pwm_samsung_config() to __pwm_samsung_config() and
add extra force_period parameter to be used during resume
(to force tin_ns and tcnt recalculation)
- add pwm_samsung_config() wrapper for preserving old behavior
- properly restore PWM configuration in pwm_samsung_resume()
- remove no longer needed pwm_samsung_suspend()
- update Copyrights
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-samsung.c')
-rw-r--r-- | drivers/pwm/pwm-samsung.c | 67 |
1 files changed, 35 insertions, 32 deletions
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 9ea7638228c8..062f2cfc45ec 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright (c) 2008 Simtec Electronics | 3 | * Copyright (c) 2008 Simtec Electronics |
4 | * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> | 4 | * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> |
5 | * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> | 5 | * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> |
6 | * Copyright (c) 2017 Samsung Electronics Co., Ltd. | ||
6 | * | 7 | * |
7 | * PWM driver for Samsung SoCs | 8 | * PWM driver for Samsung SoCs |
8 | * | 9 | * |
@@ -74,6 +75,7 @@ struct samsung_pwm_channel { | |||
74 | * @chip: generic PWM chip | 75 | * @chip: generic PWM chip |
75 | * @variant: local copy of hardware variant data | 76 | * @variant: local copy of hardware variant data |
76 | * @inverter_mask: inverter status for all channels - one bit per channel | 77 | * @inverter_mask: inverter status for all channels - one bit per channel |
78 | * @disabled_mask: disabled status for all channels - one bit per channel | ||
77 | * @base: base address of mapped PWM registers | 79 | * @base: base address of mapped PWM registers |
78 | * @base_clk: base clock used to drive the timers | 80 | * @base_clk: base clock used to drive the timers |
79 | * @tclk0: external clock 0 (can be ERR_PTR if not present) | 81 | * @tclk0: external clock 0 (can be ERR_PTR if not present) |
@@ -83,6 +85,7 @@ struct samsung_pwm_chip { | |||
83 | struct pwm_chip chip; | 85 | struct pwm_chip chip; |
84 | struct samsung_pwm_variant variant; | 86 | struct samsung_pwm_variant variant; |
85 | u8 inverter_mask; | 87 | u8 inverter_mask; |
88 | u8 disabled_mask; | ||
86 | 89 | ||
87 | void __iomem *base; | 90 | void __iomem *base; |
88 | struct clk *base_clk; | 91 | struct clk *base_clk; |
@@ -257,6 +260,8 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
257 | tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); | 260 | tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); |
258 | writel(tcon, our_chip->base + REG_TCON); | 261 | writel(tcon, our_chip->base + REG_TCON); |
259 | 262 | ||
263 | our_chip->disabled_mask &= ~BIT(pwm->hwpwm); | ||
264 | |||
260 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); | 265 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); |
261 | 266 | ||
262 | return 0; | 267 | return 0; |
@@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
275 | tcon &= ~TCON_AUTORELOAD(tcon_chan); | 280 | tcon &= ~TCON_AUTORELOAD(tcon_chan); |
276 | writel(tcon, our_chip->base + REG_TCON); | 281 | writel(tcon, our_chip->base + REG_TCON); |
277 | 282 | ||
283 | our_chip->disabled_mask |= BIT(pwm->hwpwm); | ||
284 | |||
278 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); | 285 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); |
279 | } | 286 | } |
280 | 287 | ||
@@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, | |||
297 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); | 304 | spin_unlock_irqrestore(&samsung_pwm_lock, flags); |
298 | } | 305 | } |
299 | 306 | ||
300 | static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, | 307 | static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, |
301 | int duty_ns, int period_ns) | 308 | int duty_ns, int period_ns, bool force_period) |
302 | { | 309 | { |
303 | struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); | 310 | struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); |
304 | struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); | 311 | struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); |
@@ -319,7 +326,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
319 | ++tcnt; | 326 | ++tcnt; |
320 | 327 | ||
321 | /* Check to see if we are changing the clock rate of the PWM. */ | 328 | /* Check to see if we are changing the clock rate of the PWM. */ |
322 | if (chan->period_ns != period_ns) { | 329 | if (chan->period_ns != period_ns || force_period) { |
323 | unsigned long tin_rate; | 330 | unsigned long tin_rate; |
324 | u32 period; | 331 | u32 period; |
325 | 332 | ||
@@ -378,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
378 | return 0; | 385 | return 0; |
379 | } | 386 | } |
380 | 387 | ||
388 | static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
389 | int duty_ns, int period_ns) | ||
390 | { | ||
391 | return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); | ||
392 | } | ||
393 | |||
381 | static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, | 394 | static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, |
382 | unsigned int channel, bool invert) | 395 | unsigned int channel, bool invert) |
383 | { | 396 | { |
@@ -589,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev) | |||
589 | } | 602 | } |
590 | 603 | ||
591 | #ifdef CONFIG_PM_SLEEP | 604 | #ifdef CONFIG_PM_SLEEP |
592 | static int pwm_samsung_suspend(struct device *dev) | 605 | static int pwm_samsung_resume(struct device *dev) |
593 | { | 606 | { |
594 | struct samsung_pwm_chip *chip = dev_get_drvdata(dev); | 607 | struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); |
608 | struct pwm_chip *chip = &our_chip->chip; | ||
595 | unsigned int i; | 609 | unsigned int i; |
596 | 610 | ||
597 | /* | 611 | for (i = 0; i < SAMSUNG_PWM_NUM; i++) { |
598 | * No one preserves these values during suspend so reset them. | 612 | struct pwm_device *pwm = &chip->pwms[i]; |
599 | * Otherwise driver leaves PWM unconfigured if same values are | ||
600 | * passed to pwm_config() next time. | ||
601 | */ | ||
602 | for (i = 0; i < SAMSUNG_PWM_NUM; ++i) { | ||
603 | struct pwm_device *pwm = &chip->chip.pwms[i]; | ||
604 | struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); | 613 | struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); |
605 | 614 | ||
606 | if (!chan) | 615 | if (!chan) |
607 | continue; | 616 | continue; |
608 | 617 | ||
609 | chan->period_ns = 0; | 618 | if (our_chip->variant.output_mask & BIT(i)) |
610 | chan->duty_ns = 0; | 619 | pwm_samsung_set_invert(our_chip, i, |
611 | } | 620 | our_chip->inverter_mask & BIT(i)); |
612 | |||
613 | return 0; | ||
614 | } | ||
615 | 621 | ||
616 | static int pwm_samsung_resume(struct device *dev) | 622 | if (chan->period_ns) { |
617 | { | 623 | __pwm_samsung_config(chip, pwm, chan->duty_ns, |
618 | struct samsung_pwm_chip *chip = dev_get_drvdata(dev); | 624 | chan->period_ns, true); |
619 | unsigned int chan; | 625 | /* needed to make PWM disable work on Odroid-XU3 */ |
626 | pwm_samsung_manual_update(our_chip, pwm); | ||
627 | } | ||
620 | 628 | ||
621 | /* | 629 | if (our_chip->disabled_mask & BIT(i)) |
622 | * Inverter setting must be preserved across suspend/resume | 630 | pwm_samsung_disable(chip, pwm); |
623 | * as nobody really seems to configure it more than once. | 631 | else |
624 | */ | 632 | pwm_samsung_enable(chip, pwm); |
625 | for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) { | ||
626 | if (chip->variant.output_mask & BIT(chan)) | ||
627 | pwm_samsung_set_invert(chip, chan, | ||
628 | chip->inverter_mask & BIT(chan)); | ||
629 | } | 633 | } |
630 | 634 | ||
631 | return 0; | 635 | return 0; |
632 | } | 636 | } |
633 | #endif | 637 | #endif |
634 | 638 | ||
635 | static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend, | 639 | static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); |
636 | pwm_samsung_resume); | ||
637 | 640 | ||
638 | static struct platform_driver pwm_samsung_driver = { | 641 | static struct platform_driver pwm_samsung_driver = { |
639 | .driver = { | 642 | .driver = { |