diff mbox

[V3] pwm: lpc32xx - Add a driver for the motor PWM

Message ID 1410277361-26848-1-git-send-email-alban.bedel@avionic-design.de
State Superseded
Headers show

Commit Message

Alban Bedel Sept. 9, 2014, 3:42 p.m. UTC
The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
The driver can switch the polarity to allow use either output pin A
or output pin B.

Signed-off-by: Alban Bedel <alban.bedel@avionic-design.de>
---
V3: * Updated to current mainline API
    * Fixed LPC32xx vs. LPC32XX
    * Various coding style fix
V2: * Splitted the DTS to its own patch
---
 .../devicetree/bindings/pwm/lpc32xx-motor-pwm.txt  |  24 +++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-lpc32xx-motor.c                    | 210 +++++++++++++++++++++
 4 files changed, 245 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
 create mode 100644 drivers/pwm/pwm-lpc32xx-motor.c

Comments

Arnd Bergmann Sept. 9, 2014, 3:47 p.m. UTC | #1
On Tuesday 09 September 2014 17:42:41 Alban Bedel wrote:
> +config PWM_LPC32XX_MOTOR
> +	tristate "LPC32xx Motor PWM support"
> +	depends on ARCH_LPC32XX
> +	help
> +	  Generic PWM framework driver for LPC32xx motor PWM. The LPC32xx SOC
> +	  has one motor PWM controllers.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-lpc32xx-motor.
> +

Can you change the dependency to ARCH_LPC32XX || COMPILE_TEST and
add explicit dependencies for the subsystems the driver depends
on (pwm and clk, I guess)? That would give us better build-time
coverage with allmodconfig.

> +	/* Write to limit register -> period */
> +	__raw_writel(period, lpc32xx->base + MCLIM_REG_OFFSET(pwm));
> +
> +	/* Write to match register -> duty */
> +	__raw_writel(period - duty, lpc32xx->base + MCMAT_REG_OFFSET(pwm));

Please don't use __raw_{writel,readl} in driver, and change that to use
readl_relaxed()/writel_relaxed().

	Arnd
--
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
Alban Bedel Sept. 9, 2014, 4:05 p.m. UTC | #2
On Tue, 09 Sep 2014 17:47:53 +0200
Arnd Bergmann <arnd@arndb.de> wrote:

> On Tuesday 09 September 2014 17:42:41 Alban Bedel wrote:
> > +config PWM_LPC32XX_MOTOR
> > +	tristate "LPC32xx Motor PWM support"
> > +	depends on ARCH_LPC32XX
> > +	help
> > +	  Generic PWM framework driver for LPC32xx motor PWM. The LPC32xx SOC
> > +	  has one motor PWM controllers.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called pwm-lpc32xx-motor.
> > +
> 
> Can you change the dependency to ARCH_LPC32XX || COMPILE_TEST and
> add explicit dependencies for the subsystems the driver depends
> on (pwm and clk, I guess)? That would give us better build-time
> coverage with allmodconfig.

I'll add COMPILE_TEST, PWM is already there because this is in an if
PWM block. However I'm not sure for the clk, the LPC32xx use its own
implementation of the clk functions, what should I add in this case?

> > +	/* Write to limit register -> period */
> > +	__raw_writel(period, lpc32xx->base + MCLIM_REG_OFFSET(pwm));
> > +
> > +	/* Write to match register -> duty */
> > +	__raw_writel(period - duty, lpc32xx->base + MCMAT_REG_OFFSET(pwm));
> 
> Please don't use __raw_{writel,readl} in driver, and change that to use
> readl_relaxed()/writel_relaxed().

Will do.

