linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/5] ARM: berlin: PWM support
@ 2015-09-17 10:13 Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

Hi all,

This series adds a driver for the Marvell Berlin PWM controller, which
has 4 channels.

This has been tested on a BG2Q DMP.

Thanks,

Antoine

Changes since v5:
	- Rebased on top of 4.3-rc1
	- Added a call to clk_disable_unprepare() in case of failure

Changes since v4:
        - Reworked _remove()
        - Reworked _enable()

Changes since v3:
        - Use readl_relaxed
        - Reworked the clk enable / clk disable calls
        - Removed BERLIN_PWM_DISABLE

Changes since v2:
        - Reworked the prescaler/tcnt calculation
        - Added an input clock
        - Updated the documentation
        - Reordered the pwm node
        - Added some definition for BIT()

Changes since v1:
        - Added a sentinel in berlin_pwm_match[]

Antoine Tenart (5):
  pwm: add the Berlin pwm controller driver
  Documentation: bindings: document the Berlin PWM driver
  ARM: berlin: add a PWM node on the BG2Q
  ARM: berlin: add a PWM node on the BG2
  ARM: berlin: add a PWM node on the BG2CD

 .../devicetree/bindings/pwm/pwm-berlin.txt         |  19 ++
 arch/arm/boot/dts/berlin2.dtsi                     |   7 +
 arch/arm/boot/dts/berlin2cd.dtsi                   |   7 +
 arch/arm/boot/dts/berlin2q.dtsi                    |   7 +
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-berlin.c                           | 229 +++++++++++++++++++++
 7 files changed, 279 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-berlin.txt
 create mode 100644 drivers/pwm/pwm-berlin.c

