Merge tag 'pwm/for-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
[sfrench/cifs-2.6.git] / drivers / pwm / pwm-rcar.c
index a41812fc6f95733a2568eb0d6b5c7f44fe5a1741..cfe7dd1b448e1b15ce72b41cbaafd7b65703106c 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data,
 static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
 {
        unsigned long clk_rate = clk_get_rate(rp->clk);
-       unsigned long long max; /* max cycle / nanoseconds */
-       unsigned int div;
+       u64 div, tmp;
 
        if (clk_rate == 0)
                return -EINVAL;
 
-       for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) {
-               max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE *
-                       (1 << div);
-               do_div(max, clk_rate);
-               if (period_ns <= max)
-                       break;
-       }
+       div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE;
+       tmp = (u64)period_ns * clk_rate + div - 1;
+       tmp = div64_u64(tmp, div);
+       div = ilog2(tmp - 1) + 1;
 
        return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE;
 }
@@ -139,39 +137,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
        pm_runtime_put(chip->dev);
 }
 
-static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                          int duty_ns, int period_ns)
+static int rcar_pwm_enable(struct rcar_pwm_chip *rp)
 {
-       struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-       int div, ret;
-
-       div = rcar_pwm_get_clock_division(rp, period_ns);
-       if (div < 0)
-               return div;
-
-       /*
-        * Let the core driver set pwm->period if disabled and duty_ns == 0.
-        * But, this driver should prevent to set the new duty_ns if current
-        * duty_cycle is not set
-        */
-       if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
-               return 0;
-
-       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
-
-       ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns);
-       if (!ret)
-               rcar_pwm_set_clock_control(rp, div);
-
-       /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
-       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
-
-       return ret;
-}
-
-static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-       struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
        u32 value;
 
        /* Don't enable the PWM device if CYC0 or PH0 is 0 */
@@ -185,19 +152,51 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        return 0;
 }
 
-static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+static void rcar_pwm_disable(struct rcar_pwm_chip *rp)
+{
+       rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+}
+
+static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                         struct pwm_state *state)
 {
        struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
+       struct pwm_state cur_state;
+       int div, ret;
 
-       rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+       /* This HW/driver only supports normal polarity */
+       pwm_get_state(pwm, &cur_state);
+       if (state->polarity != PWM_POLARITY_NORMAL)
+               return -ENOTSUPP;
+
+       if (!state->enabled) {
+               rcar_pwm_disable(rp);
+               return 0;
+       }
+
+       div = rcar_pwm_get_clock_division(rp, state->period);
+       if (div < 0)
+               return div;
+
+       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
+
+       ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period);
+       if (!ret)
+               rcar_pwm_set_clock_control(rp, div);
+
+       /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
+       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
+
+       if (!ret && state->enabled)
+               ret = rcar_pwm_enable(rp);
+
+       return ret;
 }
 
 static const struct pwm_ops rcar_pwm_ops = {
        .request = rcar_pwm_request,
        .free = rcar_pwm_free,
-       .config = rcar_pwm_config,
-       .enable = rcar_pwm_enable,
-       .disable = rcar_pwm_disable,
+       .apply = rcar_pwm_apply,
        .owner = THIS_MODULE,
 };
 
@@ -279,18 +278,16 @@ static int rcar_pwm_suspend(struct device *dev)
 static int rcar_pwm_resume(struct device *dev)
 {
        struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev);
+       struct pwm_state state;
 
        if (!test_bit(PWMF_REQUESTED, &pwm->flags))
                return 0;
 
        pm_runtime_get_sync(dev);
 
-       rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle,
-                       pwm->state.period);
-       if (pwm_is_enabled(pwm))
-               rcar_pwm_enable(pwm->chip, pwm);
+       pwm_get_state(pwm, &state);
 
-       return 0;
+       return rcar_pwm_apply(pwm->chip, pwm, &state);
 }
 #endif /* CONFIG_PM_SLEEP */
 static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume);