pwm: bcm-kona: Implement .apply() callback
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Fri, 3 Dec 2021 22:58:59 +0000 (23:58 +0100)
committerThierry Reding <thierry.reding@gmail.com>
Thu, 24 Feb 2022 12:53:13 +0000 (13:53 +0100)
To eventually get rid of all legacy drivers convert this driver to the
modern world implementing .apply().

The conversion wasn't quite straight forward because .config() and
.enable() were special to effectively swap their usual order. This resulted
in calculating the required values twice in some cases when
pwm_apply_state() was called. This is optimized en passant, and the order
of the callbacks is preserved without special jumping through hoops.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
drivers/pwm/pwm-bcm-kona.c

index 64148f5..f171169 100644 (file)
@@ -109,10 +109,10 @@ static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
 }
 
 static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                           int duty_ns, int period_ns)
+                           u64 duty_ns, u64 period_ns)
 {
        struct kona_pwmc *kp = to_kona_pwmc(chip);
-       u64 val, div, rate;
+       u64 div, rate;
        unsigned long prescale = PRESCALE_MIN, pc, dc;
        unsigned int value, chan = pwm->hwpwm;
 
@@ -132,10 +132,8 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
        while (1) {
                div = 1000000000;
                div *= 1 + prescale;
-               val = rate * period_ns;
-               pc = div64_u64(val, div);
-               val = rate * duty_ns;
-               dc = div64_u64(val, div);
+               pc = mul_u64_u64_div_u64(rate, period_ns, div);
+               dc = mul_u64_u64_div_u64(rate, duty_ns, div);
 
                /* If duty_ns or period_ns are not achievable then return */
                if (pc < PERIOD_COUNT_MIN)
@@ -150,25 +148,18 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
                        return -EINVAL;
        }
 
-       /*
-        * Don't apply settings if disabled. The period and duty cycle are
-        * always calculated above to ensure the new values are
-        * validated immediately instead of on enable.
-        */
-       if (pwm_is_enabled(pwm)) {
-               kona_pwmc_prepare_for_settings(kp, chan);
+       kona_pwmc_prepare_for_settings(kp, chan);
 
-               value = readl(kp->base + PRESCALE_OFFSET);
-               value &= ~PRESCALE_MASK(chan);
-               value |= prescale << PRESCALE_SHIFT(chan);
-               writel(value, kp->base + PRESCALE_OFFSET);
+       value = readl(kp->base + PRESCALE_OFFSET);
+       value &= ~PRESCALE_MASK(chan);
+       value |= prescale << PRESCALE_SHIFT(chan);
+       writel(value, kp->base + PRESCALE_OFFSET);
 
-               writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
+       writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
 
-               writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+       writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
 
-               kona_pwmc_apply_settings(kp, chan);
-       }
+       kona_pwmc_apply_settings(kp, chan);
 
        return 0;
 }
@@ -216,13 +207,6 @@ static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
                return ret;
        }
 
-       ret = kona_pwmc_config(chip, pwm, pwm_get_duty_cycle(pwm),
-                              pwm_get_period(pwm));
-       if (ret < 0) {
-               clk_disable_unprepare(kp->clk);
-               return ret;
-       }
-
        return 0;
 }
 
@@ -248,11 +232,53 @@ static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
        clk_disable_unprepare(kp->clk);
 }
 
+static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                          const struct pwm_state *state)
+{
+       int err;
+       struct kona_pwmc *kp = to_kona_pwmc(chip);
+       bool enabled = pwm->state.enabled;
+
+       if (state->polarity != pwm->state.polarity) {
+               if (enabled) {
+                       kona_pwmc_disable(chip, pwm);
+                       enabled = false;
+               }
+
+               err = kona_pwmc_set_polarity(chip, pwm, state->polarity);
+               if (err)
+                       return err;
+
+               pwm->state.polarity = state->polarity;
+       }
+
+       if (!state->enabled) {
+               if (enabled)
+                       kona_pwmc_disable(chip, pwm);
+               return 0;
+       } else if (!enabled) {
+               /*
+                * This is a bit special here, usually the PWM should only be
+                * enabled when duty and period are setup. But before this
+                * driver was converted to .apply it was done the other way
+                * around and so this behaviour was kept even though this might
+                * result in a glitch. This might be improvable by someone with
+                * hardware and/or documentation.
+                */
+               err = kona_pwmc_enable(chip, pwm);
+               if (err)
+                       return err;
+       }
+
+       err = kona_pwmc_config(pwm->chip, pwm, state->duty_cycle, state->period);
+       if (err && !pwm->state.enabled)
+               clk_disable_unprepare(kp->clk);
+
+       return err;
+}
+
 static const struct pwm_ops kona_pwm_ops = {
-       .config = kona_pwmc_config,
-       .set_polarity = kona_pwmc_set_polarity,
-       .enable = kona_pwmc_enable,
-       .disable = kona_pwmc_disable,
+       .apply = kona_pwmc_apply,
        .owner = THIS_MODULE,
 };