On Wed, 22 Mar 2017 15:29:34 +0200
Claudiu Beznea wrote:
> The currently Atmel PWM controllers supported by this driver
> could change period or duty factor without channel disable,
> for regular channels (sama5d3 support this by using period
> or duty factor update registers, sam9rl support this by
> writing channel update register and select the corresponding
> update: period or duty factor). The chip doesn't support run
> time changings of signal polarity. To take advantage of
> atomic PWM framework and let controller works without glitches,
> in this patch only the duty factor could be changed without
> disabling PWM channel. For period and signal polarity the
> atomic PWM is simulated by disabling + enabling the right PWM channel.
>
> Signed-off-by: Claudiu Beznea
Reviewed-by: Boris Brezillon
>
> ---
> drivers/pwm/pwm-atmel.c | 273
> +++-
> 1 file changed, 129 insertions(+), 144 deletions(-)
>
> diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
> index 67a7023..f147154 100644
> --- a/drivers/pwm/pwm-atmel.c
> +++ b/drivers/pwm/pwm-atmel.c
> @@ -58,17 +58,22 @@
> #define PWM_MAX_PRD 0x
> #define PRD_MAX_PRES 10
>
> +struct atmel_pwm_registers {
> + u8 period;
> + u8 period_upd;
> + u8 duty;
> + u8 duty_upd;
> +};
> +
> struct atmel_pwm_chip {
> struct pwm_chip chip;
> struct clk *clk;
> void __iomem *base;
> + const struct atmel_pwm_registers *regs;
>
> unsigned int updated_pwms;
> /* ISR is cleared when read, ensure only one thread does that */
> struct mutex isr_lock;
> -
> - void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
> -unsigned long dty, unsigned long prd);
> };
>
> static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip)
> @@ -105,153 +110,71 @@ static inline void atmel_pwm_ch_writel(struct
> atmel_pwm_chip *chip,
> writel_relaxed(val, chip->base + base + offset);
> }
>
> -static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> - int duty_ns, int period_ns)
> +static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
> + const struct pwm_state *state,
> + unsigned long *cprd, u32 *pres)
> {
> struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
> - unsigned long prd, dty;
> - unsigned long long div;
> - unsigned int pres = 0;
> - u32 val;
> - int ret;
> -
> - if (pwm_is_enabled(pwm) && (period_ns != pwm_get_period(pwm))) {
> - dev_err(chip->dev, "cannot change PWM period while enabled\n");
> - return -EBUSY;
> - }
> + unsigned long long cycles = state->period;
>
> /* Calculate the period cycles and prescale value */
> - div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns;
> - do_div(div, NSEC_PER_SEC);
> + cycles *= clk_get_rate(atmel_pwm->clk);
> + do_div(cycles, NSEC_PER_SEC);
>
> - while (div > PWM_MAX_PRD) {
> - div >>= 1;
> - pres++;
> - }
> + for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1)
> + (*pres)++;
>
> - if (pres > PRD_MAX_PRES) {
> + if (*pres > PRD_MAX_PRES) {
> dev_err(chip->dev, "pres exceeds the maximum value\n");
> return -EINVAL;
> }
>
> - /* Calculate the duty cycles */
> - prd = div;
> - div *= duty_ns;
> - do_div(div, period_ns);
> - dty = prd - div;
> -
> - ret = clk_enable(atmel_pwm->clk);
> - if (ret) {
> - dev_err(chip->dev, "failed to enable PWM clock\n");
> - return ret;
> - }
> -
> - /* It is necessary to preserve CPOL, inside CMR */
> - val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
> - val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
> - atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
> - atmel_pwm->config(chip, pwm, dty, prd);
> - mutex_lock(&atmel_pwm->isr_lock);
> - atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
> - atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm);
> - mutex_unlock(&atmel_pwm->isr_lock);
> + *cprd = cycles;
>
> - clk_disable(atmel_pwm->clk);
> - return ret;
> + return 0;
> }
>
> -static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device
> *pwm,
> - unsigned long dty, unsigned long prd)
> +static void atmel_pwm_calculate_cdty(const struct pwm_state *state,
> + unsigned long cprd, unsigned long *cdty)
> {
> - struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
> - unsigned int val;
> + unsigned long long cycles = state->duty_cycle;
>
> -
> - atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
> -
> - val = atmel