Skip to content

Instantly share code, notes, and snippets.

@apritzel
Created April 29, 2024 15:52
Show Gist options
  • Save apritzel/5c7b131acab80d7e6340a7e066e2a954 to your computer and use it in GitHub Desktop.
Save apritzel/5c7b131acab80d7e6340a7e066e2a954 to your computer and use it in GitHub Desktop.
Allwinner D1 PWM driver refactoring patch to accommodate H616 PWM IP
Author: Andre Przywara <andre.przywara@arm.com>
Date: Wed Sep 6 21:04:05 2023 +0100
pwm: sun20i: Refactor channel enablement
When enabling or disabling a PWM channel, we also ungate and gate the
respective clock at the same time.
Factor this out into a separate function, to simplify extending the
driver to other SoCs, where the clock gate is located in a different
register.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
diff --git a/drivers/pwm/pwm-sun20i.c b/drivers/pwm/pwm-sun20i.c
index eca14b36eaf44..4edb57c33f6b9 100644
--- a/drivers/pwm/pwm-sun20i.c
+++ b/drivers/pwm/pwm-sun20i.c
@@ -68,6 +68,40 @@ static inline void sun20i_pwm_writel(struct sun20i_pwm_chip *chip,
writel(val, chip->base + offset);
}
+/*
+ * Enables or disables a channel, taking care of the respective clock gating
+ * on the way: The old PWM uses a shared clock gate per channel pair, whereas
+ * the new PWM has a separate clock gate per channel.
+ *
+ * Returns the status of the *neighbouring* channel.
+ */
+static bool pwm_sun20i_enable_chan(struct pwm_device *pwm, bool set)
+{
+ struct pwm_chip *chip = pwm->chip;
+ struct sun20i_pwm_chip *sun20i_chip = to_sun20i_pwm_chip(chip);
+ int chan = pwm->hwpwm;
+ u32 clk_gate, enable;
+ bool ret;
+
+ enable = readl(sun20i_chip->base + PWM_ENABLE);
+ clk_gate = readl(sun20i_chip->base + PWM_CLK_GATE);
+
+ ret = enable & PWM_ENABLE_EN(chan ^ 1);
+
+ if (set) {
+ clk_gate |= PWM_ENABLE_EN(chan);
+ enable |= PWM_ENABLE_EN(chan);
+ } else {
+ clk_gate &= ~PWM_ENABLE_EN(chan);
+ enable &= ~PWM_ENABLE_EN(chan);
+ }
+
+ writel(clk_gate, sun20i_chip->base + PWM_CLK_GATE);
+ writel(enable, sun20i_chip->base + PWM_ENABLE);
+
+ return ret;
+}
+
static int sun20i_pwm_get_state(struct pwm_chip *chip,
struct pwm_device *pwm,
struct pwm_state *state)
@@ -119,25 +153,19 @@ static int sun20i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct sun20i_pwm_chip *sun20i_chip = to_sun20i_pwm_chip(chip);
- u32 clk_gate, clk_cfg, pwm_en, ctl, period;
+ u32 clk_cfg, ctl, period;
u64 bus_rate, hosc_rate, clk_div, val;
u32 prescaler, div_m;
- bool use_bus_clk;
+ bool other_chan, use_bus_clk;
int ret = 0;
mutex_lock(&sun20i_chip->mutex);
- pwm_en = sun20i_pwm_readl(sun20i_chip, PWM_ENABLE);
-
- if (state->enabled != pwm->state.enabled)
- clk_gate = sun20i_pwm_readl(sun20i_chip, PWM_CLK_GATE);
-
- if (state->enabled != pwm->state.enabled && !state->enabled) {
- clk_gate &= ~PWM_CLK_GATE_GATING(pwm->hwpwm);
- pwm_en &= ~PWM_ENABLE_EN(pwm->hwpwm);
- sun20i_pwm_writel(sun20i_chip, pwm_en, PWM_ENABLE);
- sun20i_pwm_writel(sun20i_chip, clk_gate, PWM_CLK_GATE);
- }
+ if (state->enabled != pwm->state.enabled && !state->enabled)
+ other_chan = pwm_sun20i_enable_chan(pwm, false);
+ else
+ other_chan = sun20i_pwm_readl(sun20i_chip, PWM_ENABLE) &
+ PWM_ENABLE_EN(pwm->hwpwm ^ 1);
if (state->polarity != pwm->state.polarity ||
state->duty_cycle != pwm->state.duty_cycle ||
@@ -146,7 +174,7 @@ static int sun20i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
clk_cfg = sun20i_pwm_readl(sun20i_chip, PWM_CLK_CFG(pwm->hwpwm));
hosc_rate = clk_get_rate(sun20i_chip->clk_hosc);
bus_rate = clk_get_rate(sun20i_chip->clk_apb0);
- if (pwm_en & PWM_ENABLE_EN(pwm->hwpwm ^ 1)) {
+ if (other_chan) {
/* if the neighbor channel is enable, check period only */
use_bus_clk = FIELD_GET(PWM_CLK_CFG_SRC, clk_cfg) != 0;
val = state->period * (use_bus_clk ? bus_rate : hosc_rate);
@@ -221,13 +249,8 @@ static int sun20i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
sun20i_pwm_writel(sun20i_chip, ctl, PWM_CTL(pwm->hwpwm));
}
- if (state->enabled != pwm->state.enabled && state->enabled) {
- clk_gate &= ~PWM_CLK_GATE_BYPASS(pwm->hwpwm);
- clk_gate |= PWM_CLK_GATE_GATING(pwm->hwpwm);
- pwm_en |= PWM_ENABLE_EN(pwm->hwpwm);
- sun20i_pwm_writel(sun20i_chip, pwm_en, PWM_ENABLE);
- sun20i_pwm_writel(sun20i_chip, clk_gate, PWM_CLK_GATE);
- }
+ if (state->enabled != pwm->state.enabled && state->enabled)
+ pwm_sun20i_enable_chan(pwm, true);
unlock_mutex:
mutex_unlock(&sun20i_chip->mutex);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment