diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-09 15:34:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-09 15:34:20 -0700 |
commit | ecc721a72c121e8b641d68efd24a225abedb9a30 (patch) | |
tree | 15d514f0c374292218a52dcc24a82d7c55bcfcfc /drivers/pwm/pwm-tegra.c | |
parent | 28b47809b2171a6cfbab839936b24280639c9f85 (diff) | |
parent | 97512ceafaacf8cdb96d6a36ae55e8335c0a9e55 (diff) |
Merge tag 'pwm/for-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding:
"Adds a new driver for the PWM controller found on MediaTek SoCs and
extends support for the Atmel PWM controller to include the SAMA5D2.
Some existing drivers have been migrated to the atomic API and a few
others see miscellaneous improvements"
* tag 'pwm/for-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
pwm: tegra: Read PWM clock source rate in driver init
pwm: pca9685: Fix GPIO-only operation
pwm: mediatek: Don't explicitly set .owner
pwm: tegra: Avoid potential overflow for short periods
pwm: tegra: Add support to configure pin state in suspends/resume
pwm: tegra: Add DT binding details to configure pin in suspends/resume
pwm: tegra: Increase precision in PWM rate calculation
pwm: tegra: Use DIV_ROUND_CLOSEST_ULL() instead of local implementation
pwm: Add MediaTek PWM support
dt-bindings: pwm: Add MediaTek PWM bindings
pwm: atmel: Enable PWM on sama5d2
pwm: atmel: Switch to atomic PWM
pwm: atmel-hlcdc: Implement the suspend/resume hooks
pwm: atmel-hlcdc: Convert to the atomic PWM API
Diffstat (limited to 'drivers/pwm/pwm-tegra.c')
-rw-r--r-- | drivers/pwm/pwm-tegra.c | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index e4647840cd6e..8c6ed556db28 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -29,6 +29,7 @@ #include <linux/of_device.h> #include <linux/pwm.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> #include <linux/slab.h> #include <linux/reset.h> @@ -49,6 +50,8 @@ struct tegra_pwm_chip { struct clk *clk; struct reset_control*rst; + unsigned long clk_rate; + void __iomem *regs; const struct tegra_pwm_soc *soc; @@ -74,8 +77,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); - unsigned long long c = duty_ns; - unsigned long rate, hz; + unsigned long long c = duty_ns, hz; + unsigned long rate; u32 val = 0; int err; @@ -85,8 +88,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * nearest integer during division. */ c *= (1 << PWM_DUTY_WIDTH); - c += period_ns / 2; - do_div(c, period_ns); + c = DIV_ROUND_CLOSEST_ULL(c, period_ns); val = (u32)c << PWM_DUTY_SHIFT; @@ -94,10 +96,11 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH) * cycles at the PWM clock rate will take period_ns nanoseconds. */ - rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH; - hz = NSEC_PER_SEC / period_ns; + rate = pc->clk_rate >> PWM_DUTY_WIDTH; - rate = (rate + (hz / 2)) / hz; + /* Consider precision in PWM_SCALE_WIDTH rate calculation */ + hz = DIV_ROUND_CLOSEST_ULL(100ULL * NSEC_PER_SEC, period_ns); + rate = DIV_ROUND_CLOSEST_ULL(100ULL * rate, hz); /* * Since the actual PWM divider is the register's frequency divider @@ -198,6 +201,9 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm->clk)) return PTR_ERR(pwm->clk); + /* Read PWM clock rate from source */ + pwm->clk_rate = clk_get_rate(pwm->clk); + pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); if (IS_ERR(pwm->rst)) { ret = PTR_ERR(pwm->rst); @@ -253,6 +259,18 @@ static int tegra_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +#ifdef CONFIG_PM_SLEEP +static int tegra_pwm_suspend(struct device *dev) +{ + return pinctrl_pm_select_sleep_state(dev); +} + +static int tegra_pwm_resume(struct device *dev) +{ + return pinctrl_pm_select_default_state(dev); +} +#endif + static const struct tegra_pwm_soc tegra20_pwm_soc = { .num_channels = 4, }; @@ -269,10 +287,15 @@ static const struct of_device_id tegra_pwm_of_match[] = { MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); +static const struct dev_pm_ops tegra_pwm_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume) +}; + static struct platform_driver tegra_pwm_driver = { .driver = { .name = "tegra-pwm", .of_match_table = tegra_pwm_of_match, + .pm = &tegra_pwm_pm_ops, }, .probe = tegra_pwm_probe, .remove = tegra_pwm_remove, |