summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2021-12-03 23:58:59 +0100
committerThierry Reding <thierry.reding@gmail.com>2022-02-24 13:53:13 +0100
commit1c1283db07993d0246865abe03765ac09171107b (patch)
treeb7065cbb2de04a920373fe5674151577803fa2b0 /drivers
parent5f027d9b83dbb8c3f9c61c8a53363df86d158a32 (diff)
pwm: bcm-kona: Implement .apply() callback
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pwm/pwm-bcm-kona.c90
1 files changed, 58 insertions, 32 deletions
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index 64148f5f81d0..f171169c1c1f 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -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,
};