-- 
2.5.2


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
@ 2015-09-17 10:13 ` Antoine Tenart
  2015-09-20 18:13   ` Sebastian Hesselbarth
  2015-09-21  8:40   ` Thierry Reding
  2015-09-17 10:13 ` [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver Antoine Tenart
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
controller has 4 channels.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
 drivers/pwm/Kconfig      |   9 ++
 drivers/pwm/Makefile     |   1 +
 drivers/pwm/pwm-berlin.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/pwm/pwm-berlin.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 062630ab7424..f3e8b7566ce5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -92,6 +92,15 @@ config PWM_BCM2835
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-bcm2835.
 
+config PWM_BERLIN
+	tristate "Berlin PWM support"
+	depends on ARCH_BERLIN
+	help
+	  PWM framework driver for Berlin.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-berlin.
+
 config PWM_BFIN
 	tristate "Blackfin PWM support"
 	depends on BFIN_GPTIMERS
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index a0e00c09ead3..601833d82da5 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
 obj-$(CONFIG_PWM_BCM2835)	+= pwm-bcm2835.o
+obj-$(CONFIG_PWM_BERLIN)	+= pwm-berlin.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_CLPS711X)	+= pwm-clps711x.o
 obj-$(CONFIG_PWM_CRC)		+= pwm-crc.o
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
new file mode 100644
index 000000000000..cb9790a2cde7
--- /dev/null
+++ b/drivers/pwm/pwm-berlin.c
@@ -0,0 +1,229 @@
+/*
+ * Marvell Berlin PWM driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/spinlock.h>
+
+#define BERLIN_PWM_EN			0x0
+#define BERLIN_PWM_CONTROL		0x4
+#define BERLIN_PWM_DUTY			0x8
+#define BERLIN_PWM_TCNT			0xc
+
+#define BERLIN_PWM_ENABLE		BIT(0)
+#define BERLIN_PWM_INVERT_POLARITY	BIT(3)
+#define BERLIN_PWM_PRESCALE_MASK	0x7
+#define BERLIN_PWM_PRESCALE_MAX		4096
+#define BERLIN_PWM_MAX_TCNT		65535
+
+struct berlin_pwm_chip {
+	struct pwm_chip chip;
+	struct clk *clk;
+	void __iomem *base;
+	spinlock_t lock;
+};
+
+#define to_berlin_pwm_chip(chip)	\
+	container_of((chip), struct berlin_pwm_chip, chip)
+
+#define berlin_pwm_readl(chip, channel, offset)		\
+	readl_relaxed((chip)->base + (channel) * 0x10 + offset)
+#define berlin_pwm_writel(val, chip, channel, offset)	\
+	writel_relaxed(val, (chip)->base + (channel) * 0x10 + offset)
+
+/* prescaler table: {1, 4, 8, 16, 64, 256, 1024, 4096} */
+static const u32 prescaler_diff_table[] = {
+	1, 4, 2, 2, 4, 4, 4, 4,
+};
+
+static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
+			     int duty_ns, int period_ns)
+{
+	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+	int prescale = 0;
+	u32 val, duty, period;
+	u64 cycles;
+
+	cycles = clk_get_rate(pwm->clk);
+	cycles *= period_ns;
+	do_div(cycles, NSEC_PER_SEC);
+
+	while (cycles > BERLIN_PWM_MAX_TCNT)
+		do_div(cycles, prescaler_diff_table[++prescale]);
+
+	if (cycles > BERLIN_PWM_MAX_TCNT)
+		return -EINVAL;
+
+	period = cycles;
+	cycles *= duty_ns;
+	do_div(cycles, period_ns);
+	duty = cycles;
+
+	spin_lock(&pwm->lock);
+
+	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+	val &= ~BERLIN_PWM_PRESCALE_MASK;
+	val |= prescale;
+	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+
+	berlin_pwm_writel(duty, pwm, pwm_dev->hwpwm, BERLIN_PWM_DUTY);
+	berlin_pwm_writel(period, pwm, pwm_dev->hwpwm, BERLIN_PWM_TCNT);
+
+	spin_unlock(&pwm->lock);
+
+	return 0;
+}
+
+static int berlin_pwm_set_polarity(struct pwm_chip *chip,
+				   struct pwm_device *pwm_dev,
+				   enum pwm_polarity polarity)
+{
+	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+	u32 val;
+
+	spin_lock(&pwm->lock);
+
+	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+
+	if (polarity == PWM_POLARITY_NORMAL)
+		val &= ~BERLIN_PWM_INVERT_POLARITY;
+	else
+		val |= BERLIN_PWM_INVERT_POLARITY;
+
+	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+
+	spin_unlock(&pwm->lock);
+
+	return 0;
+}
+
+static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev)
+{
+	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+	u32 val;
+
+	spin_lock(&pwm->lock);
+
+	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+	val |= BERLIN_PWM_ENABLE;
+	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+
+	spin_unlock(&pwm->lock);
+
+	return 0;
+}
+
+static void berlin_pwm_disable(struct pwm_chip *chip,
+			       struct pwm_device *pwm_dev)
+{
+	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+	u32 val;
+
+	spin_lock(&pwm->lock);
+
+	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+	val &= ~BERLIN_PWM_ENABLE;
+	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+
+	spin_unlock(&pwm->lock);
+}
+
+static const struct pwm_ops berlin_pwm_ops = {
+	.config = berlin_pwm_config,
+	.set_polarity = berlin_pwm_set_polarity,
+	.enable = berlin_pwm_enable,
+	.disable = berlin_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static const struct of_device_id berlin_pwm_match[] = {
+	{ .compatible = "marvell,berlin-pwm" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, berlin_pwm_match);
+
+static int berlin_pwm_probe(struct platform_device *pdev)
+{
+	struct berlin_pwm_chip *pwm;
+	struct resource *res;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pwm->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pwm->base))
+		return PTR_ERR(pwm->base);
+
+	pwm->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pwm->clk))
+		return PTR_ERR(pwm->clk);
+
+	ret = clk_prepare_enable(pwm->clk);
+	if (ret)
+		return ret;
+
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &berlin_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = 4;
+	pwm->chip.can_sleep = true;
+	pwm->chip.of_xlate = of_pwm_xlate_with_flags;
+	pwm->chip.of_pwm_n_cells = 3;
+
+	spin_lock_init(&pwm->lock);
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		clk_disable_unprepare(pwm->clk);
+
+		dev_err(&pdev->dev, "Failed to add PWM chip: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	return 0;
+}
+
+static int berlin_pwm_remove(struct platform_device *pdev)
+{
+	struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = pwmchip_remove(&pwm->chip);
+	if (ret)
+		return ret;
+
+	clk_disable_unprepare(pwm->clk);
+	return 0;
+}
+
+static struct platform_driver berlin_pwm_driver = {
+	.probe	= berlin_pwm_probe,
+	.remove	= berlin_pwm_remove,
+	.driver	= {
+		.name	= "berlin-pwm",
+		.of_match_table = berlin_pwm_match,
+	},
+};
+module_platform_driver(berlin_pwm_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell Berlin PWM driver");
+MODULE_LICENSE("GPL v2");
-- 
2.5.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver
  2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
@ 2015-09-17 10:13 ` Antoine Tenart
  2015-09-21  8:14   ` Thierry Reding
  2015-09-17 10:13 ` [PATCH v6 3/5] ARM: berlin: add a PWM node on the BG2Q Antoine Tenart
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

Following the addition of a Berlin PWM driver, this patch adds the
corresponding documentation.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
 Documentation/devicetree/bindings/pwm/pwm-berlin.txt | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-berlin.txt

diff --git a/Documentation/devicetree/bindings/pwm/pwm-berlin.txt b/Documentation/devicetree/bindings/pwm/pwm-berlin.txt
new file mode 100644
index 000000000000..8f9bc11f8c4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-berlin.txt
@@ -0,0 +1,19 @@
+Berlin PWM controller
+
+PWM IP found in Marvell Berlin SoCs.
+
+Required properties:
+- compatible: should be "marvell,berlin-pwm"
+- reg: physical base address and length of the controller's registers
+- clocks: phandle to the input clock
+- #pwm-cells: should be 3. See pwm.txt in this directory for a description of
+  the cells format.
+
+Example:
+
+pwm: pwm@f7f20000 {
+	compatible = "marvell,berlin-pwm";
+	reg = <0xf7f20000 0x40>;
+	clocks = <&chip_clk CLKID_CFG>;
+	#pwm-cells = <3>;
+}
-- 
2.5.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v6 3/5] ARM: berlin: add a PWM node on the BG2Q
  2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver Antoine Tenart
@ 2015-09-17 10:13 ` Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 4/5] ARM: berlin: add a PWM node on the BG2 Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 5/5] ARM: berlin: add a PWM node on the BG2CD Antoine Tenart
  4 siblings, 0 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

