diff mbox

[PATCHv2,2/2] pwm: imx: support polarity inversion

Message ID 1389859585-14006-3-git-send-email-LW@KARO-electronics.de
State New
Headers show

Commit Message

Lothar Waßmann Jan. 16, 2014, 8:06 a.m. UTC
The i.MX PWM controller supports inverting the polarity of the PWM
output. Make this feature available in the pxm-imx driver.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt |    5 +-
 drivers/pwm/pwm-imx.c                             |   42 +++++++++++++++++++++
 2 files changed, 45 insertions(+), 2 deletions(-)

Comments

Sascha Hauer Jan. 16, 2014, 4:03 p.m. UTC | #1
On Thu, Jan 16, 2014 at 09:06:25AM +0100, Lothar Waßmann wrote:
> The i.MX PWM controller supports inverting the polarity of the PWM
> output. Make this feature available in the pxm-imx driver.
> 
> Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
> ---
>  Documentation/devicetree/bindings/pwm/imx-pwm.txt |    5 +-
>  drivers/pwm/pwm-imx.c                             |   42 +++++++++++++++++++++
>  2 files changed, 45 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> index b50d7a6d..d0b04b5 100644
> --- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> @@ -3,8 +3,9 @@ Freescale i.MX PWM controller
>  Required properties:
>  - compatible: should be "fsl,<soc>-pwm"
>  - reg: physical base address and length of the controller's registers
> -- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
> -  the cells format.
> +- #pwm-cells: may be 2 for backwards compatibility or 3 to support
> +  switching the output polarity. See pwm.txt in this directory for a
> +  description of the cells format.
>  - interrupts: The interrupt for the pwm controller
>  
>  Example:
> diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
> index 3b00a82..05461bb 100644
> --- a/drivers/pwm/pwm-imx.c
> +++ b/drivers/pwm/pwm-imx.c
> @@ -36,6 +36,7 @@
>  #define MX3_PWMCR_DOZEEN		(1 << 24)
>  #define MX3_PWMCR_WAITEN		(1 << 23)
>  #define MX3_PWMCR_DBGEN			(1 << 22)
> +#define MX3_PWMCR_POUTC			(1 << 18)
>  #define MX3_PWMCR_CLKSRC_IPG_HIGH	(2 << 16)
>  #define MX3_PWMCR_CLKSRC_IPG		(1 << 16)
>  #define MX3_PWMCR_EN			(1 << 0)
> @@ -138,6 +139,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
>  	if (test_bit(PWMF_ENABLED, &pwm->flags))
>  		cr |= MX3_PWMCR_EN;
>  
> +	if (pwm->polarity == PWM_POLARITY_INVERSED)
> +		cr |= MX3_PWMCR_POUTC;
> +
>  	writel(cr, imx->mmio_base + MX3_PWMCR);
>  
>  	return 0;
> @@ -155,6 +159,11 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
>  	else
>  		val &= ~MX3_PWMCR_EN;
>  
> +	if (chip->pwms[0].polarity == PWM_POLARITY_INVERSED)
> +		val |= MX3_PWMCR_POUTC;
> +	else
> +		val &= ~MX3_PWMCR_POUTC;
> +
>  	writel(val, imx->mmio_base + MX3_PWMCR);
>  }
>  
> @@ -198,6 +207,17 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>  	clk_disable_unprepare(imx->clk_per);
>  }
>  
> +static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
> +				enum pwm_polarity polarity)
> +{
> +	struct imx_chip *imx = to_imx_chip(chip);
> +
> +	dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
> +		polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
> +
> +	return 0;
> +}
> +
>  static struct pwm_ops imx_pwm_ops = {
>  	.enable = imx_pwm_enable,
>  	.disable = imx_pwm_disable,
> @@ -209,6 +229,7 @@ struct imx_pwm_data {
>  	int (*config)(struct pwm_chip *chip,
>  		struct pwm_device *pwm, int duty_ns, int period_ns);
>  	void (*set_enable)(struct pwm_chip *chip, bool enable);
> +	unsigned output_polarity:1;
>  };
>  
>  static struct imx_pwm_data imx_pwm_data_v1 = {
> @@ -219,6 +240,7 @@ static struct imx_pwm_data imx_pwm_data_v1 = {
>  static struct imx_pwm_data imx_pwm_data_v2 = {
>  	.config = imx_pwm_config_v2,
>  	.set_enable = imx_pwm_set_enable_v2,
> +	.output_polarity = 1,
>  };
>  
>  static const struct of_device_id imx_pwm_dt_ids[] = {
> @@ -271,6 +293,26 @@ static int imx_pwm_probe(struct platform_device *pdev)
>  		return PTR_ERR(imx->mmio_base);
>  
>  	data = of_id->data;
> +	if (data->output_polarity) {
> +		const struct device_node *np = pdev->dev.of_node;
> +		u32 num_cells;
> +
> +		dev_dbg(&pdev->dev, "PWM supports output inversion\n");
> +		ret = of_property_read_u32(np, "#pwm-cells", &num_cells);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "missing property '#pwm-cells'\n");
> +			return ret;
> +		}
> +		if (num_cells == 3) {
> +			imx_pwm_ops.set_polarity = imx_pwm_set_polarity;
> +			imx->chip.of_xlate = of_pwm_xlate_with_flags;
> +		} else if (num_cells != 2) {
> +			dev_err(&pdev->dev, "'#pwm-cells' must be <2> or <3>\n");
> +			return -EINVAL;
> +		}
> +		imx->chip.of_pwm_n_cells = num_cells;
> +	}

Can't this be done in the PWM core? Right now the PWM core checks for
of_pwm_n_cells before calling ->of_xlate. IMO this check should be done
in the of_xlate hook. Then of_pwm_simple_xlate and
of_pwm_xlate_with_flags can be merged into something like:

static struct pwm_device *
of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args
*args)
{
	struct pwm_device *pwm;
	int ret;

	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;

	if (pc->of_pwm_n_cells >= 3) {
		if (args->args[2] & PWM_POLARITY_INVERTED)
			ret = pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);
		else
			ret = pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);
		if (ret)
			return ret;
	}

	pwm_set_period(pwm, args->args[1]);

	return pwm;
}