Alban
Mark Rutland Sept. 9, 2014, 4:05 p.m. UTC | #3
On Tue, Sep 09, 2014 at 04:42:41PM +0100, Alban Bedel wrote:
> The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
> The driver can switch the polarity to allow use either output pin A
> or output pin B.
> 
> Signed-off-by: Alban Bedel <alban.bedel@avionic-design.de>
> ---
> V3: * Updated to current mainline API
>     * Fixed LPC32xx vs. LPC32XX
>     * Various coding style fix
> V2: * Splitted the DTS to its own patch
> ---
>  .../devicetree/bindings/pwm/lpc32xx-motor-pwm.txt  |  24 +++
>  drivers/pwm/Kconfig                                |  10 +
>  drivers/pwm/Makefile                               |   1 +
>  drivers/pwm/pwm-lpc32xx-motor.c                    | 210 +++++++++++++++++++++
>  4 files changed, 245 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
>  create mode 100644 drivers/pwm/pwm-lpc32xx-motor.c
> 
> diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
> new file mode 100644
> index 0000000..decc27c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
> @@ -0,0 +1,24 @@
> +LPC32xx Motor PWM controller
> +
> +The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
> +By default, output A should be used, if output B is used the PWM
> +polarity should be inverted using the linux,polarity property.
> +
> +Required properties:
> +- compatible: should be "nxp,lpc3220-motor-pwm"
> +- reg: physical base address and length of the controller's registers
> +
> +Optional properties:
> +- linux,polarity: Bit mask of the polarity to use for each output,
> +      a bit set to 0 indicate the default polarity, a bit set to 1
> +      indicate an inverted polarity. In other word this set if output
> +      pin A or output pin B has the correct polarity.

What exactly does linux have to do with the choice of pin? Why should
this be "linux,polarity"?

> +
> +Examples:
> +
> +mpwm@400e8000 {
> +	compatible = "nxp,lpc3220-motor-pwm";
> +	reg = <0x400E8000 0x78>;
> +	linux,polarity = <0x5>; /* Use outputs B0, A1 and B2 */

This doesn't match the description of there being two output pins. I
take it the description above is somewhat misleading?

> +	#pwm-cells = <2>;

The format of these cells should be described.

Wouldn't it make more sense to describe the polarity in the
pwm-specifier? It seems like a property of the connection rather than
the PWM controller itself.

[...]

> +	lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);

No clock was described in the binding.

Is there only the one clock feeding the pwm? (rather than separate
interface and pwm clocks).

Please describe clocks in the binding. If the clock inputs are named,
please use clock-names.

Thanks,
Mark.
--
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
Arnd Bergmann Sept. 9, 2014, 6:19 p.m. UTC | #4
On Tuesday 09 September 2014 18:05:30 Alban Bedel wrote:
> 
> I'll add COMPILE_TEST, PWM is already there because this is in an if
> PWM block. However I'm not sure for the clk, the LPC32xx use its own
> implementation of the clk functions, what should I add in this case?

You can add a dependency on CLKDEV_LOOKUP, which is set by both LPC32xx
and by COMMON_CLK.

	Arnd
--
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
Alban Bedel Sept. 10, 2014, 8:42 a.m. UTC | #5
On Tue, 9 Sep 2014 17:05:48 +0100
Mark Rutland <mark.rutland@arm.com> wrote:

> On Tue, Sep 09, 2014 at 04:42:41PM +0100, Alban Bedel wrote:
> > The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
> > The driver can switch the polarity to allow use either output pin A
> > or output pin B.
> > 
> > Signed-off-by: Alban Bedel <alban.bedel@avionic-design.de>
> > ---
> > V3: * Updated to current mainline API
> >     * Fixed LPC32xx vs. LPC32XX
> >     * Various coding style fix
> > V2: * Splitted the DTS to its own patch
> > ---
> >  .../devicetree/bindings/pwm/lpc32xx-motor-pwm.txt  |  24 +++
> >  drivers/pwm/Kconfig                                |  10 +
> >  drivers/pwm/Makefile                               |   1 +
> >  drivers/pwm/pwm-lpc32xx-motor.c                    | 210 +++++++++++++++++++++
> >  4 files changed, 245 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
> >  create mode 100644 drivers/pwm/pwm-lpc32xx-motor.c
> > 
> > diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
> > new file mode 100644
> > index 0000000..decc27c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
> > @@ -0,0 +1,24 @@
> > +LPC32xx Motor PWM controller
> > +
> > +The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
> > +By default, output A should be used, if output B is used the PWM
> > +polarity should be inverted using the linux,polarity property.
> > +
> > +Required properties:
> > +- compatible: should be "nxp,lpc3220-motor-pwm"
> > +- reg: physical base address and length of the controller's registers
> > +
> > +Optional properties:
> > +- linux,polarity: Bit mask of the polarity to use for each output,
> > +      a bit set to 0 indicate the default polarity, a bit set to 1
> > +      indicate an inverted polarity. In other word this set if output
> > +      pin A or output pin B has the correct polarity.
> 
> What exactly does linux have to do with the choice of pin? Why should
> this be "linux,polarity"?

