pwm: mxs: Implement ->apply()
authorRasmus Villemoes <linux@rasmusvillemoes.dk>
Fri, 4 Oct 2019 13:32:02 +0000 (15:32 +0200)
committerThierry Reding <thierry.reding@gmail.com>
Wed, 8 Jan 2020 12:32:47 +0000 (13:32 +0100)
In preparation for supporting setting the polarity, switch the driver
to support the ->apply() method.

Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
drivers/pwm/pwm-mxs.c

index b14376b47ac8c1bde7b5330bbd32be24d9c1a183..10efd3de0bb327fceb9529b95b6b6afaf4c62691 100644 (file)
@@ -26,6 +26,7 @@
 #define  PERIOD_PERIOD_MAX     0x10000
 #define  PERIOD_ACTIVE_HIGH    (3 << 16)
 #define  PERIOD_INACTIVE_LOW   (2 << 18)
+#define  PERIOD_POLARITY_NORMAL        (PERIOD_ACTIVE_HIGH | PERIOD_INACTIVE_LOW)
 #define  PERIOD_CDIV(div)      (((div) & 0x7) << 20)
 #define  PERIOD_CDIV_MAX       8
 
@@ -41,6 +42,74 @@ struct mxs_pwm_chip {
 
 #define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
 
+static int mxs_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                        const struct pwm_state *state)
+{
+       struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+       int ret, div = 0;
+       unsigned int period_cycles, duty_cycles;
+       unsigned long rate;
+       unsigned long long c;
+
+       if (state->polarity != PWM_POLARITY_NORMAL)
+               return -ENOTSUPP;
+
+       /*
+        * If the PWM channel is disabled, make sure to turn on the
+        * clock before calling clk_get_rate() and writing to the
+        * registers. Otherwise, just keep it enabled.
+        */
+       if (!pwm_is_enabled(pwm)) {
+               ret = clk_prepare_enable(mxs->clk);
+               if (ret)
+                       return ret;
+       }
+
+       if (!state->enabled && pwm_is_enabled(pwm))
+               writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+       rate = clk_get_rate(mxs->clk);
+       while (1) {
+               c = rate / cdiv[div];
+               c = c * state->period;
+               do_div(c, 1000000000);
+               if (c < PERIOD_PERIOD_MAX)
+                       break;
+               div++;
+               if (div >= PERIOD_CDIV_MAX)
+                       return -EINVAL;
+       }
+
+       period_cycles = c;
+       c *= state->duty_cycle;
+       do_div(c, state->period);
+       duty_cycles = c;
+
+       /*
+        * The data sheet the says registers must be written to in
+        * this order (ACTIVEn, then PERIODn). Also, the new settings
+        * only take effect at the beginning of a new period, avoiding
+        * glitches.
+        */
+       writel(duty_cycles << 16,
+              mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+       writel(PERIOD_PERIOD(period_cycles) | PERIOD_POLARITY_NORMAL | PERIOD_CDIV(div),
+              mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+       if (state->enabled) {
+               if (!pwm_is_enabled(pwm)) {
+                       /*
+                        * The clock was enabled above. Just enable
+                        * the channel in the control register.
+                        */
+                       writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+               }
+       } else {
+               clk_disable_unprepare(mxs->clk);
+       }
+       return 0;
+}
+
 static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                          int duty_ns, int period_ns)
 {
@@ -116,6 +185,7 @@ static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 }
 
 static const struct pwm_ops mxs_pwm_ops = {
+       .apply = mxs_pwm_apply,
        .config = mxs_pwm_config,
        .enable = mxs_pwm_enable,
        .disable = mxs_pwm_disable,