This patch adds a PWM node in the Berlin BG2Q device tree, using the
newly added Berlin PWM driver.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
 arch/arm/boot/dts/berlin2q.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi
index 63a48490e2f9..5bfa71a87617 100644
--- a/arch/arm/boot/dts/berlin2q.dtsi
+++ b/arch/arm/boot/dts/berlin2q.dtsi
@@ -477,6 +477,13 @@
 			status = "disabled";
 		};
 
+		pwm: pwm@f20000 {
+			compatible = "marvell,berlin-pwm";
+			reg = <0xf20000 0x40>;
+			clocks = <&chip_clk CLKID_CFG>;
+			#pwm-cells = <3>;
+		};
+
 		apb@fc0000 {
 			compatible = "simple-bus";
 			#address-cells = <1>;
-- 
2.5.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v6 4/5] ARM: berlin: add a PWM node on the BG2
  2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
                   ` (2 preceding siblings ...)
  2015-09-17 10:13 ` [PATCH v6 3/5] ARM: berlin: add a PWM node on the BG2Q Antoine Tenart
@ 2015-09-17 10:13 ` Antoine Tenart
  2015-09-17 10:13 ` [PATCH v6 5/5] ARM: berlin: add a PWM node on the BG2CD Antoine Tenart
  4 siblings, 0 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

This patch adds a PWM node in the Berlin BG2 device tree, using the
newly added Berlin PWM driver.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
 arch/arm/boot/dts/berlin2.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2.dtsi b/arch/arm/boot/dts/berlin2.dtsi
index ef811de09908..f094b06d16aa 100644
--- a/arch/arm/boot/dts/berlin2.dtsi
+++ b/arch/arm/boot/dts/berlin2.dtsi
@@ -404,6 +404,13 @@
 			};
 		};
 
+		pwm: pwm@f20000 {
+			compatible = "marvell,berlin-pwm";
+			reg = <0xf20000 0x40>;
+			clocks = <&chip_clk CLKID_CFG>;
+			#pwm-cells = <3>;
+		};
+
 		apb@fc0000 {
 			compatible = "simple-bus";
 			#address-cells = <1>;
-- 
2.5.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v6 5/5] ARM: berlin: add a PWM node on the BG2CD
  2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
                   ` (3 preceding siblings ...)
  2015-09-17 10:13 ` [PATCH v6 4/5] ARM: berlin: add a PWM node on the BG2 Antoine Tenart
@ 2015-09-17 10:13 ` Antoine Tenart
  4 siblings, 0 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-17 10:13 UTC (permalink / raw)
  To: sebastian.hesselbarth, thierry.reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

This patch adds a PWM node in the Berlin BG2CD device tree, using the
newly added Berlin PWM driver.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
 arch/arm/boot/dts/berlin2cd.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2cd.dtsi b/arch/arm/boot/dts/berlin2cd.dtsi
index 900213d78a32..5d984f58dde7 100644
--- a/arch/arm/boot/dts/berlin2cd.dtsi
+++ b/arch/arm/boot/dts/berlin2cd.dtsi
@@ -368,6 +368,13 @@
 			status = "disabled";
 		};
 
+		pwm: pwm@f20000 {
+			compatible = "marvell,berlin-pwm";
+			reg = <0xf20000 0x40>;
+			clocks = <&chip_clk CLKID_CFG>;
+			#pwm-cells = <3>;
+		};
+
 		apb@fc0000 {
 			compatible = "simple-bus";
 			#address-cells = <1>;
-- 
2.5.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
@ 2015-09-20 18:13   ` Sebastian Hesselbarth
  2015-09-21  8:09     ` Thierry Reding
  2015-09-21  8:40   ` Thierry Reding
  1 sibling, 1 reply; 12+ messages in thread
From: Sebastian Hesselbarth @ 2015-09-20 18:13 UTC (permalink / raw)
  To: Antoine Tenart, thierry.reding
  Cc: zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

On 17.09.2015 12:13, Antoine Tenart wrote:
> Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
> controller has 4 channels.
>
> Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>

Thierry,

if you are also fine with the driver, please let me know if you want
me to take the driver through berlin-tree or if you are taking it.

I am taking the binding docs and dts changes anyway.

Sebastian