Right, I'll remove this in favor of the standard 3 cells pwm specifiers.

> > +
> > +Examples:
> > +
> > +mpwm@400e8000 {
> > +	compatible = "nxp,lpc3220-motor-pwm";
> > +	reg = <0x400E8000 0x78>;
> > +	linux,polarity = <0x5>; /* Use outputs B0, A1 and B2 */
> 
> This doesn't match the description of there being two output pins. I
> take it the description above is somewhat misleading?
> 
> > +	#pwm-cells = <2>;
> 
> The format of these cells should be described.
> 
> Wouldn't it make more sense to describe the polarity in the
> pwm-specifier? It seems like a property of the connection rather than
> the PWM controller itself.

Yes, will be done.

> 
> > +	lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
> 
> No clock was described in the binding.
> 
> Is there only the one clock feeding the pwm? (rather than separate
> interface and pwm clocks).

There is only one clock for this PWM block.

> Please describe clocks in the binding. If the clock inputs are named,
> please use clock-names.

No clock is defined in the current LPC32xx DTS, what should I do in
this case?

Alban
Arnd Bergmann Sept. 10, 2014, 9:21 a.m. UTC | #6
On Wednesday 10 September 2014 10:42:20 Alban Bedel wrote:
> 
> > Please describe clocks in the binding. If the clock inputs are named,
> > please use clock-names.
> 
> No clock is defined in the current LPC32xx DTS, what should I do in
> this case?

This is a bit tricky. I would recommend describing the clock inputs
in the binding anyway, as "optional" properties, in case we ever get
a full DT-aware clocksource driver for lpc32xx.

I've just mentioned the topic to Roland yesterday, I would really
like to see such a clocksource driver done for other reasons
(multiplatform support), but introducing one with full DT support
is hard to do without breaking existing dtb files.

	Arnd
--
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
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
new file mode 100644
index 0000000..decc27c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/lpc32xx-motor-pwm.txt
@@ -0,0 +1,24 @@ 
+LPC32xx Motor PWM controller
+
+The LPC32xx motor PWMs have two output pin, A and B, with B = !A.
+By default, output A should be used, if output B is used the PWM
+polarity should be inverted using the linux,polarity property.
+
+Required properties:
+- compatible: should be "nxp,lpc3220-motor-pwm"
+- reg: physical base address and length of the controller's registers
+
+Optional properties:
+- linux,polarity: Bit mask of the polarity to use for each output,
+      a bit set to 0 indicate the default polarity, a bit set to 1
+      indicate an inverted polarity. In other word this set if output
+      pin A or output pin B has the correct polarity.
+
+Examples:
+
+mpwm@400e8000 {
+	compatible = "nxp,lpc3220-motor-pwm";
+	reg = <0x400E8000 0x78>;
+	linux,polarity = <0x5>; /* Use outputs B0, A1 and B2 */
+	#pwm-cells = <2>;
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index b800783..1143348 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -147,6 +147,16 @@  config PWM_LPC32XX
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-lpc32xx.
 
+config PWM_LPC32XX_MOTOR
+	tristate "LPC32xx Motor PWM support"
+	depends on ARCH_LPC32XX
+	help
+	  Generic PWM framework driver for LPC32xx motor PWM. The LPC32xx SOC
+	  has one motor PWM controllers.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-lpc32xx-motor.
+
 config PWM_LPSS
 	tristate "Intel LPSS PWM support"
 	depends on ACPI
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index f8c577d..3eb5dd9 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -12,6 +12,7 @@  obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_JZ4740)	+= pwm-jz4740.o
 obj-$(CONFIG_PWM_LP3943)	+= pwm-lp3943.o
 obj-$(CONFIG_PWM_LPC32XX)	+= pwm-lpc32xx.o
+obj-$(CONFIG_PWM_LPC32XX_MOTOR)	+= pwm-lpc32xx-motor.o
 obj-$(CONFIG_PWM_LPSS)		+= pwm-lpss.o
 obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
 obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o
diff --git a/drivers/pwm/pwm-lpc32xx-motor.c b/drivers/pwm/pwm-lpc32xx-motor.c
new file mode 100644
index 0000000..01cac53
--- /dev/null
+++ b/drivers/pwm/pwm-lpc32xx-motor.c
@@ -0,0 +1,210 @@ 
+/*
+ * Copyright 2012 Alban Bedel <alban.bedel@avionic-design.de>
+ *
+ * Based on pwm-lpc32xx.c from Alexandre Pereira da Silva
+ * <aletes.xgr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct lpc32xx_motor_pwm_chip {
+	struct pwm_chip chip;
+	struct clk *clk;
+	unsigned int pins;
+	void __iomem *base;
+};
+
+#define to_motor_pwm_chip(_chip) \
+	container_of(_chip, struct lpc32xx_motor_pwm_chip, chip)
+
+/* Register mapping for MCPWM modules */
+#define LPC32XX_MCPWM_MCCON		0x00
+#define LPC32XX_MCPWM_MCCON_SET		0x04
+#define LPC32XX_MCPWM_MCCON_CLR		0x08
+#define LPC32XX_MCPWM_MCCAPCON		0x0C
+#define LPC32XX_MCPWM_MCCAPCON_SET	0x10
+#define LPC32XX_MCPWM_MCCAPCON_CLR	0x14
+#define LPC32XX_MCPWM_MCLIM0		0x24
+#define LPC32XX_MCPWM_MCLIM1		0x28
+#define LPC32XX_MCPWM_MCLIM2		0x2C
+#define LPC32XX_MCPWM_MCMAT0		0x30
+#define LPC32XX_MCPWM_MCMAT1		0x34
+#define LPC32XX_MCPWM_MCMAT2		0x38
+#define LPC32XX_MCPWM_MCINTEN_CLR	0x58
+
+#define LPC32XX_MCPWM_COUNT		3
+
+#define PWM_EN_MASK(pwm)	BIT(0 + (pwm)->hwpwm * 8)
+#define MCLIM_REG_OFFSET(pwm)	(LPC32XX_MCPWM_MCLIM0 + (pwm)->hwpwm * 4)
+#define MCMAT_REG_OFFSET(pwm)	(LPC32XX_MCPWM_MCMAT0 + (pwm)->hwpwm * 4)
+
+static int lpc32xx_motor_pwm_config(struct pwm_chip *chip,
+				    struct pwm_device *pwm,
+				    int duty_ns, int period_ns)
+{
+	struct lpc32xx_motor_pwm_chip *lpc32xx = to_motor_pwm_chip(chip);
+	u64 rate, period, duty;
+	int err = 0;
+
+	/* The clock is needed to access the registers */
+	err = clk_enable(lpc32xx->clk);
+	if (err)
+		return err;
+
+	/* Calculate period */
+	rate = clk_get_rate(lpc32xx->clk);
+	period  = (u64)period_ns * rate;
+	duty = (u64)duty_ns * rate;
+	do_div(period,  1000000000);
+	do_div(duty, 1000000000);
+
+	/* Write to limit register -> period */
+	__raw_writel(period, lpc32xx->base + MCLIM_REG_OFFSET(pwm));
+
+	/* Write to match register -> duty */
+	__raw_writel(period - duty, lpc32xx->base + MCMAT_REG_OFFSET(pwm));
+
+	/* Disable the clock now that we are done */
+	clk_disable(lpc32xx->clk);
+	return 0;
+}
+
+static int lpc32xx_motor_pwm_enable(struct pwm_chip *chip,
+				    struct pwm_device *pwm)
+{
+	struct lpc32xx_motor_pwm_chip *lpc32xx = to_motor_pwm_chip(chip);
+	int err;
+
+	err = clk_enable(lpc32xx->clk);
+	if (err)
+		return err;
+
+	__raw_writel(PWM_EN_MASK(pwm), lpc32xx->base + LPC32XX_MCPWM_MCCON_SET);
+
+	return 0;
+}
+
+static void lpc32xx_motor_pwm_disable(struct pwm_chip *chip,
+				      struct pwm_device *pwm)
+{
+	struct lpc32xx_motor_pwm_chip *lpc32xx = to_motor_pwm_chip(chip);
+
+	__raw_writel(PWM_EN_MASK(pwm), lpc32xx->base + LPC32XX_MCPWM_MCCON_CLR);
+
+	clk_disable(lpc32xx->clk);
+}
+
+static const struct pwm_ops lpc32xx_motor_pwm_ops = {
+	.config = lpc32xx_motor_pwm_config,
+	.enable = lpc32xx_motor_pwm_enable,
+	.disable = lpc32xx_motor_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int lpc32xx_motor_pwm_probe(struct platform_device *pdev)
+{
+	struct lpc32xx_motor_pwm_chip *lpc32xx;
+	struct resource *res;
+	int ret;
+
+	lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL);
+	if (!lpc32xx)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	lpc32xx->base = devm_ioremap_resource(&pdev->dev, res);
+	if (!lpc32xx->base)
+		return -EADDRNOTAVAIL;
+
+	lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(lpc32xx->clk))
+		return PTR_ERR(lpc32xx->clk);
+
+	/* Configure the pins polarity */
+	ret = of_property_read_u32(pdev->dev.of_node, "linux,polarity",
+				   &lpc32xx->pins);
+	if (!ret) {
+		u32 set = 0, clr = 0;
+		int i;
+
+		for (i = 0; i < LPC32XX_MCPWM_COUNT; i++)
+			if (lpc32xx->pins & BIT(i))
+				set |= BIT(2 + i * 8);
+			else
+				clr |= BIT(2 + i * 8);
+
+		ret = clk_enable(lpc32xx->clk);
+		if (ret)
+			return ret;
+
+		__raw_writel(set, lpc32xx->base + LPC32XX_MCPWM_MCCON_SET);
+		__raw_writel(clr, lpc32xx->base + LPC32XX_MCPWM_MCCON_CLR);
+
+		clk_disable(lpc32xx->clk);
+	}
+
+	lpc32xx->chip.dev = &pdev->dev;
+	lpc32xx->chip.ops = &lpc32xx_motor_pwm_ops;
+	lpc32xx->chip.npwm = LPC32XX_MCPWM_COUNT;
+	lpc32xx->chip.base = -1;
+
+	ret = pwmchip_add(&lpc32xx->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, lpc32xx);
+
+	return 0;
+}
+
+static int lpc32xx_motor_pwm_remove(struct platform_device *pdev)
+{
+	struct lpc32xx_motor_pwm_chip *lpc32xx = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < lpc32xx->chip.npwm; i++)
+		pwm_disable(&lpc32xx->chip.pwms[i]);
+
+	return pwmchip_remove(&lpc32xx->chip);
+}
+
+static const struct of_device_id lpc32xx_motor_pwm_dt_ids[] = {
+	{ .compatible = "nxp,lpc3220-motor-pwm", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_motor_pwm_dt_ids);
+
+static struct platform_driver lpc32xx_motor_pwm_driver = {
+	.driver = {
+		.name = "lpc32xx-motor-pwm",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(lpc32xx_motor_pwm_dt_ids),
+	},
+	.probe = lpc32xx_motor_pwm_probe,
+	.remove = lpc32xx_motor_pwm_remove,
+};
+module_platform_driver(lpc32xx_motor_pwm_driver);
+
+MODULE_ALIAS("platform:lpc32xx-motor-pwm");
+MODULE_AUTHOR("Alban Bedel <alban.bedel@avionic-design.de>");
+MODULE_DESCRIPTION("LPC32xx Motor PWM Driver");
+MODULE_LICENSE("GPL v2");