Sascha
Lothar Waßmann Jan. 23, 2014, 7:37 a.m. UTC | #2
Hi,

Sascha Hauer wrote:
> On Thu, Jan 16, 2014 at 09:06:25AM +0100, Lothar Waßmann wrote:
> > The i.MX PWM controller supports inverting the polarity of the PWM
> > output. Make this feature available in the pxm-imx driver.
> > 
> > Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
> > ---
> >  Documentation/devicetree/bindings/pwm/imx-pwm.txt |    5 +-
> >  drivers/pwm/pwm-imx.c                             |   42 +++++++++++++++++++++
> >  2 files changed, 45 insertions(+), 2 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> > index b50d7a6d..d0b04b5 100644
> > --- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> > +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> > @@ -3,8 +3,9 @@ Freescale i.MX PWM controller
> >  Required properties:
> >  - compatible: should be "fsl,<soc>-pwm"
> >  - reg: physical base address and length of the controller's registers
> > -- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
> > -  the cells format.
> > +- #pwm-cells: may be 2 for backwards compatibility or 3 to support
> > +  switching the output polarity. See pwm.txt in this directory for a
> > +  description of the cells format.
> >  - interrupts: The interrupt for the pwm controller
> >  
> >  Example:
> > diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
> > index 3b00a82..05461bb 100644
> > --- a/drivers/pwm/pwm-imx.c
> > +++ b/drivers/pwm/pwm-imx.c
> > @@ -36,6 +36,7 @@
> >  #define MX3_PWMCR_DOZEEN		(1 << 24)
> >  #define MX3_PWMCR_WAITEN		(1 << 23)
> >  #define MX3_PWMCR_DBGEN			(1 << 22)
> > +#define MX3_PWMCR_POUTC			(1 << 18)
> >  #define MX3_PWMCR_CLKSRC_IPG_HIGH	(2 << 16)
> >  #define MX3_PWMCR_CLKSRC_IPG		(1 << 16)
> >  #define MX3_PWMCR_EN			(1 << 0)
> > @@ -138,6 +139,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
> >  	if (test_bit(PWMF_ENABLED, &pwm->flags))
> >  		cr |= MX3_PWMCR_EN;
> >  
> > +	if (pwm->polarity == PWM_POLARITY_INVERSED)
> > +		cr |= MX3_PWMCR_POUTC;
> > +
> >  	writel(cr, imx->mmio_base + MX3_PWMCR);
> >  
> >  	return 0;
> > @@ -155,6 +159,11 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
> >  	else
> >  		val &= ~MX3_PWMCR_EN;
> >  
> > +	if (chip->pwms[0].polarity == PWM_POLARITY_INVERSED)
> > +		val |= MX3_PWMCR_POUTC;
> > +	else
> > +		val &= ~MX3_PWMCR_POUTC;
> > +
> >  	writel(val, imx->mmio_base + MX3_PWMCR);
> >  }
> >  
> > @@ -198,6 +207,17 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> >  	clk_disable_unprepare(imx->clk_per);
> >  }
> >  
> > +static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
> > +				enum pwm_polarity polarity)
> > +{
> > +	struct imx_chip *imx = to_imx_chip(chip);
> > +
> > +	dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
> > +		polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
> > +
> > +	return 0;
> > +}
> > +
> >  static struct pwm_ops imx_pwm_ops = {
> >  	.enable = imx_pwm_enable,
> >  	.disable = imx_pwm_disable,
> > @@ -209,6 +229,7 @@ struct imx_pwm_data {
> >  	int (*config)(struct pwm_chip *chip,
> >  		struct pwm_device *pwm, int duty_ns, int period_ns);
> >  	void (*set_enable)(struct pwm_chip *chip, bool enable);
> > +	unsigned output_polarity:1;
> >  };
> >  
> >  static struct imx_pwm_data imx_pwm_data_v1 = {
> > @@ -219,6 +240,7 @@ static struct imx_pwm_data imx_pwm_data_v1 = {
> >  static struct imx_pwm_data imx_pwm_data_v2 = {
> >  	.config = imx_pwm_config_v2,
> >  	.set_enable = imx_pwm_set_enable_v2,
> > +	.output_polarity = 1,
> >  };
> >  
> >  static const struct of_device_id imx_pwm_dt_ids[] = {
> > @@ -271,6 +293,26 @@ static int imx_pwm_probe(struct platform_device *pdev)
> >  		return PTR_ERR(imx->mmio_base);
> >  
> >  	data = of_id->data;
> > +	if (data->output_polarity) {
> > +		const struct device_node *np = pdev->dev.of_node;
> > +		u32 num_cells;
> > +
> > +		dev_dbg(&pdev->dev, "PWM supports output inversion\n");
> > +		ret = of_property_read_u32(np, "#pwm-cells", &num_cells);
> > +		if (ret < 0) {
> > +			dev_err(&pdev->dev, "missing property '#pwm-cells'\n");
> > +			return ret;
> > +		}
> > +		if (num_cells == 3) {
> > +			imx_pwm_ops.set_polarity = imx_pwm_set_polarity;
> > +			imx->chip.of_xlate = of_pwm_xlate_with_flags;
> > +		} else if (num_cells != 2) {
> > +			dev_err(&pdev->dev, "'#pwm-cells' must be <2> or <3>\n");
> > +			return -EINVAL;
> > +		}
> > +		imx->chip.of_pwm_n_cells = num_cells;
> > +	}
> 
> Can't this be done in the PWM core? Right now the PWM core checks for
> of_pwm_n_cells before calling ->of_xlate. IMO this check should be done
> in the of_xlate hook. Then of_pwm_simple_xlate and
> of_pwm_xlate_with_flags can be merged into something like:
> 
This wouldn't buy much without a material change to of_pwm_get().
The function of_parse_phandle_with_args() called by of_pwm_get()
requires the number of args in the pwms property be greater or equal to
the #pwm-cells property in the pwm node. Thus, the interesting case of
having #pwm-cells = <3> without changing the existing users is
prohibited by of_parse_phandle_with_args().

of_pwm_get() would have to be changed to something like below to allow
this:
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
{
	struct pwm_device *pwm = NULL;
	struct of_phandle_args args;
	struct pwm_chip *pc;
	int index = 0;
	int err;
	struct property *prop;
	u32 num_cells;
	int i;
	const __be32 *list;

	if (con_id) {
		index = of_property_match_string(np, "pwm-names", con_id);
		if (index < 0)
			return ERR_PTR(index);
	}

	args.np = of_parse_phandle(np, "pwms", index);
	if (!args.np) {
		pr_err("%s(): property 'pwms' not found in '%s'\n",
			__func__, np->full_name);
		return ERR_PTR(-ENOENT);
	}

	err = of_property_read_u32(args.np, "#pwm-cells", &num_cells);
	if (err) {
		pr_err("%s(): could not read property '#pwm-cells' in '%s': %d\n",
			__func__, args.np->full_name, err);
		return ERR_PTR(err);
	}

	prop = of_find_property(np, "pwms", NULL);
	if (WARN_ON(!prop))
		return ERR_PTR(-EINVAL);

	args.args_count = prop->length / sizeof(u32) - 1;
	list = prop->value;
	for (i = 0; i < args.args_count; i++)
		args.args[i] = be32_to_cpup(++list);

	pc = of_node_to_pwmchip(args.np);
	if (IS_ERR(pc)) {
		pr_err("%s(): PWM chip not found\n", __func__);
		pwm = ERR_CAST(pc);
		goto put;
	}
[...]

 

Lothar Waßmann
Sascha Hauer Jan. 23, 2014, 9:04 a.m. UTC | #3
I thinking more of the following. I haven't tested it, but it has a negative
diffstat, so it must be good ;)

Sascha

----------------------------------------------------------------
Sascha Hauer (2):
      PWM: let of_xlate handlers check args count
      PWM: handle additional flags in of_pwm_simple_xlate

 drivers/pwm/core.c            | 41 +++++++----------------------------------
 drivers/pwm/pwm-atmel-tcb.c   |  2 --
 drivers/pwm/pwm-renesas-tpu.c |  2 --
 drivers/pwm/pwm-samsung.c     |  3 ---
 drivers/pwm/pwm-tiecap.c      |  2 --
 drivers/pwm/pwm-tiehrpwm.c    |  2 --
 drivers/pwm/pwm-vt8500.c      |  2 --
 include/linux/pwm.h           |  3 ---
 8 files changed, 7 insertions(+), 50 deletions(-)
Lothar Waßmann Jan. 23, 2014, 10:56 a.m. UTC | #4
Hi,

Sascha Hauer wrote:
> of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> but it is only ever used by the of_xlate handler itsel. Remove
> of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> count checking to simplify the code.
> 
This still does not make the PWM_POLARITY flag in the pwms node
optional as was the goal because of_parse_phandle_with_args() requires
at least #pwm-cells arguments in the node.

So, with a DT configuration like:
pwm0: pwm@0 {
	#pwm-cells = <3>;
};
backlight {
	pwms = <&pwm0 0 100000>;
};
the driver will bail out at of_parse_phandle_with_args() in
of_pwm_get() with the error message:
"/backlight: arguments longer than property" and never reach your
clever xlate function.

Thus you will still need to replace of_parse_phandle_with_args()
with different code that copies most but not all of the functionality.


Lothar Waßmann
Sascha Hauer Jan. 23, 2014, 11:04 a.m. UTC | #5
On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Waßmann wrote:
> Hi,
> 
> Sascha Hauer wrote:
> > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > but it is only ever used by the of_xlate handler itsel. Remove
> > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > count checking to simplify the code.
> > 
> This still does not make the PWM_POLARITY flag in the pwms node
> optional as was the goal because of_parse_phandle_with_args() requires
> at least #pwm-cells arguments in the node.
> 
> So, with a DT configuration like:
> pwm0: pwm@0 {
> 	#pwm-cells = <3>;
> };
> backlight {
> 	pwms = <&pwm0 0 100000>;
> };

We misunderstood each other. My goal was to allow the driver to also
work with old devicetrees which specify #pwm-cells = <2>, not to allow
inconsistent devicetrees like the snippet above.

Sascha
Russell King - ARM Linux Jan. 23, 2014, 12:33 p.m. UTC | #6
On Thu, Jan 23, 2014 at 11:52:03AM +0000, Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 08:37:14AM +0100, Lothar Waßmann wrote:
> > This wouldn't buy much without a material change to of_pwm_get().
> > The function of_parse_phandle_with_args() called by of_pwm_get()
> > requires the number of args in the pwms property be greater or equal to
> > the #pwm-cells property in the pwm node. Thus, the interesting case of
> > having #pwm-cells = <3> without changing the existing users is
> > prohibited by of_parse_phandle_with_args().
> 
> I really don't think that's a problem we need to be concerned with at
> the moment.  What we need is for the kernel to be able to parse files
> with #pwm-cells = <2> with the pwms property containing two arguments,
> and when they're updated to #pwm-cells = <3> with the pwms property
> containing three arguments.
> 
> Yes, that means all the board dt files need to be updated at the same
> time to include the additional argument, but I don't see that as a big
> problem.
> 
> What we do need to do is to adjust the PWM parsing code such that it's
> possible to use either specification without causing any side effects.
> 
> I would test this, but as u-boot is rather fscked at the moment and the
> networking has broken on my cubox-i as a result... and it seems that the
> u-boot developers have pissed off cubox-i u-boot hackers soo much that
> they've dropped u-boot in favour of barebox...

Oh, and another reason... the u-boot video settings are totally and utterly
buggered to the point that it doesn't produce correct timings, and it seems
that u-boot people have zero interest in fixing that, so u-boot mainline is
basically refusing to fix this - another reason to stay away from it.

(1024x768 @ 60Hz produces 70Hz refresh on iMX6Q here - I've seen it produce
51Hz on iMX6S, both of which are far enough out that lots of display devices
will not accept it as a valid signal.)
Russell King - ARM Linux Jan. 23, 2014, 4:44 p.m. UTC | #7
On Thu, Jan 23, 2014 at 11:52:03AM +0000, Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 08:37:14AM +0100, Lothar Waßmann wrote:
> > This wouldn't buy much without a material change to of_pwm_get().
> > The function of_parse_phandle_with_args() called by of_pwm_get()
> > requires the number of args in the pwms property be greater or equal to
> > the #pwm-cells property in the pwm node. Thus, the interesting case of
> > having #pwm-cells = <3> without changing the existing users is
> > prohibited by of_parse_phandle_with_args().
> 
> I really don't think that's a problem we need to be concerned with at
> the moment.  What we need is for the kernel to be able to parse files
> with #pwm-cells = <2> with the pwms property containing two arguments,
> and when they're updated to #pwm-cells = <3> with the pwms property
> containing three arguments.
> 
> Yes, that means all the board dt files need to be updated at the same
> time to include the additional argument, but I don't see that as a big
> problem.
> 
> What we do need to do is to adjust the PWM parsing code such that it's
> possible to use either specification without causing any side effects.
> 
> I would test this, but as u-boot is rather fscked at the moment and the
> networking has broken on my cubox-i as a result... and it seems that the
> u-boot developers have pissed off cubox-i u-boot hackers soo much that
> they've dropped u-boot in favour of barebox...

Okay, finally confirmed that this works with #pwm-cells = 2.
Russell King - ARM Linux Jan. 23, 2014, 4:53 p.m. UTC | #8
On Thu, Jan 23, 2014 at 12:04:44PM +0100, Sascha Hauer wrote:
> On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Waßmann wrote:
> > Hi,
> > 
> > Sascha Hauer wrote:
> > > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > > but it is only ever used by the of_xlate handler itsel. Remove
> > > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > > count checking to simplify the code.
> > > 
> > This still does not make the PWM_POLARITY flag in the pwms node
> > optional as was the goal because of_parse_phandle_with_args() requires
> > at least #pwm-cells arguments in the node.
> > 
> > So, with a DT configuration like:
> > pwm0: pwm@0 {
> > 	#pwm-cells = <3>;
> > };
> > backlight {
> > 	pwms = <&pwm0 0 100000>;
> > };
> 
> We misunderstood each other. My goal was to allow the driver to also
> work with old devicetrees which specify #pwm-cells = <2>, not to allow
> inconsistent devicetrees like the snippet above.

In which case, the patch I've posted seems to do that job too... I'm
just about to test out the three-cell version.
Russell King - ARM Linux Jan. 23, 2014, 5:36 p.m. UTC | #9
On Thu, Jan 23, 2014 at 04:53:50PM +0000, Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 12:04:44PM +0100, Sascha Hauer wrote:
> > On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Waßmann wrote:
> > > Hi,
> > > 
> > > Sascha Hauer wrote:
> > > > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > > > but it is only ever used by the of_xlate handler itsel. Remove
> > > > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > > > count checking to simplify the code.
> > > > 
> > > This still does not make the PWM_POLARITY flag in the pwms node
> > > optional as was the goal because of_parse_phandle_with_args() requires
> > > at least #pwm-cells arguments in the node.
> > > 
> > > So, with a DT configuration like:
> > > pwm0: pwm@0 {
> > > 	#pwm-cells = <3>;
> > > };
> > > backlight {
> > > 	pwms = <&pwm0 0 100000>;
> > > };
> > 
> > We misunderstood each other. My goal was to allow the driver to also
> > work with old devicetrees which specify #pwm-cells = <2>, not to allow
> > inconsistent devicetrees like the snippet above.
> 
> In which case, the patch I've posted seems to do that job too... I'm
> just about to test out the three-cell version.

Okay, this works, but there's a problem with pwm-leds.

When the duty cycle is set to zero (when you set the brightness to zero)
pwm-leds decides to disable the PWM after configuring it.  This causes
the PWM output to be driven low, causing the LED to go to maximum
brightness.

So, using the inversion at PWM level doesn't work.

To make this work correctly, we really need pwm-leds to do the inversion
rather than setting the inversion bit in hardware.
Lothar Waßmann Jan. 24, 2014, 5:42 a.m. UTC | #10
Hi,

Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 04:53:50PM +0000, Russell King - ARM Linux wrote:
> > On Thu, Jan 23, 2014 at 12:04:44PM +0100, Sascha Hauer wrote:
> > > On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Waßmann wrote:
> > > > Hi,
> > > > 
> > > > Sascha Hauer wrote:
> > > > > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > > > > but it is only ever used by the of_xlate handler itsel. Remove
> > > > > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > > > > count checking to simplify the code.
> > > > > 
> > > > This still does not make the PWM_POLARITY flag in the pwms node
> > > > optional as was the goal because of_parse_phandle_with_args() requires
> > > > at least #pwm-cells arguments in the node.
> > > > 
> > > > So, with a DT configuration like:
> > > > pwm0: pwm@0 {
> > > > 	#pwm-cells = <3>;
> > > > };
> > > > backlight {
> > > > 	pwms = <&pwm0 0 100000>;
> > > > };
> > > 
> > > We misunderstood each other. My goal was to allow the driver to also
> > > work with old devicetrees which specify #pwm-cells = <2>, not to allow
> > > inconsistent devicetrees like the snippet above.
> > 
> > In which case, the patch I've posted seems to do that job too... I'm
> > just about to test out the three-cell version.
> 
> Okay, this works, but there's a problem with pwm-leds.
> 
> When the duty cycle is set to zero (when you set the brightness to zero)
> pwm-leds decides to disable the PWM after configuring it.  This causes
> the PWM output to be driven low, causing the LED to go to maximum
> brightness.
> 
> So, using the inversion at PWM level doesn't work.
> 
The problem is that the driver calls pwm_disable() when the duty cycle is 0.
This sets the PWM output low independent from the output polarity setting.

> To make this work correctly, we really need pwm-leds to do the inversion
> rather than setting the inversion bit in hardware.
> 
The same holds for the pwm-backlight driver.

The easiest fix would be not to call pwm_disable() even for a zero duty
cycle.


Lothar Waßmann
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index b50d7a6d..d0b04b5 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -3,8 +3,9 @@  Freescale i.MX PWM controller
 Required properties:
 - compatible: should be "fsl,<soc>-pwm"
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: may be 2 for backwards compatibility or 3 to support
+  switching the output polarity. See pwm.txt in this directory for a
+  description of the cells format.
 - interrupts: The interrupt for the pwm controller
 
 Example:
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 3b00a82..05461bb 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -36,6 +36,7 @@ 
 #define MX3_PWMCR_DOZEEN		(1 << 24)
 #define MX3_PWMCR_WAITEN		(1 << 23)
 #define MX3_PWMCR_DBGEN			(1 << 22)
+#define MX3_PWMCR_POUTC			(1 << 18)
 #define MX3_PWMCR_CLKSRC_IPG_HIGH	(2 << 16)
 #define MX3_PWMCR_CLKSRC_IPG		(1 << 16)
 #define MX3_PWMCR_EN			(1 << 0)
@@ -138,6 +139,9 @@  static int imx_pwm_config_v2(struct pwm_chip *chip,
 	if (test_bit(PWMF_ENABLED, &pwm->flags))
 		cr |= MX3_PWMCR_EN;
 
+	if (pwm->polarity == PWM_POLARITY_INVERSED)
+		cr |= MX3_PWMCR_POUTC;
+
 	writel(cr, imx->mmio_base + MX3_PWMCR);
 
 	return 0;
@@ -155,6 +159,11 @@  static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
 	else
 		val &= ~MX3_PWMCR_EN;
 
+	if (chip->pwms[0].polarity == PWM_POLARITY_INVERSED)
+		val |= MX3_PWMCR_POUTC;
+	else
+		val &= ~MX3_PWMCR_POUTC;
+
 	writel(val, imx->mmio_base + MX3_PWMCR);
 }
 
@@ -198,6 +207,17 @@  static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 	clk_disable_unprepare(imx->clk_per);
 }
 
+static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+				enum pwm_polarity polarity)
+{
+	struct imx_chip *imx = to_imx_chip(chip);
+
+	dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
+		polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
+
+	return 0;
+}
+
 static struct pwm_ops imx_pwm_ops = {
 	.enable = imx_pwm_enable,
 	.disable = imx_pwm_disable,
@@ -209,6 +229,7 @@  struct imx_pwm_data {
 	int (*config)(struct pwm_chip *chip,
 		struct pwm_device *pwm, int duty_ns, int period_ns);
 	void (*set_enable)(struct pwm_chip *chip, bool enable);
+	unsigned output_polarity:1;
 };
 
 static struct imx_pwm_data imx_pwm_data_v1 = {
@@ -219,6 +240,7 @@  static struct imx_pwm_data imx_pwm_data_v1 = {
 static struct imx_pwm_data imx_pwm_data_v2 = {
 	.config = imx_pwm_config_v2,
 	.set_enable = imx_pwm_set_enable_v2,
+	.output_polarity = 1,
 };
 
 static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -271,6 +293,26 @@  static int imx_pwm_probe(struct platform_device *pdev)
 		return PTR_ERR(imx->mmio_base);
 
 	data = of_id->data;
+	if (data->output_polarity) {
+		const struct device_node *np = pdev->dev.of_node;
+		u32 num_cells;
+
+		dev_dbg(&pdev->dev, "PWM supports output inversion\n");
+		ret = of_property_read_u32(np, "#pwm-cells", &num_cells);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "missing property '#pwm-cells'\n");
+			return ret;
+		}
+		if (num_cells == 3) {
+			imx_pwm_ops.set_polarity = imx_pwm_set_polarity;
+			imx->chip.of_xlate = of_pwm_xlate_with_flags;
+		} else if (num_cells != 2) {
+			dev_err(&pdev->dev, "'#pwm-cells' must be <2> or <3>\n");
+			return -EINVAL;
+		}
+		imx->chip.of_pwm_n_cells = num_cells;
+	}
+
 	imx->config = data->config;
 	imx->set_enable = data->set_enable;