> ---
>   drivers/pwm/Kconfig      |   9 ++
>   drivers/pwm/Makefile     |   1 +
>   drivers/pwm/pwm-berlin.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 239 insertions(+)
>   create mode 100644 drivers/pwm/pwm-berlin.c
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 062630ab7424..f3e8b7566ce5 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -92,6 +92,15 @@ config PWM_BCM2835
>   	  To compile this driver as a module, choose M here: the module
>   	  will be called pwm-bcm2835.
>
> +config PWM_BERLIN
> +	tristate "Berlin PWM support"
> +	depends on ARCH_BERLIN
> +	help
> +	  PWM framework driver for Berlin.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-berlin.
> +
>   config PWM_BFIN
>   	tristate "Blackfin PWM support"
>   	depends on BFIN_GPTIMERS
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index a0e00c09ead3..601833d82da5 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
>   obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
>   obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
>   obj-$(CONFIG_PWM_BCM2835)	+= pwm-bcm2835.o
> +obj-$(CONFIG_PWM_BERLIN)	+= pwm-berlin.o
>   obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
>   obj-$(CONFIG_PWM_CLPS711X)	+= pwm-clps711x.o
>   obj-$(CONFIG_PWM_CRC)		+= pwm-crc.o
> diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
> new file mode 100644
> index 000000000000..cb9790a2cde7
> --- /dev/null
> +++ b/drivers/pwm/pwm-berlin.c
> @@ -0,0 +1,229 @@
> +/*
> + * Marvell Berlin PWM driver
> + *
> + * Copyright (C) 2015 Marvell Technology Group Ltd.
> + *
> + * Antoine Tenart <antoine.tenart@free-electrons.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/spinlock.h>
> +
> +#define BERLIN_PWM_EN			0x0
> +#define BERLIN_PWM_CONTROL		0x4
> +#define BERLIN_PWM_DUTY			0x8
> +#define BERLIN_PWM_TCNT			0xc
> +
> +#define BERLIN_PWM_ENABLE		BIT(0)
> +#define BERLIN_PWM_INVERT_POLARITY	BIT(3)
> +#define BERLIN_PWM_PRESCALE_MASK	0x7
> +#define BERLIN_PWM_PRESCALE_MAX		4096
> +#define BERLIN_PWM_MAX_TCNT		65535
> +
> +struct berlin_pwm_chip {
> +	struct pwm_chip chip;
> +	struct clk *clk;
> +	void __iomem *base;
> +	spinlock_t lock;
> +};
> +
> +#define to_berlin_pwm_chip(chip)	\
> +	container_of((chip), struct berlin_pwm_chip, chip)
> +
> +#define berlin_pwm_readl(chip, channel, offset)		\
> +	readl_relaxed((chip)->base + (channel) * 0x10 + offset)
> +#define berlin_pwm_writel(val, chip, channel, offset)	\
> +	writel_relaxed(val, (chip)->base + (channel) * 0x10 + offset)
> +
> +/* prescaler table: {1, 4, 8, 16, 64, 256, 1024, 4096} */
> +static const u32 prescaler_diff_table[] = {
> +	1, 4, 2, 2, 4, 4, 4, 4,
> +};
> +
> +static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
> +			     int duty_ns, int period_ns)
> +{
> +	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
> +	int prescale = 0;
> +	u32 val, duty, period;
> +	u64 cycles;
> +
> +	cycles = clk_get_rate(pwm->clk);
> +	cycles *= period_ns;
> +	do_div(cycles, NSEC_PER_SEC);
> +
> +	while (cycles > BERLIN_PWM_MAX_TCNT)
> +		do_div(cycles, prescaler_diff_table[++prescale]);
> +
> +	if (cycles > BERLIN_PWM_MAX_TCNT)
> +		return -EINVAL;
> +
> +	period = cycles;
> +	cycles *= duty_ns;
> +	do_div(cycles, period_ns);
> +	duty = cycles;
> +
> +	spin_lock(&pwm->lock);
> +
> +	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +	val &= ~BERLIN_PWM_PRESCALE_MASK;
> +	val |= prescale;
> +	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +
> +	berlin_pwm_writel(duty, pwm, pwm_dev->hwpwm, BERLIN_PWM_DUTY);
> +	berlin_pwm_writel(period, pwm, pwm_dev->hwpwm, BERLIN_PWM_TCNT);
> +
> +	spin_unlock(&pwm->lock);
> +
> +	return 0;
> +}
> +
> +static int berlin_pwm_set_polarity(struct pwm_chip *chip,
> +				   struct pwm_device *pwm_dev,
> +				   enum pwm_polarity polarity)
> +{
> +	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
> +	u32 val;
> +
> +	spin_lock(&pwm->lock);
> +
> +	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +
> +	if (polarity == PWM_POLARITY_NORMAL)
> +		val &= ~BERLIN_PWM_INVERT_POLARITY;
> +	else
> +		val |= BERLIN_PWM_INVERT_POLARITY;
> +
> +	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +
> +	spin_unlock(&pwm->lock);
> +
> +	return 0;
> +}
> +
> +static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev)
> +{
> +	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
> +	u32 val;
> +
> +	spin_lock(&pwm->lock);
> +
> +	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
> +	val |= BERLIN_PWM_ENABLE;
> +	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
> +
> +	spin_unlock(&pwm->lock);
> +
> +	return 0;
> +}
> +
> +static void berlin_pwm_disable(struct pwm_chip *chip,
> +			       struct pwm_device *pwm_dev)
> +{
> +	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
> +	u32 val;
> +
> +	spin_lock(&pwm->lock);
> +
> +	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
> +	val &= ~BERLIN_PWM_ENABLE;
> +	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
> +
> +	spin_unlock(&pwm->lock);
> +}
> +
> +static const struct pwm_ops berlin_pwm_ops = {
> +	.config = berlin_pwm_config,
> +	.set_polarity = berlin_pwm_set_polarity,
> +	.enable = berlin_pwm_enable,
> +	.disable = berlin_pwm_disable,
> +	.owner = THIS_MODULE,
> +};
> +
> +static const struct of_device_id berlin_pwm_match[] = {
> +	{ .compatible = "marvell,berlin-pwm" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, berlin_pwm_match);
> +
> +static int berlin_pwm_probe(struct platform_device *pdev)
> +{
> +	struct berlin_pwm_chip *pwm;
> +	struct resource *res;
> +	int ret;
> +
> +	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
> +	if (!pwm)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pwm->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(pwm->base))
> +		return PTR_ERR(pwm->base);
> +
> +	pwm->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(pwm->clk))
> +		return PTR_ERR(pwm->clk);
> +
> +	ret = clk_prepare_enable(pwm->clk);
> +	if (ret)
> +		return ret;
> +
> +	pwm->chip.dev = &pdev->dev;
> +	pwm->chip.ops = &berlin_pwm_ops;
> +	pwm->chip.base = -1;
> +	pwm->chip.npwm = 4;
> +	pwm->chip.can_sleep = true;
> +	pwm->chip.of_xlate = of_pwm_xlate_with_flags;
> +	pwm->chip.of_pwm_n_cells = 3;
> +
> +	spin_lock_init(&pwm->lock);
> +
> +	ret = pwmchip_add(&pwm->chip);
> +	if (ret < 0) {
> +		clk_disable_unprepare(pwm->clk);
> +
> +		dev_err(&pdev->dev, "Failed to add PWM chip: %d\n", ret);
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, pwm);
> +
> +	return 0;
> +}
> +
> +static int berlin_pwm_remove(struct platform_device *pdev)
> +{
> +	struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = pwmchip_remove(&pwm->chip);
> +	if (ret)
> +		return ret;
> +
> +	clk_disable_unprepare(pwm->clk);
> +	return 0;
> +}
> +
> +static struct platform_driver berlin_pwm_driver = {
> +	.probe	= berlin_pwm_probe,
> +	.remove	= berlin_pwm_remove,
> +	.driver	= {
> +		.name	= "berlin-pwm",
> +		.of_match_table = berlin_pwm_match,
> +	},
> +};
> +module_platform_driver(berlin_pwm_driver);
> +
> +MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
> +MODULE_DESCRIPTION("Marvell Berlin PWM driver");
> +MODULE_LICENSE("GPL v2");
>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-20 18:13   ` Sebastian Hesselbarth
@ 2015-09-21  8:09     ` Thierry Reding
  2015-09-21 21:05       ` Sebastian Hesselbarth
  0 siblings, 1 reply; 12+ messages in thread
From: Thierry Reding @ 2015-09-21  8:09 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 847 bytes --]

On Sun, Sep 20, 2015 at 08:13:48PM +0200, Sebastian Hesselbarth wrote:
> On 17.09.2015 12:13, Antoine Tenart wrote:
> >Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
> >controller has 4 channels.
> >
> >Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
> >Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> 
> Thierry,
> 
> if you are also fine with the driver, please let me know if you want
> me to take the driver through berlin-tree or if you are taking it.
> 
> I am taking the binding docs and dts changes anyway.

Sorry but no. The binding documentation needs to go into the same tree
as the driver because they need to be kept in sync. We can't have DT
binding documentation go into platform trees if the driver implementing
the binding hasn't been merged.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver
  2015-09-17 10:13 ` [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver Antoine Tenart
@ 2015-09-21  8:14   ` Thierry Reding
  0 siblings, 0 replies; 12+ messages in thread
From: Thierry Reding @ 2015-09-21  8:14 UTC (permalink / raw)
  To: Antoine Tenart
  Cc: sebastian.hesselbarth, zmxu, jszhang, linux-arm-kernel,
	linux-pwm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1587 bytes --]

On Thu, Sep 17, 2015 at 12:13:05PM +0200, Antoine Tenart wrote:
> Following the addition of a Berlin PWM driver, this patch adds the
> corresponding documentation.
> 
> Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
>  Documentation/devicetree/bindings/pwm/pwm-berlin.txt | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-berlin.txt

That's the wrong way around. You define the binding, get concensus that
it's okay and then implement the binding in the driver. Of course you'd
always provide both the binding and an implementation in the same patch
series for convenience, but that doesn't change the logical ordering.

> diff --git a/Documentation/devicetree/bindings/pwm/pwm-berlin.txt b/Documentation/devicetree/bindings/pwm/pwm-berlin.txt
> new file mode 100644
> index 000000000000..8f9bc11f8c4c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-berlin.txt
> @@ -0,0 +1,19 @@
> +Berlin PWM controller
> +
> +PWM IP found in Marvell Berlin SoCs.

This isn't a proper sentence and doesn't add much useful information. If
you want to say anything here, provide details about the PWM controller.

> +
> +Required properties:
> +- compatible: should be "marvell,berlin-pwm"
> +- reg: physical base address and length of the controller's registers
> +- clocks: phandle to the input clock

You should think about adding a clock-names property here as well.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
  2015-09-20 18:13   ` Sebastian Hesselbarth
@ 2015-09-21  8:40   ` Thierry Reding
  2015-09-25  9:15     ` Antoine Tenart
  1 sibling, 1 reply; 12+ messages in thread
From: Thierry Reding @ 2015-09-21  8:40 UTC (permalink / raw)
  To: Antoine Tenart
  Cc: sebastian.hesselbarth, zmxu, jszhang, linux-arm-kernel,
	linux-pwm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 8734 bytes --]

