Merge remote-tracking branch 'mauro-exp/docbook3' into death-to-docbook
[linux-2.6-block.git] / drivers / pwm / pwm-tegra.c
index e4647840cd6e3129fb94da036e30470714852f14..8c6ed556db28a874c244c1300cae30109e43deb1 100644 (file)
@@ -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,