[V2,2/4] drivers: pwm: pwm-bcm-kona: Switch to using atomic PWM Framework

Message ID 1547669716-20070-3-git-send-email-sheetal.tigadoli@broadcom.com
State Changes Requested
Headers show
Series
  • Add support for PWM Configure and stablize for PWM kona
Related show

Commit Message

Sheetal Tigadoli Jan. 16, 2019, 8:15 p.m.
Switch to using atomic PWM Framework on broadcom PWM kona driver

Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
---
 drivers/pwm/pwm-bcm-kona.c | 201 +++++++++++++++++++--------------------------
 1 file changed, 83 insertions(+), 118 deletions(-)

Comments

Uwe Kleine-K├Ânig Jan. 21, 2019, 6:46 p.m. | #1
Hello,

On Thu, Jan 17, 2019 at 01:45:14AM +0530, Sheetal Tigadoli wrote:
> Switch to using atomic PWM Framework on broadcom PWM kona driver
> 
> Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
> ---
>  drivers/pwm/pwm-bcm-kona.c | 201 +++++++++++++++++++--------------------------
>  1 file changed, 83 insertions(+), 118 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
> index 09a95ae..fe63289 100644
> --- a/drivers/pwm/pwm-bcm-kona.c
> +++ b/drivers/pwm/pwm-bcm-kona.c
> @@ -108,151 +108,116 @@ static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
>  	ndelay(400);
>  }
>  
> -static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
> -			    int duty_ns, int period_ns)
> -{
> -	struct kona_pwmc *kp = to_kona_pwmc(chip);
> -	u64 val, div, rate;
> -	unsigned long prescale = PRESCALE_MIN, pc, dc;
> -	unsigned int value, chan = pwm->hwpwm;
> -
> -	/*
> -	 * Find period count, duty count and prescale to suit duty_ns and
> -	 * period_ns. This is done according to formulas described below:
> -	 *
> -	 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
> -	 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
> -	 *
> -	 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
> -	 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
> -	 */
> -
> -	rate = clk_get_rate(kp->clk);
> -
> -	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);
> -
> -		/* If duty_ns or period_ns are not achievable then return */
> -		if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN)
> -			return -EINVAL;
> -
> -		/* If pc and dc are in bounds, the calculation is done */
> -		if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
> -			break;
> -
> -		/* Otherwise, increase prescale and recalculate pc and dc */
> -		if (++prescale > PRESCALE_MAX)
> -			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);
> -
> -		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(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
> -
> -		kona_pwmc_apply_settings(kp, chan);
> -	}
> -
> -	return 0;
> -}
> -
> -static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
> -				  enum pwm_polarity polarity)
> +static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>  {
>  	struct kona_pwmc *kp = to_kona_pwmc(chip);
>  	unsigned int chan = pwm->hwpwm;
>  	unsigned int value;
> -	int ret;
> -
> -	ret = clk_prepare_enable(kp->clk);
> -	if (ret < 0) {
> -		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
> -		return ret;
> -	}
>  
>  	kona_pwmc_prepare_for_settings(kp, chan);
>  
> -	value = readl(kp->base + PWM_CONTROL_OFFSET);
> -
> -	if (polarity == PWM_POLARITY_NORMAL)
> -		value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
> -	else
> -		value &= ~(1 << PWM_CONTROL_POLARITY_SHIFT(chan));
> +	/* Simulate a disable by configuring for zero duty */
> +	writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
> +	writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
>  
> -	writel(value, kp->base + PWM_CONTROL_OFFSET);
> +	/* Set prescale to 0 for this channel */

kona_pwmc_apply uses PRESCALE_MIN instead of a plain 0. To make the
comment more helpful tell *why* you do it instead of stating the obvious
for the fluent C programmer.

> +	value = readl(kp->base + PRESCALE_OFFSET);
> +	value &= ~PRESCALE_MASK(chan);
> +	writel(value, kp->base + PRESCALE_OFFSET);
>  
>  	kona_pwmc_apply_settings(kp, chan);
>  
>  	clk_disable_unprepare(kp->clk);
> -
> -	return 0;
>  }
>  
> -static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> +			   struct pwm_state *state)
>  {
>  	struct kona_pwmc *kp = to_kona_pwmc(chip);
> +	struct pwm_state cstate;
> +	u64 val, div, rate;
> +	unsigned long prescale = PRESCALE_MIN, pc, dc;
> +	unsigned int value, chan = pwm->hwpwm;
>  	int ret;
>  
> -	ret = clk_prepare_enable(kp->clk);
> -	if (ret < 0) {
> -		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
> -		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;
> -	}
> +	pwm_get_state(pwm, &cstate);

The pwm_get_state function is designed for PWM-consumers. It is an
implementation detail that it also works for drivers. So I'd like to see
its usage dropped in drivers. (Note that Thierry might not agree here.)

> +
> +	if (state->enabled) {
> +		/*
> +		 * Find period count, duty count and prescale to suit duty_ns
> +		 * and period_ns. This is done according to formulas described
> +		 * below:
> +		 *
> +		 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
> +		 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
> +		 *
> +		 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
> +		 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
> +		 */
> +		rate = clk_get_rate(kp->clk);
> +
> +		while (1) {
> +			div = 1000000000;
> +			div *= 1 + prescale;
> +			val = rate * state->period;
> +			pc = div64_u64(val, div);
> +			val = rate * state->duty_cycle;
> +			dc = div64_u64(val, div);
> +
> +			/* If duty_ns or period_ns are not achievable then

Please stick to the usual style for multi-line comments, i.e. "/*" on a
separate line.

> +			 * return
> +			 */
> +			if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN)
> +				return -EINVAL;
> +
> +			/* If pc & dc are in bounds, the calculation is done */
> +			if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
> +				break;
> +
> +			/* Otherwise, increase prescale & recalculate pc & dc */
> +			if (++prescale > PRESCALE_MAX)
> +				return -EINVAL;
> +		}
> +
> +		if (!cstate.enabled) {
> +			ret = clk_prepare_enable(kp->clk);
> +			if (ret < 0) {
> +				dev_err(chip->dev,
> +					"failed to enable clock: %d\n", ret);
> +				return ret;
> +			}
> +		}
>  
> -	return 0;
> -}
> -
> -static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct kona_pwmc *kp = to_kona_pwmc(chip);
> -	unsigned int chan = pwm->hwpwm;
> -	unsigned int value;
> +		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);
> +		writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
> +		writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
>  
> -	/* Simulate a disable by configuring for zero duty */
> -	writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
> -	writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
> +		if (cstate.polarity != state->polarity) {
> +			value = readl(kp->base + PWM_CONTROL_OFFSET);
> +			if (state->polarity == PWM_POLARITY_NORMAL)
> +				value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
> +			else
> +				value &= ~(1 <<
> +					   PWM_CONTROL_POLARITY_SHIFT(chan));
>  
> -	/* Set prescale to 0 for this channel */
> -	value = readl(kp->base + PRESCALE_OFFSET);
> -	value &= ~PRESCALE_MASK(chan);
> -	writel(value, kp->base + PRESCALE_OFFSET);
> +			writel(value, kp->base + PWM_CONTROL_OFFSET);
> +		}
>  
> -	kona_pwmc_apply_settings(kp, chan);
> +		kona_pwmc_apply_settings(kp, chan);
> +	} else if (cstate.enabled) {
> +		kona_pwmc_disable(chip, pwm);

If I do:

	pwm_apply_state(pwm, { .polarity = PWM_POLARITY_NORMAL, .enabled = true, ... });
	pwm_apply_state(pwm, { .polarity = PWM_POLARITY_INVERSED, .enabled = false, ... });

the output is constant low, which is wrong.

Best regards
Uwe

Patch

diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index 09a95ae..fe63289 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -108,151 +108,116 @@  static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
 	ndelay(400);
 }
 