On Thu, Sep 17, 2015 at 12:13:04PM +0200, Antoine Tenart wrote:
> Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
> controller has 4 channels.
> 
> Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
>  drivers/pwm/Kconfig      |   9 ++
>  drivers/pwm/Makefile     |   1 +
>  drivers/pwm/pwm-berlin.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 239 insertions(+)
>  create mode 100644 drivers/pwm/pwm-berlin.c
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 062630ab7424..f3e8b7566ce5 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -92,6 +92,15 @@ config PWM_BCM2835
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-bcm2835.
>  
> +config PWM_BERLIN
> +	tristate "Berlin PWM support"
> +	depends on ARCH_BERLIN
> +	help
> +	  PWM framework driver for Berlin.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-berlin.
> +
>  config PWM_BFIN
>  	tristate "Blackfin PWM support"
>  	depends on BFIN_GPTIMERS
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index a0e00c09ead3..601833d82da5 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
>  obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
>  obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
>  obj-$(CONFIG_PWM_BCM2835)	+= pwm-bcm2835.o
> +obj-$(CONFIG_PWM_BERLIN)	+= pwm-berlin.o
>  obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
>  obj-$(CONFIG_PWM_CLPS711X)	+= pwm-clps711x.o
>  obj-$(CONFIG_PWM_CRC)		+= pwm-crc.o
> diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
> new file mode 100644
> index 000000000000..cb9790a2cde7
> --- /dev/null
> +++ b/drivers/pwm/pwm-berlin.c
> @@ -0,0 +1,229 @@
> +/*
> + * Marvell Berlin PWM driver
> + *
> + * Copyright (C) 2015 Marvell Technology Group Ltd.
> + *
> + * Antoine Tenart <antoine.tenart@free-electrons.com>

There's no mention here about what this line means. From the commit
message and the MODULE_AUTHOR I know that you're the author, so either
this should just be dropped or it should be prefixed with "Author: ".

> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/spinlock.h>
> +
> +#define BERLIN_PWM_EN			0x0
> +#define BERLIN_PWM_CONTROL		0x4
> +#define BERLIN_PWM_DUTY			0x8
> +#define BERLIN_PWM_TCNT			0xc
> +
> +#define BERLIN_PWM_ENABLE		BIT(0)
> +#define BERLIN_PWM_INVERT_POLARITY	BIT(3)
> +#define BERLIN_PWM_PRESCALE_MASK	0x7
> +#define BERLIN_PWM_PRESCALE_MAX		4096
> +#define BERLIN_PWM_MAX_TCNT		65535

It'd be nice to see some sort of connection between the register
definitions and which fields belong to them.

> +struct berlin_pwm_chip {
> +	struct pwm_chip chip;
> +	struct clk *clk;
> +	void __iomem *base;
> +	spinlock_t lock;

I don't think that lock is necessary here. You have per-channel
registers and each channel can only be used by one consumer at a time
anyway.

> +};
> +
> +#define to_berlin_pwm_chip(chip)	\
> +	container_of((chip), struct berlin_pwm_chip, chip)
> +
> +#define berlin_pwm_readl(chip, channel, offset)		\
> +	readl_relaxed((chip)->base + (channel) * 0x10 + offset)
> +#define berlin_pwm_writel(val, chip, channel, offset)	\
> +	writel_relaxed(val, (chip)->base + (channel) * 0x10 + offset)

These should be static inline functions. Also I think for
berlin_pwm_writel() val should come after chip and channel to preserve a
more natural ordering of parameters.

> +
> +/* prescaler table: {1, 4, 8, 16, 64, 256, 1024, 4096} */
> +static const u32 prescaler_diff_table[] = {
> +	1, 4, 2, 2, 4, 4, 4, 4,
> +};

