diff options
Diffstat (limited to 'drivers/pwm/pwm-meson.c')
-rw-r--r-- | drivers/pwm/pwm-meson.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 4ae5d774443e..fb5a369b1a8d 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -111,6 +111,10 @@ struct meson_pwm { const struct meson_pwm_data *data; void __iomem *base; u8 inverter_mask; + /* + * Protects register (write) access to the REG_MISC_AB register + * that is shared between the two PWMs. + */ spinlock_t lock; }; @@ -184,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, do_div(fin_ps, fin_freq); /* Calc pre_div with the period */ - for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { + for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) { cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, fin_ps * (pre_div + 1)); dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", @@ -193,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, break; } - if (pre_div == MISC_CLK_DIV_MASK) { + if (pre_div > MISC_CLK_DIV_MASK) { dev_err(meson->chip.dev, "unable to get period pre_div\n"); return -EINVAL; } @@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson, { u32 value, clk_shift, clk_enable, enable; unsigned int offset; + unsigned long flags; switch (id) { case 0: @@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson, return; } + spin_lock_irqsave(&meson->lock, flags); + value = readl(meson->base + REG_MISC_AB); value &= ~(MISC_CLK_DIV_MASK << clk_shift); value |= channel->pre_div << clk_shift; @@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson, value = readl(meson->base + REG_MISC_AB); value |= enable; writel(value, meson->base + REG_MISC_AB); + + spin_unlock_irqrestore(&meson->lock, flags); } static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) { u32 value, enable; + unsigned long flags; switch (id) { case 0: @@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) return; } + spin_lock_irqsave(&meson->lock, flags); + value = readl(meson->base + REG_MISC_AB); value &= ~enable; writel(value, meson->base + REG_MISC_AB); + + spin_unlock_irqrestore(&meson->lock, flags); } static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -296,29 +310,21 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); struct meson_pwm *meson = to_meson_pwm(chip); - unsigned long flags; int err = 0; if (!state) return -EINVAL; - spin_lock_irqsave(&meson->lock, flags); - if (!state->enabled) { meson_pwm_disable(meson, pwm->hwpwm); channel->state.enabled = false; - goto unlock; + return 0; } if (state->period != channel->state.period || state->duty_cycle != channel->state.duty_cycle || state->polarity != channel->state.polarity) { - if (channel->state.enabled) { - meson_pwm_disable(meson, pwm->hwpwm); - channel->state.enabled = false; - } - if (state->polarity != channel->state.polarity) { if (state->polarity == PWM_POLARITY_NORMAL) meson->inverter_mask |= BIT(pwm->hwpwm); @@ -329,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, err = meson_pwm_calc(meson, channel, pwm->hwpwm, state->duty_cycle, state->period); if (err < 0) - goto unlock; + return err; channel->state.polarity = state->polarity; channel->state.period = state->period; @@ -341,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, channel->state.enabled = true; } -unlock: - spin_unlock_irqrestore(&meson->lock, flags); - return err; + return 0; } static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, @@ -429,6 +433,24 @@ static const struct meson_pwm_data pwm_axg_ao_data = { .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), }; +static const char * const pwm_g12a_ao_cd_parent_names[] = { + "aoclk81", "xtal", +}; + +static const struct meson_pwm_data pwm_g12a_ao_cd_data = { + .parent_names = pwm_g12a_ao_cd_parent_names, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names), +}; + +static const char * const pwm_g12a_ee_parent_names[] = { + "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" +}; + +static const struct meson_pwm_data pwm_g12a_ee_data = { + .parent_names = pwm_g12a_ee_parent_names, + .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names), +}; + static const struct of_device_id meson_pwm_matches[] = { { .compatible = "amlogic,meson8b-pwm", @@ -450,6 +472,18 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson-axg-ao-pwm", .data = &pwm_axg_ao_data }, + { + .compatible = "amlogic,meson-g12a-ee-pwm", + .data = &pwm_g12a_ee_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-ab", + .data = &pwm_axg_ao_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-cd", + .data = &pwm_g12a_ao_cd_data + }, {}, }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); |