Message ID | 20190525181133.4875-12-martin.blumenstingl@googlemail.com |
---|---|
State | Changes Requested |
Headers | show |
Series | pwm-meson: cleanups and improvements | expand |
On 25/05/2019 20:11, Martin Blumenstingl wrote: > Update the meson_pwm_get_state() implementation to take care of all > information in the registers instead of only reading the "enabled" > state. > > The PWM output is only enabled if two conditions are met: > 1. the per-channel clock is enabled > 2. the PWM output is enabled > > Calculate the PWM period and duty cycle using the reverse formula which > we already have in meson_pwm_calc() and update struct pwm_state with the > results. > > As result of this /sys/kernel/debug/pwm now shows the PWM state set by > the bootloader (or firmware) after booting Linux. > > Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> > --- > drivers/pwm/pwm-meson.c | 52 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 49 insertions(+), 3 deletions(-) > > diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c > index 9afa1e5aaebf..010212166d5d 100644 > --- a/drivers/pwm/pwm-meson.c > +++ b/drivers/pwm/pwm-meson.c > @@ -287,19 +287,65 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > return 0; > } > > +static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip, > + struct pwm_device *pwm, u32 cnt) > +{ > + struct meson_pwm *meson = to_meson_pwm(chip); > + struct meson_pwm_channel *channel; > + unsigned long fin_freq; > + u32 fin_ns; > + > + /* to_meson_pwm() can only be used after .get_state() is called */ > + channel = &meson->channels[pwm->hwpwm]; > + > + fin_freq = clk_get_rate(channel->clk); > + if (fin_freq == 0) > + return 0; > + > + fin_ns = div_u64(NSEC_PER_SEC, fin_freq); > + > + return cnt * fin_ns * (channel->pre_div + 1); > +} > + > static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, > struct pwm_state *state) > { > struct meson_pwm *meson = to_meson_pwm(chip); > - u32 value, mask; > + struct meson_pwm_channel_data *channel_data; > + struct meson_pwm_channel *channel; > + u32 value, tmp; > > if (!state) > return; > > - mask = meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; > + channel = &meson->channels[pwm->hwpwm]; > + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; > > value = readl(meson->base + REG_MISC_AB); > - state->enabled = (value & mask) != 0; > + > + tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask; > + state->enabled = (value & tmp) == tmp; > + > + tmp = value >> channel_data->clk_div_shift; > + channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp); > + > + value = readl(meson->base + channel_data->reg_offset); > + > + channel->lo = FIELD_GET(PWM_LOW_MASK, value); > + channel->hi = FIELD_GET(PWM_HIGH_MASK, value); > + > + if (channel->lo == 0) { > + state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); > + state->duty_cycle = state->period; > + } else if (channel->lo >= channel->hi) { > + state->period = meson_pwm_cnt_to_ns(chip, pwm, > + channel->lo + channel->hi); > + state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, > + channel->hi); > + } else { > + state->period = 0; > + state->duty_cycle = 0; > + } > } > > static const struct pwm_ops meson_pwm_ops = { > Thanks for that !!! Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 9afa1e5aaebf..010212166d5d 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -287,19 +287,65 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip, + struct pwm_device *pwm, u32 cnt) +{ + struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel; + unsigned long fin_freq; + u32 fin_ns; + + /* to_meson_pwm() can only be used after .get_state() is called */ + channel = &meson->channels[pwm->hwpwm]; + + fin_freq = clk_get_rate(channel->clk); + if (fin_freq == 0) + return 0; + + fin_ns = div_u64(NSEC_PER_SEC, fin_freq); + + return cnt * fin_ns * (channel->pre_div + 1); +} + static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct meson_pwm *meson = to_meson_pwm(chip); - u32 value, mask; + struct meson_pwm_channel_data *channel_data; + struct meson_pwm_channel *channel; + u32 value, tmp; if (!state) return; - mask = meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; + channel = &meson->channels[pwm->hwpwm]; + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; value = readl(meson->base + REG_MISC_AB); - state->enabled = (value & mask) != 0; + + tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask; + state->enabled = (value & tmp) == tmp; + + tmp = value >> channel_data->clk_div_shift; + channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp); + + value = readl(meson->base + channel_data->reg_offset); + + channel->lo = FIELD_GET(PWM_LOW_MASK, value); + channel->hi = FIELD_GET(PWM_HIGH_MASK, value); + + if (channel->lo == 0) { + state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); + state->duty_cycle = state->period; + } else if (channel->lo >= channel->hi) { + state->period = meson_pwm_cnt_to_ns(chip, pwm, + channel->lo + channel->hi); + state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, + channel->hi); + } else { + state->period = 0; + state->duty_cycle = 0; + } } static const struct pwm_ops meson_pwm_ops = {
Update the meson_pwm_get_state() implementation to take care of all information in the registers instead of only reading the "enabled" state. The PWM output is only enabled if two conditions are met: 1. the per-channel clock is enabled 2. the PWM output is enabled Calculate the PWM period and duty cycle using the reverse formula which we already have in meson_pwm_calc() and update struct pwm_state with the results. As result of this /sys/kernel/debug/pwm now shows the PWM state set by the bootloader (or firmware) after booting Linux. Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> --- drivers/pwm/pwm-meson.c | 52 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-)