Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Bartlomiej Zolnierkiewicz and committed by
Thierry Reding
08a4d8ec 23aa19a2

+35 -32
+35 -32
drivers/pwm/pwm-samsung.c
··· 3 3 * Copyright (c) 2008 Simtec Electronics 4 4 * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> 5 5 * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> 6 + * Copyright (c) 2017 Samsung Electronics Co., Ltd. 6 7 * 7 8 * PWM driver for Samsung SoCs 8 9 * ··· 75 74 * @chip: generic PWM chip 76 75 * @variant: local copy of hardware variant data 77 76 * @inverter_mask: inverter status for all channels - one bit per channel 77 + * @disabled_mask: disabled status for all channels - one bit per channel 78 78 * @base: base address of mapped PWM registers 79 79 * @base_clk: base clock used to drive the timers 80 80 * @tclk0: external clock 0 (can be ERR_PTR if not present) ··· 85 83 struct pwm_chip chip; 86 84 struct samsung_pwm_variant variant; 87 85 u8 inverter_mask; 86 + u8 disabled_mask; 88 87 89 88 void __iomem *base; 90 89 struct clk *base_clk; ··· 260 257 tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); 261 258 writel(tcon, our_chip->base + REG_TCON); 262 259 260 + our_chip->disabled_mask &= ~BIT(pwm->hwpwm); 261 + 263 262 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 264 263 265 264 return 0; ··· 279 274 tcon = readl(our_chip->base + REG_TCON); 280 275 tcon &= ~TCON_AUTORELOAD(tcon_chan); 281 276 writel(tcon, our_chip->base + REG_TCON); 277 + 278 + our_chip->disabled_mask |= BIT(pwm->hwpwm); 282 279 283 280 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 284 281 } ··· 304 297 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 305 298 } 306 299 307 - static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, 308 - int duty_ns, int period_ns) 300 + static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, 301 + int duty_ns, int period_ns, bool force_period) 309 302 { 310 303 struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); 311 304 struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); ··· 326 319 ++tcnt; 327 320 328 321 /* Check to see if we are changing the clock rate of the PWM. */ 329 - if (chan->period_ns != period_ns) { 322 + if (chan->period_ns != period_ns || force_period) { 330 323 unsigned long tin_rate; 331 324 u32 period; 332 325 ··· 383 376 chan->duty_ns = duty_ns; 384 377 385 378 return 0; 379 + } 380 + 381 + static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, 382 + int duty_ns, int period_ns) 383 + { 384 + return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); 386 385 } 387 386 388 387 static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, ··· 602 589 } 603 590 604 591 #ifdef CONFIG_PM_SLEEP 605 - static int pwm_samsung_suspend(struct device *dev) 592 + static int pwm_samsung_resume(struct device *dev) 606 593 { 607 - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); 594 + struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); 595 + struct pwm_chip *chip = &our_chip->chip; 608 596 unsigned int i; 609 597 610 - /* 611 - * No one preserves these values during suspend so reset them. 612 - * Otherwise driver leaves PWM unconfigured if same values are 613 - * passed to pwm_config() next time. 614 - */ 615 - for (i = 0; i < SAMSUNG_PWM_NUM; ++i) { 616 - struct pwm_device *pwm = &chip->chip.pwms[i]; 598 + for (i = 0; i < SAMSUNG_PWM_NUM; i++) { 599 + struct pwm_device *pwm = &chip->pwms[i]; 617 600 struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); 618 601 619 602 if (!chan) 620 603 continue; 621 604 622 - chan->period_ns = 0; 623 - chan->duty_ns = 0; 624 - } 605 + if (our_chip->variant.output_mask & BIT(i)) 606 + pwm_samsung_set_invert(our_chip, i, 607 + our_chip->inverter_mask & BIT(i)); 625 608 626 - return 0; 627 - } 609 + if (chan->period_ns) { 610 + __pwm_samsung_config(chip, pwm, chan->duty_ns, 611 + chan->period_ns, true); 612 + /* needed to make PWM disable work on Odroid-XU3 */ 613 + pwm_samsung_manual_update(our_chip, pwm); 614 + } 628 615 629 - static int pwm_samsung_resume(struct device *dev) 630 - { 631 - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); 632 - unsigned int chan; 633 - 634 - /* 635 - * Inverter setting must be preserved across suspend/resume 636 - * as nobody really seems to configure it more than once. 637 - */ 638 - for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) { 639 - if (chip->variant.output_mask & BIT(chan)) 640 - pwm_samsung_set_invert(chip, chan, 641 - chip->inverter_mask & BIT(chan)); 616 + if (our_chip->disabled_mask & BIT(i)) 617 + pwm_samsung_disable(chip, pwm); 618 + else 619 + pwm_samsung_enable(chip, pwm); 642 620 } 643 621 644 622 return 0; 645 623 } 646 624 #endif 647 625 648 - static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend, 649 - pwm_samsung_resume); 626 + static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); 650 627 651 628 static struct platform_driver pwm_samsung_driver = { 652 629 .driver = {