-static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
-			    int duty_ns, int period_ns)
-{
-	struct kona_pwmc *kp = to_kona_pwmc(chip);
-	u64 val, div, rate;
-	unsigned long prescale = PRESCALE_MIN, pc, dc;
-	unsigned int value, chan = pwm->hwpwm;
-
-	/*
-	 * Find period count, duty count and prescale to suit duty_ns and
-	 * period_ns. This is done according to formulas described below:
-	 *
-	 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
-	 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
-	 *
-	 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
-	 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
-	 */
-
-	rate = clk_get_rate(kp->clk);
-
-	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);
-
-		/* If duty_ns or period_ns are not achievable then return */
-		if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN)
-			return -EINVAL;
-
-		/* If pc and dc are in bounds, the calculation is done */
-		if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
-			break;
-
-		/* Otherwise, increase prescale and recalculate pc and dc */
-		if (++prescale > PRESCALE_MAX)
-			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);
-
-		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(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
-
-		kona_pwmc_apply_settings(kp, chan);
-	}
-
-	return 0;
-}
-
-static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
-				  enum pwm_polarity polarity)
+static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct kona_pwmc *kp = to_kona_pwmc(chip);
 	unsigned int chan = pwm->hwpwm;
 	unsigned int value;
-	int ret;
-
-	ret = clk_prepare_enable(kp->clk);
-	if (ret < 0) {
-		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
-		return ret;
-	}
 
 	kona_pwmc_prepare_for_settings(kp, chan);
 
