Patchwork [PATCHv3,1/3] pwm: make the PWM_POLARITY flag in DTB optional

login
register
mail settings
Submitter Lothar Waßmann
Date March 28, 2014, 8:48 a.m.
Message ID <1395996540-10999-2-git-send-email-LW@KARO-electronics.de>
Download mbox | patch
Permalink /patch/334616/
State Superseded
Headers show

Comments

Lothar Waßmann - March 28, 2014, 8:48 a.m.
Change the pwm chip driver registration, so that a chip driver that
supports polarity inversion can still be used with DTBs that don't
provide the 'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx
driver without having to modify all existing DTS files.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/pwm/core.c |   46 ++++++++++++++++------------------------------
 1 file changed, 16 insertions(+), 30 deletions(-)
Sascha Hauer - April 2, 2014, 5:53 a.m.
On Fri, Mar 28, 2014 at 09:48:58AM +0100, Lothar Waßmann wrote:
> Change the pwm chip driver registration, so that a chip driver that
> supports polarity inversion can still be used with DTBs that don't
> provide the 'PWM_POLARITY' flag.
> 
> This is done to provide polarity inversion support for the pwm-imx
> driver without having to modify all existing DTS files.
> 
> Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
> ---
>  drivers/pwm/core.c |   46 ++++++++++++++++------------------------------
>  1 file changed, 16 insertions(+), 30 deletions(-)
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index a804713..dc15543 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -131,12 +131,12 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
>  	return 0;
>  }
>  
> -struct pwm_device *
> -of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
> +struct pwm_device *of_pwm_xlate(struct pwm_chip *pc,
> +				const struct of_phandle_args *args)
>  {
>  	struct pwm_device *pwm;
>  
> -	if (pc->of_pwm_n_cells < 3)
> +	if (pc->of_pwm_n_cells < 2)
>  		return ERR_PTR(-EINVAL);
>  
>  	if (args->args[0] >= pc->npwm)
> @@ -148,6 +148,9 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
>  
>  	pwm_set_period(pwm, args->args[1]);
>  
> +	if (pc->of_pwm_n_cells < 3)
> +		return pwm;
> +
>  	if (args->args[2] & PWM_POLARITY_INVERTED)
>  		pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);
>  	else
> @@ -155,27 +158,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
>  
>  	return pwm;
>  }
> -EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
>  
> -static struct pwm_device *
> -of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
> +struct pwm_device *
> +of_pwm_xlate_with_flags(struct pwm_chip *pc,
> +			const struct of_phandle_args *args)
>  {
> -	struct pwm_device *pwm;
> -
> -	if (pc->of_pwm_n_cells < 2)
> -		return ERR_PTR(-EINVAL);
> -
> -	if (args->args[0] >= pc->npwm)
> -		return ERR_PTR(-EINVAL);
> -
> -	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
> -	if (IS_ERR(pwm))
> -		return pwm;
> -
> -	pwm_set_period(pwm, args->args[1]);
> -
> -	return pwm;
> +	return of_pwm_xlate(pc, args);
>  }
> +EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
>  
>  static void of_pwmchip_add(struct pwm_chip *chip)
>  {
> @@ -183,8 +173,11 @@ static void of_pwmchip_add(struct pwm_chip *chip)
>  		return;
>  
>  	if (!chip->of_xlate) {
> -		chip->of_xlate = of_pwm_simple_xlate;
> -		chip->of_pwm_n_cells = 2;
> +		chip->of_xlate = of_pwm_xlate;
> +		if (chip->ops->set_polarity)
> +			chip->of_pwm_n_cells = 3;
> +		else
> +			chip->of_pwm_n_cells = 2;

I think the presence of the set_polarity callback shouldn't influence
the number of cells the parser expects. As commented on 2/2 this doesn't
actually mean the device actually support polarity inversion. Also,
polarity inversion could still be done in software for hardware that
doesn't support it.

Sascha
Thierry Reding - April 7, 2014, 11:36 a.m.
On Wed, Apr 02, 2014 at 07:53:50AM +0200, Sascha Hauer wrote:
> On Fri, Mar 28, 2014 at 09:48:58AM +0100, Lothar Waßmann wrote:
[...]
> > @@ -183,8 +173,11 @@ static void of_pwmchip_add(struct pwm_chip *chip)
> >  		return;
> >  
> >  	if (!chip->of_xlate) {
> > -		chip->of_xlate = of_pwm_simple_xlate;
> > -		chip->of_pwm_n_cells = 2;
> > +		chip->of_xlate = of_pwm_xlate;
> > +		if (chip->ops->set_polarity)
> > +			chip->of_pwm_n_cells = 3;
> > +		else
> > +			chip->of_pwm_n_cells = 2;
> 
> I think the presence of the set_polarity callback shouldn't influence
> the number of cells the parser expects. As commented on 2/2 this doesn't
> actually mean the device actually support polarity inversion.

How so? A driver should only implement .set_polarity() if it supports
changing the polarity.

That said, I agree that the presence of .set_polarity() shouldn't
determine the number of cells. You could have any number of other flags
set via the third cell.

> Also, polarity inversion could still be done in software for hardware
> that doesn't support it.

No. You cannot emulate polarity inversion in software.

Thierry
Lothar Waßmann - April 8, 2014, 5:02 a.m.
Hi,

Thierry Reding wrote:
> On Wed, Apr 02, 2014 at 07:53:50AM +0200, Sascha Hauer wrote:
> > On Fri, Mar 28, 2014 at 09:48:58AM +0100, Lothar Waßmann wrote:
> [...]
> > > @@ -183,8 +173,11 @@ static void of_pwmchip_add(struct pwm_chip *chip)
> > >  		return;
> > >  
> > >  	if (!chip->of_xlate) {
> > > -		chip->of_xlate = of_pwm_simple_xlate;
> > > -		chip->of_pwm_n_cells = 2;
> > > +		chip->of_xlate = of_pwm_xlate;
> > > +		if (chip->ops->set_polarity)
> > > +			chip->of_pwm_n_cells = 3;
> > > +		else
> > > +			chip->of_pwm_n_cells = 2;
> > 
> > I think the presence of the set_polarity callback shouldn't influence
> > the number of cells the parser expects. As commented on 2/2 this doesn't
> > actually mean the device actually support polarity inversion.
> 
> How so? A driver should only implement .set_polarity() if it supports
> changing the polarity.
> 
> That said, I agree that the presence of .set_polarity() shouldn't
> determine the number of cells. You could have any number of other flags
> set via the third cell.
> 
> > Also, polarity inversion could still be done in software for hardware
> > that doesn't support it.
> 
> No. You cannot emulate polarity inversion in software.
> 
Why not?

duty_ns = period_ns - duty_ns;


Lothar Waßmann
Tim Kryger - April 8, 2014, 8:43 p.m.
On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de> wrote:
> Thierry Reding wrote:

>> No. You cannot emulate polarity inversion in software.
>>
> Why not?
>
> duty_ns = period_ns - duty_ns;

Since I made the same mistake, I will pass along the pointer Thierry gave me.

In include/linux/pwm.h the second difference for an inverted signal is
described.

/**
 * enum pwm_polarity - polarity of a PWM signal
 * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
 * cycle, followed by a low signal for the remainder of the pulse
 * period
 * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
 * cycle, followed by a high signal for the remainder of the pulse
 * period
 */
enum pwm_polarity {
PWM_POLARITY_NORMAL,
PWM_POLARITY_INVERSED,
};

Of course, I suspect not all PWM hardware respects this definition of
inverted output.

Either way, hacking the duty in software certainly would get the
high/low order wrong.

-Tim
--
To unsubscribe from this list: send the line "unsubscribe linux-pwm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Lothar Waßmann - April 9, 2014, 6:04 a.m.
Hi,

Tim Kryger wrote:
> On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de>wrote:
> >
> >
> > Thierry Reding wrote:
> >
> > > No. You cannot emulate polarity inversion in software.
> > >
> > Why not?
> >
> > duty_ns = period_ns - duty_ns;
> >
> 
> Since I made the same mistake, I will pass along the pointer Thierry gave
> me.
> 
> In include/linux/pwm.h the second difference for an inverted signal is
> described.
> 
> /**
>  * enum pwm_polarity - polarity of a PWM signal
>  * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
>  * cycle, followed by a low signal for the remainder of the pulse
>  * period
>  * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
>  * cycle, followed by a high signal for the remainder of the pulse
>  * period
>  */
> enum pwm_polarity {
> PWM_POLARITY_NORMAL,
> PWM_POLARITY_INVERSED,
> };
> 
> Of course, I suspect not all PWM hardware respects this definition of
> inverted output.
> 
> Either way, hacking the duty in software certainly would get the high/low
> order wrong.
> 
OK. But for a periodic signal this doesn't make any difference. It's
just a matter of where you set your reference point.
Only if you program the PWM to create a single cycle you would see the
difference. I wonder if this is a real life usecase though.


Lothar Waßmann
Sascha Hauer - April 9, 2014, 6:12 a.m.
On Tue, Apr 08, 2014 at 01:43:22PM -0700, Tim Kryger wrote:
> On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de> wrote:
> > Thierry Reding wrote:
> 
> >> No. You cannot emulate polarity inversion in software.
> >>
> > Why not?
> >
> > duty_ns = period_ns - duty_ns;
> 
> Since I made the same mistake, I will pass along the pointer Thierry gave me.
> 
> In include/linux/pwm.h the second difference for an inverted signal is
> described.
> 
> /**
>  * enum pwm_polarity - polarity of a PWM signal
>  * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
>  * cycle, followed by a low signal for the remainder of the pulse
>  * period
>  * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
>  * cycle, followed by a high signal for the remainder of the pulse
>  * period
>  */
> enum pwm_polarity {
> PWM_POLARITY_NORMAL,
> PWM_POLARITY_INVERSED,
> };
> 
> Of course, I suspect not all PWM hardware respects this definition of
> inverted output.
> 
> Either way, hacking the duty in software certainly would get the
> high/low order wrong.

This only relevant if you have some reference signal the PWM must be
relative to, for example if you combine multiple PWMs for motor control.
For PWMs used for backlight or beepers a signal inversion in software is
perfectly fine. And I also think that it makes sense to put it once into
the framework instead of bothering all consumer drivers with the
inversion.

Sascha
Thierry Reding - April 9, 2014, 7:16 a.m.
On Wed, Apr 09, 2014 at 08:04:50AM +0200, Lothar Waßmann wrote:
> Hi,
> 
> Tim Kryger wrote:
> > On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de>wrote:
> > >
> > >
> > > Thierry Reding wrote:
> > >
> > > > No. You cannot emulate polarity inversion in software.
> > > >
> > > Why not?
> > >
> > > duty_ns = period_ns - duty_ns;
> > >
> > 
> > Since I made the same mistake, I will pass along the pointer Thierry gave
> > me.
> > 
> > In include/linux/pwm.h the second difference for an inverted signal is
> > described.
> > 
> > /**
> >  * enum pwm_polarity - polarity of a PWM signal
> >  * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
> >  * cycle, followed by a low signal for the remainder of the pulse
> >  * period
> >  * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
> >  * cycle, followed by a high signal for the remainder of the pulse
> >  * period
> >  */
> > enum pwm_polarity {
> > PWM_POLARITY_NORMAL,
> > PWM_POLARITY_INVERSED,
> > };
> > 
> > Of course, I suspect not all PWM hardware respects this definition of
> > inverted output.
> > 
> > Either way, hacking the duty in software certainly would get the high/low
> > order wrong.
> > 
> OK. But for a periodic signal this doesn't make any difference. It's
> just a matter of where you set your reference point.
> Only if you program the PWM to create a single cycle you would see the
> difference. I wonder if this is a real life usecase though.

It doesn't make a difference if all you're concerned about is the signal
power (which happens to be the case for LED and backlight use-cases).

Currently any in-kernel users only care about the signal power, but
there's no guarantee that it will stay that way. Furthermore, PWM
channels are exposed to userspace via sysfs, so code that we don't
actually see may rely on this behaviour.

Thierry
Thierry Reding - April 9, 2014, 7:22 a.m.
On Wed, Apr 09, 2014 at 08:12:09AM +0200, Sascha Hauer wrote:
> On Tue, Apr 08, 2014 at 01:43:22PM -0700, Tim Kryger wrote:
> > On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de> wrote:
> > > Thierry Reding wrote:
> > 
> > >> No. You cannot emulate polarity inversion in software.
> > >>
> > > Why not?
> > >
> > > duty_ns = period_ns - duty_ns;
> > 
> > Since I made the same mistake, I will pass along the pointer Thierry gave me.
> > 
> > In include/linux/pwm.h the second difference for an inverted signal is
> > described.
> > 
> > /**
> >  * enum pwm_polarity - polarity of a PWM signal
> >  * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
> >  * cycle, followed by a low signal for the remainder of the pulse
> >  * period
> >  * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
> >  * cycle, followed by a high signal for the remainder of the pulse
> >  * period
> >  */
> > enum pwm_polarity {
> > PWM_POLARITY_NORMAL,
> > PWM_POLARITY_INVERSED,
> > };
> > 
> > Of course, I suspect not all PWM hardware respects this definition of
> > inverted output.
> > 
> > Either way, hacking the duty in software certainly would get the
> > high/low order wrong.
> 
> This only relevant if you have some reference signal the PWM must be
> relative to, for example if you combine multiple PWMs for motor control.
> For PWMs used for backlight or beepers a signal inversion in software is
> perfectly fine. And I also think that it makes sense to put it once into
> the framework instead of bothering all consumer drivers with the
> inversion.

The PWM framework itself doesn't have enough knowledge about what a PWM
is being used for. Therefore it cannot determine whether emulating
polarity inversion by reversing the duty cycle will be appropriate.

Putting such functionality into the core will prevent PWM channels from
being used for cases where the signal polarity does matter.

Thierry
Sascha Hauer - April 10, 2014, 5:55 a.m.
On Wed, Apr 09, 2014 at 09:22:50AM +0200, Thierry Reding wrote:
> On Wed, Apr 09, 2014 at 08:12:09AM +0200, Sascha Hauer wrote:
> > On Tue, Apr 08, 2014 at 01:43:22PM -0700, Tim Kryger wrote:
> > > On Mon, Apr 7, 2014 at 10:02 PM, Lothar Waßmann <LW@karo-electronics.de> wrote:
> > > > Thierry Reding wrote:
> > > 
> > > >> No. You cannot emulate polarity inversion in software.
> > > >>
> > > > Why not?
> > > >
> > > > duty_ns = period_ns - duty_ns;
> > > 
> > > Since I made the same mistake, I will pass along the pointer Thierry gave me.
> > > 
> > > In include/linux/pwm.h the second difference for an inverted signal is
> > > described.
> > > 
> > > /**
> > >  * enum pwm_polarity - polarity of a PWM signal
> > >  * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
> > >  * cycle, followed by a low signal for the remainder of the pulse
> > >  * period
> > >  * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
> > >  * cycle, followed by a high signal for the remainder of the pulse
> > >  * period
> > >  */
> > > enum pwm_polarity {
> > > PWM_POLARITY_NORMAL,
> > > PWM_POLARITY_INVERSED,
> > > };
> > > 
> > > Of course, I suspect not all PWM hardware respects this definition of
> > > inverted output.
> > > 
> > > Either way, hacking the duty in software certainly would get the
> > > high/low order wrong.
> > 
> > This only relevant if you have some reference signal the PWM must be
> > relative to, for example if you combine multiple PWMs for motor control.
> > For PWMs used for backlight or beepers a signal inversion in software is
> > perfectly fine. And I also think that it makes sense to put it once into
> > the framework instead of bothering all consumer drivers with the
> > inversion.
> 
> The PWM framework itself doesn't have enough knowledge about what a PWM
> is being used for. Therefore it cannot determine whether emulating
> polarity inversion by reversing the duty cycle will be appropriate.
> 
> Putting such functionality into the core will prevent PWM channels from
> being used for cases where the signal polarity does matter

The PWM core is in no way prepared for handling such situations. Should
we want to add support it a PWM_POLARITY_INVERSED flag would be the
least of our problems. It could be renamed to
PWM_POLARITY_INVERSED_ASYNC for the beeper/led drivers which do not need
synchronization.

Sascha

Patch

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index a804713..dc15543 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -131,12 +131,12 @@  static int pwm_device_request(struct pwm_device *pwm, const char *label)
 	return 0;
 }
 
-struct pwm_device *
-of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
+struct pwm_device *of_pwm_xlate(struct pwm_chip *pc,
+				const struct of_phandle_args *args)
 {
 	struct pwm_device *pwm;
 
-	if (pc->of_pwm_n_cells < 3)
+	if (pc->of_pwm_n_cells < 2)
 		return ERR_PTR(-EINVAL);
 
 	if (args->args[0] >= pc->npwm)
@@ -148,6 +148,9 @@  of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
 
 	pwm_set_period(pwm, args->args[1]);
 
+	if (pc->of_pwm_n_cells < 3)
+		return pwm;
+
 	if (args->args[2] & PWM_POLARITY_INVERTED)
 		pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);
 	else
@@ -155,27 +158,14 @@  of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
 
 	return pwm;
 }
-EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
 
-static struct pwm_device *
-of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
+struct pwm_device *
+of_pwm_xlate_with_flags(struct pwm_chip *pc,
+			const struct of_phandle_args *args)
 {
-	struct pwm_device *pwm;
-
-	if (pc->of_pwm_n_cells < 2)
-		return ERR_PTR(-EINVAL);
-
-	if (args->args[0] >= pc->npwm)
-		return ERR_PTR(-EINVAL);
-
-	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
-	if (IS_ERR(pwm))
-		return pwm;
-
-	pwm_set_period(pwm, args->args[1]);
-
-	return pwm;
+	return of_pwm_xlate(pc, args);
 }
+EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
 
 static void of_pwmchip_add(struct pwm_chip *chip)
 {
@@ -183,8 +173,11 @@  static void of_pwmchip_add(struct pwm_chip *chip)
 		return;
 
 	if (!chip->of_xlate) {
-		chip->of_xlate = of_pwm_simple_xlate;
-		chip->of_pwm_n_cells = 2;
+		chip->of_xlate = of_pwm_xlate;
+		if (chip->ops->set_polarity)
+			chip->of_pwm_n_cells = 3;
+		else
+			chip->of_pwm_n_cells = 2;
 	}
 
 	of_node_get(chip->dev->of_node);
@@ -536,13 +529,6 @@  struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
 		goto put;
 	}
 
-	if (args.args_count != pc->of_pwm_n_cells) {
-		pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
-			 args.np->full_name);
-		pwm = ERR_PTR(-EINVAL);
-		goto put;
-	}
-
 	pwm = pc->of_xlate(pc, &args);
 	if (IS_ERR(pwm))
 		goto put;