I don't see any relationship between these values and the prescaler
table given in the comment. Please expand the comment to explain the
connection.

After reading the remainder of the code, I see that the values in this
table are the multiplication factors for each of the prescalers. It
shouldn't be necessary to read the code to find out, so please clarify
in the comment (and perhaps rename the table to something more related
to its purpose, such as prescale_factors).

Perhaps an even more easily digestible alternative would be to make this
a list of prescaler values and then use the values directly to compute
the number of cycles rather than iteratively dividing and needing this
unintuitive mapping.

> +
> +static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
> +			     int duty_ns, int period_ns)
> +{
> +	struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
> +	int prescale = 0;

This can be unsigned.

> +	u32 val, duty, period;
> +	u64 cycles;
> +
> +	cycles = clk_get_rate(pwm->clk);
> +	cycles *= period_ns;
> +	do_div(cycles, NSEC_PER_SEC);
> +
> +	while (cycles > BERLIN_PWM_MAX_TCNT)
> +		do_div(cycles, prescaler_diff_table[++prescale]);

Don't you need to make sure that prescale doesn't exceed the table size?

> +
> +	if (cycles > BERLIN_PWM_MAX_TCNT)
> +		return -EINVAL;

Perhaps -ERANGE?

> +
> +	period = cycles;
> +	cycles *= duty_ns;
> +	do_div(cycles, period_ns);
> +	duty = cycles;
> +
> +	spin_lock(&pwm->lock);
> +
> +	val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +	val &= ~BERLIN_PWM_PRESCALE_MASK;
> +	val |= prescale;
> +	berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
> +
> +	berlin_pwm_writel(duty, pwm, pwm_dev->hwpwm, BERLIN_PWM_DUTY);
> +	berlin_pwm_writel(period, pwm, pwm_dev->hwpwm, BERLIN_PWM_TCNT);
> +
> +	spin_unlock(&pwm->lock);
> +
> +	return 0;
> +}
[...]
> +static int berlin_pwm_probe(struct platform_device *pdev)
> +{
> +	struct berlin_pwm_chip *pwm;
> +	struct resource *res;
> +	int ret;
> +
> +	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
> +	if (!pwm)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pwm->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(pwm->base))
> +		return PTR_ERR(pwm->base);
> +
> +	pwm->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(pwm->clk))
> +		return PTR_ERR(pwm->clk);
> +
> +	ret = clk_prepare_enable(pwm->clk);
> +	if (ret)
> +		return ret;
> +
> +	pwm->chip.dev = &pdev->dev;
> +	pwm->chip.ops = &berlin_pwm_ops;
> +	pwm->chip.base = -1;
> +	pwm->chip.npwm = 4;
> +	pwm->chip.can_sleep = true;
> +	pwm->chip.of_xlate = of_pwm_xlate_with_flags;
> +	pwm->chip.of_pwm_n_cells = 3;
> +
> +	spin_lock_init(&pwm->lock);
> +
> +	ret = pwmchip_add(&pwm->chip);
> +	if (ret < 0) {
> +		clk_disable_unprepare(pwm->clk);

Why not enable the clock until after successful registration? It doesn't
seem like you need access before that. Doing so would introduce a subtle
race condition between adding the chip (and hence exposing it via sysfs)
and enabling the clock, so perhaps an even better approach would be to
add more fine-grained clock management by enabling or disabling it only
when necessary (clock enables are reference counted, so ->request() and
->free() would probably work fine in this case).

This isn't a real objection, though. If you prefer to keep things simple
the current code is fine with me.

> +
> +		dev_err(&pdev->dev, "Failed to add PWM chip: %d\n", ret);
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, pwm);
> +
> +	return 0;
> +}
> +
> +static int berlin_pwm_remove(struct platform_device *pdev)
> +{
> +	struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = pwmchip_remove(&pwm->chip);
> +	if (ret)
> +		return ret;
> +
> +	clk_disable_unprepare(pwm->clk);

You might want to disable the clock regardless. The driver will be
unloaded regardless of whether pwmchip_remove() returns failure or
not. The above would leak a clock enable, which may not be what you
want.

> +	return 0;
> +}
> +
> +static struct platform_driver berlin_pwm_driver = {
> +	.probe	= berlin_pwm_probe,
> +	.remove	= berlin_pwm_remove,
> +	.driver	= {
> +		.name	= "berlin-pwm",
> +		.of_match_table = berlin_pwm_match,
> +	},

Please don't artificially pad. Single spaces around '=' is just fine.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-21  8:09     ` Thierry Reding
@ 2015-09-21 21:05       ` Sebastian Hesselbarth
  0 siblings, 0 replies; 12+ messages in thread
From: Sebastian Hesselbarth @ 2015-09-21 21:05 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Antoine Tenart, zmxu, jszhang, linux-arm-kernel, linux-pwm, linux-kernel

On 21.09.2015 10:09, Thierry Reding wrote:
> On Sun, Sep 20, 2015 at 08:13:48PM +0200, Sebastian Hesselbarth wrote:
>> On 17.09.2015 12:13, Antoine Tenart wrote:
>>> Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
>>> controller has 4 channels.
>>>
>>> Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
>>> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>>
>> Thierry,
>>
>> if you are also fine with the driver, please let me know if you want
>> me to take the driver through berlin-tree or if you are taking it.
>>
>> I am taking the binding docs and dts changes anyway.
>
> Sorry but no. The binding documentation needs to go into the same tree
> as the driver because they need to be kept in sync. We can't have DT
> binding documentation go into platform trees if the driver implementing
> the binding hasn't been merged.

Ok, sometimes sub-maintainers do not want to care about the DT stuff,
so I offered to take them. I am very fine with keeping binding and
driver together.

Sebastian


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/5] pwm: add the Berlin pwm controller driver
  2015-09-21  8:40   ` Thierry Reding
@ 2015-09-25  9:15     ` Antoine Tenart
  0 siblings, 0 replies; 12+ messages in thread
From: Antoine Tenart @ 2015-09-25  9:15 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Antoine Tenart, sebastian.hesselbarth, zmxu, jszhang,
	linux-arm-kernel, linux-pwm, linux-kernel

On Mon, Sep 21, 2015 at 10:40:08AM +0200, Thierry Reding wrote:
> On Thu, Sep 17, 2015 at 12:13:04PM +0200, Antoine Tenart wrote:
> > +
> > +#define BERLIN_PWM_EN			0x0
> > +#define BERLIN_PWM_CONTROL		0x4
> > +#define BERLIN_PWM_DUTY			0x8
> > +#define BERLIN_PWM_TCNT			0xc
> > +
> > +#define BERLIN_PWM_ENABLE		BIT(0)
> > +#define BERLIN_PWM_INVERT_POLARITY	BIT(3)
> > +#define BERLIN_PWM_PRESCALE_MASK	0x7
> > +#define BERLIN_PWM_PRESCALE_MAX		4096
> > +#define BERLIN_PWM_MAX_TCNT		65535
> 
> It'd be nice to see some sort of connection between the register
> definitions and which fields belong to them.

Something like:

#define BERLIN_PWM_EN		0x0
#define  BERLIN_PWM_ENABLE	BIT(0)
#define BERLIN_PWM_CONTROL	0x4
...

> > +struct berlin_pwm_chip {
> > +	struct pwm_chip chip;
> > +	struct clk *clk;
> > +	void __iomem *base;
> > +	spinlock_t lock;
> 
> I don't think that lock is necessary here. You have per-channel
> registers and each channel can only be used by one consumer at a time
> anyway.

Sure. I'll make some tests and remove the lock if possible.

> > +#define to_berlin_pwm_chip(chip)	\
> > +	container_of((chip), struct berlin_pwm_chip, chip)
> > +
> > +#define berlin_pwm_readl(chip, channel, offset)		\
> > +	readl_relaxed((chip)->base + (channel) * 0x10 + offset)
> > +#define berlin_pwm_writel(val, chip, channel, offset)	\
> > +	writel_relaxed(val, (chip)->base + (channel) * 0x10 + offset)
> 
> These should be static inline functions. Also I think for
> berlin_pwm_writel() val should come after chip and channel to preserve a
> more natural ordering of parameters.

What's the benefit of using static inline functions here?

I'm not convinced having val after chip and channel is more natural, but
this is not a big matter. I'll update.

> > +
> > +/* prescaler table: {1, 4, 8, 16, 64, 256, 1024, 4096} */
> > +static const u32 prescaler_diff_table[] = {
> > +	1, 4, 2, 2, 4, 4, 4, 4,
> > +};
> 
> I don't see any relationship between these values and the prescaler
> table given in the comment. Please expand the comment to explain the
> connection.
> 
> After reading the remainder of the code, I see that the values in this
> table are the multiplication factors for each of the prescalers. It
> shouldn't be necessary to read the code to find out, so please clarify
> in the comment (and perhaps rename the table to something more related
> to its purpose, such as prescale_factors).

Will do.

> Perhaps an even more easily digestible alternative would be to make this
> a list of prescaler values and then use the values directly to compute
> the number of cycles rather than iteratively dividing and needing this
> unintuitive mapping.

Would something like the following be better?

"""
static const prescaler_table = {1, 4, 8, 16, 64, 256, 1024, 4096};
unsigned int prescale;
u64 tmp;

for (prescale = 0; prescale < ARRAY_SIZE(prescaler_table); prescale++) {
	tmp = cycles;
	do_div(tmp, prescaler_table[prescale]);

	if (tmp <= BERLIN_PWM_MAX_TCNT)
		break;
}

if (tmp > BERLIN_PWM_MAX_TCNT)
	return -ERANGE;

cycles = tmp;
"""

I personally prefer the prescale factors implementation, but I admit
this is maybe more readable.

> > +
> > +	while (cycles > BERLIN_PWM_MAX_TCNT)
> > +		do_div(cycles, prescaler_diff_table[++prescale]);
> 
> Don't you need to make sure that prescale doesn't exceed the table size?

Sure.

> > +
> > +	ret = pwmchip_add(&pwm->chip);
> > +	if (ret < 0) {
> > +		clk_disable_unprepare(pwm->clk);
> 
> Why not enable the clock until after successful registration? It doesn't
> seem like you need access before that. Doing so would introduce a subtle
> race condition between adding the chip (and hence exposing it via sysfs)
> and enabling the clock, so perhaps an even better approach would be to
> add more fine-grained clock management by enabling or disabling it only
> when necessary (clock enables are reference counted, so ->request() and
> ->free() would probably work fine in this case).
> 
> This isn't a real objection, though. If you prefer to keep things simple
> the current code is fine with me.

That was the idea. We may update this latter.

> > +static int berlin_pwm_remove(struct platform_device *pdev)
> > +{
> > +	struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
> > +	int ret;
> > +
> > +	ret = pwmchip_remove(&pwm->chip);
> > +	if (ret)
> > +		return ret;
> > +
> > +	clk_disable_unprepare(pwm->clk);
> 
> You might want to disable the clock regardless. The driver will be
> unloaded regardless of whether pwmchip_remove() returns failure or
> not. The above would leak a clock enable, which may not be what you
> want.

Yes, I'll call clk_disable_unprepare() regardless of what pwmchip_remove()
returns.

Thanks for the review!

Antoine

-- 
Antoine Ténart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-09-25  9:15 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-17 10:13 [PATCH v6 0/5] ARM: berlin: PWM support Antoine Tenart
2015-09-17 10:13 ` [PATCH v6 1/5] pwm: add the Berlin pwm controller driver Antoine Tenart
2015-09-20 18:13   ` Sebastian Hesselbarth
2015-09-21  8:09     ` Thierry Reding
2015-09-21 21:05       ` Sebastian Hesselbarth
2015-09-21  8:40   ` Thierry Reding
2015-09-25  9:15     ` Antoine Tenart
2015-09-17 10:13 ` [PATCH v6 2/5] Documentation: bindings: document the Berlin PWM driver Antoine Tenart
2015-09-21  8:14   ` Thierry Reding
2015-09-17 10:13 ` [PATCH v6 3/5] ARM: berlin: add a PWM node on the BG2Q Antoine Tenart
2015-09-17 10:13 ` [PATCH v6 4/5] ARM: berlin: add a PWM node on the BG2 Antoine Tenart
2015-09-17 10:13 ` [PATCH v6 5/5] ARM: berlin: add a PWM node on the BG2CD Antoine Tenart

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).