-	value = readl(kp->base + PWM_CONTROL_OFFSET);
-
-	if (polarity == PWM_POLARITY_NORMAL)
-		value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
-	else
-		value &= ~(1 << PWM_CONTROL_POLARITY_SHIFT(chan));
+	/* Simulate a disable by configuring for zero duty */
+	writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+	writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
 
-	writel(value, kp->base + PWM_CONTROL_OFFSET);
+	/* Set prescale to 0 for this channel */
+	value = readl(kp->base + PRESCALE_OFFSET);
+	value &= ~PRESCALE_MASK(chan);
+	writel(value, kp->base + PRESCALE_OFFSET);
 
 	kona_pwmc_apply_settings(kp, chan);
 
 	clk_disable_unprepare(kp->clk);
-
-	return 0;
 }
 
-static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			   struct pwm_state *state)
 {
 	struct kona_pwmc *kp = to_kona_pwmc(chip);
+	struct pwm_state cstate;
+	u64 val, div, rate;
+	unsigned long prescale = PRESCALE_MIN, pc, dc;
+	unsigned int value, chan = pwm->hwpwm;
 	int ret;
 
-	ret = clk_prepare_enable(kp->clk);
-	if (ret < 0) {
-		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
-		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;
-	}
+	pwm_get_state(pwm, &cstate);
+
+	if (state->enabled) {
+		/*
+		 * Find period count, duty count and prescale to suit duty_ns
+		 * and period_ns. This is done according to formulas described
+		 * below:
+		 *
+		 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
+		 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+		 *
+		 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+		 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+		 */
+		rate = clk_get_rate(kp->clk);
+
+		while (1) {
+			div = 1000000000;
+			div *= 1 + prescale;
+			val = rate * state->period;
+			pc = div64_u64(val, div);
+			val = rate * state->duty_cycle;
+			dc = div64_u64(val, div);
+
+			/* If duty_ns or period_ns are not achievable then
+			 * return
+			 */
+			if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN)
+				return -EINVAL;
+
+			/* If pc & dc are in bounds, the calculation is done */
+			if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
+				break;
+
+			/* Otherwise, increase prescale & recalculate pc & dc */
+			if (++prescale > PRESCALE_MAX)
+				return -EINVAL;
+		}
+
+		if (!cstate.enabled) {
+			ret = clk_prepare_enable(kp->clk);
+			if (ret < 0) {
+				dev_err(chip->dev,
+					"failed to enable clock: %d\n", ret);
+				return ret;
+			}
+		}
 
-	return 0;
-}
-
-static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct kona_pwmc *kp = to_kona_pwmc(chip);
-	unsigned int chan = pwm->hwpwm;
-	unsigned int value;
+		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);
+		writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
+		writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
 
-	/* Simulate a disable by configuring for zero duty */
-	writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
-	writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
+		if (cstate.polarity != state->polarity) {
+			value = readl(kp->base + PWM_CONTROL_OFFSET);
+			if (state->polarity == PWM_POLARITY_NORMAL)
+				value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
+			else
+				value &= ~(1 <<
+					   PWM_CONTROL_POLARITY_SHIFT(chan));
 
-	/* Set prescale to 0 for this channel */
-	value = readl(kp->base + PRESCALE_OFFSET);
-	value &= ~PRESCALE_MASK(chan);
-	writel(value, kp->base + PRESCALE_OFFSET);
+			writel(value, kp->base + PWM_CONTROL_OFFSET);
+		}
 
-	kona_pwmc_apply_settings(kp, chan);
+		kona_pwmc_apply_settings(kp, chan);
+	} else if (cstate.enabled) {
+		kona_pwmc_disable(chip, pwm);
+	}
 
-	clk_disable_unprepare(kp->clk);
+	return 0;
 }
 
 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,
 };