linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alexandre Belloni <alexandre.belloni@free-electrons.com>
To: Thierry Reding <thierry.reding@gmail.com>,
	Olliver Schinagl <o.schinagl@ultimaker.com>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>,
	Chen-Yu Tsai <wens@csie.org>,
	Boris Brezillon <boris.brezillon@free-electrons.com>,
	linux-pwm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] pwm: sun4i: switch to atomic PWM
Date: Fri, 28 Apr 2017 15:34:40 +0200	[thread overview]
Message-ID: <20170428133440.sxeyrcmkqfwqg6fm@piout.net> (raw)
In-Reply-To: <20170427220002.20266-1-alexandre.belloni@free-electrons.com>

Hi,

On 28/04/2017 at 00:00:02 +0200, Alexandre Belloni wrote:
> +static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> +			   struct pwm_state *state)
>  {
>  	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
> -	u32 val;
> -	int ret;
> -
> -	ret = clk_prepare_enable(sun4i_pwm->clk);
> -	if (ret) {
> -		dev_err(chip->dev, "failed to enable PWM clock\n");
> -		return ret;
> +	struct pwm_state cstate;
> +	u32 ctrl;
> +	int delay_us, ret;
> +	bool needs_delay = false, prescaler_changed = false;
> +
> +	pwm_get_state(pwm, &cstate);
> +
> +	if (!cstate.enabled) {
> +		ret = clk_prepare_enable(sun4i_pwm->clk);
> +		if (ret) {
> +			dev_err(chip->dev, "failed to enable PWM clock\n");
> +			return ret;
> +		}
>  	}
>  
>  	spin_lock(&sun4i_pwm->ctrl_lock);
> -	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
> +	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
>  
> -	if (polarity != PWM_POLARITY_NORMAL)
> -		val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
> -	else
> -		val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
> +	if ((cstate.period != state->period) ||
> +	    (cstate.duty_cycle != state->duty_cycle)) {
> +		u32 period, duty, val;
> +		unsigned int prescaler;
>  
> -	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
> +		needs_delay = true;
>  
> -	spin_unlock(&sun4i_pwm->ctrl_lock);
> -	clk_disable_unprepare(sun4i_pwm->clk);
> +		ret = sun4i_pwm_calculate(sun4i_pwm, state,
> +					  &duty, &period, &prescaler);
> +		if (ret) {
> +			dev_err(chip->dev, "period exceeds the maximum value\n");
> +			spin_unlock(&sun4i_pwm->ctrl_lock);
> +			if (!cstate.enabled)
> +				clk_disable_unprepare(sun4i_pwm->clk);
> +			return ret;
> +		}
>  
> -	return 0;
> -}
> +		if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
> +			prescaler_changed = true;
> +			/* Prescaler changed, the clock has to be gated */
> +			ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> +			sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
>  
> -static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
> -	u32 val;
> -	int ret;
> +			ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
> +			ctrl |= BIT_CH(prescaler, pwm->hwpwm);
> +		}
>  
> -	ret = clk_prepare_enable(sun4i_pwm->clk);
> -	if (ret) {
> -		dev_err(chip->dev, "failed to enable PWM clock\n");
> -		return ret;
> +		val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
> +		sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
>  	}
>  
> -	spin_lock(&sun4i_pwm->ctrl_lock);
> -	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
> -	val |= BIT_CH(PWM_EN, pwm->hwpwm);
> -	val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> -	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
> -	spin_unlock(&sun4i_pwm->ctrl_lock);
> -
> -	return 0;
> -}
> +	if (state->polarity != PWM_POLARITY_NORMAL)
> +		ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
> +	else
> +		ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
> +
> +	ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> +	if (state->enabled) {
> +		ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
> +	} else if (!needs_delay) {
> +		ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
> +		ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> +	}
>  
> -static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
> -	u32 val;
> +	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
>  
> -	spin_lock(&sun4i_pwm->ctrl_lock);
> -	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
> -	val &= ~BIT_CH(PWM_EN, pwm->hwpwm);
> -	val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> -	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
>  	spin_unlock(&sun4i_pwm->ctrl_lock);
>  
> -	clk_disable_unprepare(sun4i_pwm->clk);
> +	if (!needs_delay)
> +		return 0;
> +
> +	/* We need a full (previous) period to elapse before disabling the
> +	 * channel. If a ready bit is available, wait for it instead of waiting
> +	 * for a full period.
> +	 *
> +	 * If the new period is greater than the previous one, the prescaler may
> +	 * have changed and the previous period may go slower.
> +	 *
> +	 */
> +	delay_us = max(state->period / 1000 + 1, cstate.period / 1000 + 1);
> +	if ((cstate.enabled && !state->enabled) || !sun4i_pwm->data->has_rdy)

This condition doesn't always work as expected if the non atomic path is
used (using sysfs for example). I'll resubmit.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

      parent reply	other threads:[~2017-04-28 13:35 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-27 22:00 [PATCH] pwm: sun4i: switch to atomic PWM Alexandre Belloni
2017-04-28  9:06 ` Maxime Ripard
2017-04-28 13:34 ` Alexandre Belloni [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170428133440.sxeyrcmkqfwqg6fm@piout.net \
    --to=alexandre.belloni@free-electrons.com \
    --cc=boris.brezillon@free-electrons.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=maxime.ripard@free-electrons.com \
    --cc=o.schinagl@ultimaker.com \
    --cc=thierry.reding@gmail.com \
    --cc=wens@csie.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).