From d9c6ff8c2524c66801d9e1f9eedfc102a6464498 Mon Sep 17 00:00:00 2001
From: Imre Kaloz <kaloz@openwrt.org>
Date: Sun, 25 Jan 2015 15:36:47 +0000
Subject: mvebu: gpio based pwm support

Signed-off-by: Imre Kaloz <kaloz@openwrt.org>

SVN-Revision: 44129
---
 target/linux/mvebu/config-3.18                     |   3 +
 .../200-gpio_mvebu_checkpatch_fixes.patch          | 223 +++++++++++
 ...201-gpio_mvebu_fix_probe_cleanup_on_error.patch |  63 +++
 .../202-gpio_mvebu_add_limited_pwm_support.patch   | 433 +++++++++++++++++++++
 ..._extend_mvebu_gpio_documentation_with_pwm.patch |  52 +++
 ...mvebu_xp_add_pwm_properties_to_dtsi_files.patch | 149 +++++++
 .../205-arm_mvebu_enable_pwm_in_defconfig.patch    |  18 +
 ...rt1900ac_use_pwm-fan_rather_than_gpio-fan.patch |  28 ++
 8 files changed, 969 insertions(+)
 create mode 100644 target/linux/mvebu/patches-3.18/200-gpio_mvebu_checkpatch_fixes.patch
 create mode 100644 target/linux/mvebu/patches-3.18/201-gpio_mvebu_fix_probe_cleanup_on_error.patch
 create mode 100644 target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch
 create mode 100644 target/linux/mvebu/patches-3.18/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
 create mode 100644 target/linux/mvebu/patches-3.18/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
 create mode 100644 target/linux/mvebu/patches-3.18/205-arm_mvebu_enable_pwm_in_defconfig.patch
 create mode 100644 target/linux/mvebu/patches-3.18/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch

diff --git a/target/linux/mvebu/config-3.18 b/target/linux/mvebu/config-3.18
index 9b2b4800fe..068d0fd064 100644
--- a/target/linux/mvebu/config-3.18
+++ b/target/linux/mvebu/config-3.18
@@ -142,6 +142,7 @@ CONFIG_GPIOLIB=y
 CONFIG_GPIO_DEVRES=y
 CONFIG_GPIO_GENERIC=y
 CONFIG_GPIO_MVEBU=y
+CONFIG_GPIO_MVEBU_PWM=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_HANDLE_DOMAIN_IRQ=y
 CONFIG_HARDIRQS_SW_RESEND=y
@@ -291,6 +292,8 @@ CONFIG_PJ4B_ERRATA_4742=y
 # CONFIG_PL310_ERRATA_769419 is not set
 CONFIG_PLAT_ORION=y
 CONFIG_PM_OPP=y
+CONFIG_PWM=y
+# CONFIG_PWM_FSL_FTM is not set
 # CONFIG_PREEMPT_RCU is not set
 CONFIG_RCU_STALL_COMMON=y
 CONFIG_RFS_ACCEL=y
diff --git a/target/linux/mvebu/patches-3.18/200-gpio_mvebu_checkpatch_fixes.patch b/target/linux/mvebu/patches-3.18/200-gpio_mvebu_checkpatch_fixes.patch
new file mode 100644
index 0000000000..844df4f6f5
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/200-gpio_mvebu_checkpatch_fixes.patch
@@ -0,0 +1,223 @@
+Wrap some long lines.
+Prefer seq_puts() over seq_printf().
+space to tab conversions.
+Spelling error fix.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ drivers/gpio/gpio-mvebu.c | 77 ++++++++++++++++++++++++++---------------------
+ 1 file changed, 42 insertions(+), 35 deletions(-)
+
+--- a/drivers/gpio/gpio-mvebu.c
++++ b/drivers/gpio/gpio-mvebu.c
+@@ -59,7 +59,7 @@
+ #define GPIO_LEVEL_MASK_OFF	0x001c
+ 
+ /* The MV78200 has per-CPU registers for edge mask and level mask */
+-#define GPIO_EDGE_MASK_MV78200_OFF(cpu)   ((cpu) ? 0x30 : 0x18)
++#define GPIO_EDGE_MASK_MV78200_OFF(cpu)	  ((cpu) ? 0x30 : 0x18)
+ #define GPIO_LEVEL_MASK_MV78200_OFF(cpu)  ((cpu) ? 0x34 : 0x1C)
+ 
+ /* The Armada XP has per-CPU registers for interrupt cause, interrupt
+@@ -69,11 +69,11 @@
+ #define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu)  (0x10 + (cpu) * 0x4)
+ #define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
+ 
+-#define MVEBU_GPIO_SOC_VARIANT_ORION    0x1
+-#define MVEBU_GPIO_SOC_VARIANT_MV78200  0x2
++#define MVEBU_GPIO_SOC_VARIANT_ORION	0x1
++#define MVEBU_GPIO_SOC_VARIANT_MV78200	0x2
+ #define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
+ 
+-#define MVEBU_MAX_GPIO_PER_BANK         32
++#define MVEBU_MAX_GPIO_PER_BANK		32
+ 
+ struct mvebu_gpio_chip {
+ 	struct gpio_chip   chip;
+@@ -82,9 +82,9 @@ struct mvebu_gpio_chip {
+ 	void __iomem	  *percpu_membase;
+ 	int		   irqbase;
+ 	struct irq_domain *domain;
+-	int                soc_variant;
++	int		   soc_variant;
+ 
+-	/* Used to preserve GPIO registers accross suspend/resume */
++	/* Used to preserve GPIO registers across suspend/resume */
+ 	u32                out_reg;
+ 	u32                io_conf_reg;
+ 	u32                blink_en_reg;
+@@ -107,7 +107,8 @@ static inline void __iomem *mvebu_gpiore
+ 	return mvchip->membase + GPIO_BLINK_EN_OFF;
+ }
+ 
+-static inline void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
++static inline void __iomem *
++mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
+ {
+ 	return mvchip->membase + GPIO_IO_CONF_OFF;
+ }
+@@ -117,12 +118,14 @@ static inline void __iomem *mvebu_gpiore
+ 	return mvchip->membase + GPIO_IN_POL_OFF;
+ }
+ 
+-static inline void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
++static inline void __iomem *
++mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
+ {
+ 	return mvchip->membase + GPIO_DATA_IN_OFF;
+ }
+ 
+-static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
++static inline void __iomem *
++mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
+ {
+ 	int cpu;
+ 
+@@ -132,13 +135,15 @@ static inline void __iomem *mvebu_gpiore
+ 		return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
+ 	case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ 		cpu = smp_processor_id();
+-		return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
++		return mvchip->percpu_membase +
++			GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
+ 	default:
+ 		BUG();
+ 	}
+ }
+ 
+-static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
++static inline void __iomem *
++mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
+ {
+ 	int cpu;
+ 
+@@ -150,7 +155,8 @@ static inline void __iomem *mvebu_gpiore
+ 		return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu);
+ 	case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ 		cpu = smp_processor_id();
+-		return mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
++		return mvchip->percpu_membase +
++			GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
+ 	default:
+ 		BUG();
+ 	}
+@@ -168,7 +174,8 @@ static void __iomem *mvebu_gpioreg_level
+ 		return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu);
+ 	case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ 		cpu = smp_processor_id();
+-		return mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
++		return mvchip->percpu_membase +
++			GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
+ 	default:
+ 		BUG();
+ 	}
+@@ -364,22 +371,22 @@ static void mvebu_gpio_level_irq_unmask(
+  * value of the line or the opposite value.
+  *
+  * Level IRQ handlers: DATA_IN is used directly as cause register.
+- *                     Interrupt are masked by LEVEL_MASK registers.
++ *		       Interrupt are masked by LEVEL_MASK registers.
+  * Edge IRQ handlers:  Change in DATA_IN are latched in EDGE_CAUSE.
+- *                     Interrupt are masked by EDGE_MASK registers.
++ *		       Interrupt are masked by EDGE_MASK registers.
+  * Both-edge handlers: Similar to regular Edge handlers, but also swaps
+- *                     the polarity to catch the next line transaction.
+- *                     This is a race condition that might not perfectly
+- *                     work on some use cases.
++ *		       the polarity to catch the next line transaction.
++ *		       This is a race condition that might not perfectly
++ *		       work on some use cases.
+  *
+  * Every eight GPIO lines are grouped (OR'ed) before going up to main
+  * cause register.
+  *
+- *                    EDGE  cause    mask
+- *        data-in   /--------| |-----| |----\
+- *     -----| |-----                         ---- to main cause reg
+- *           X      \----------------| |----/
+- *        polarity    LEVEL          mask
++ *		      EDGE  cause    mask
++ *	  data-in   /--------| |-----| |----\
++ *     -----| |-----			     ---- to main cause reg
++ *	     X	    \----------------| |----/
++ *	  polarity    LEVEL	     mask
+  *
+  ****************************************************************************/
+ 
+@@ -394,9 +401,8 @@ static int mvebu_gpio_irq_set_type(struc
+ 	pin = d->hwirq;
+ 
+ 	u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin);
+-	if (!u) {
++	if (!u)
+ 		return -EINVAL;
+-	}
+ 
+ 	type &= IRQ_TYPE_SENSE_MASK;
+ 	if (type == IRQ_TYPE_NONE)
+@@ -529,13 +535,13 @@ static void mvebu_gpio_dbg_show(struct s
+ 			   (data_in ^ in_pol) & msk  ? "hi" : "lo",
+ 			   in_pol & msk ? "lo" : "hi");
+ 		if (!((edg_msk | lvl_msk) & msk)) {
+-			seq_printf(s, " disabled\n");
++			seq_puts(s, " disabled\n");
+ 			continue;
+ 		}
+ 		if (edg_msk & msk)
+-			seq_printf(s, " edge ");
++			seq_puts(s, " edge ");
+ 		if (lvl_msk & msk)
+-			seq_printf(s, " level");
++			seq_puts(s, " level");
+ 		seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear  ");
+ 	}
+ }
+@@ -546,15 +552,15 @@ static void mvebu_gpio_dbg_show(struct s
+ static const struct of_device_id mvebu_gpio_of_match[] = {
+ 	{
+ 		.compatible = "marvell,orion-gpio",
+-		.data       = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
++		.data	    = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
+ 	},
+ 	{
+ 		.compatible = "marvell,mv78200-gpio",
+-		.data       = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
++		.data	    = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
+ 	},
+ 	{
+ 		.compatible = "marvell,armadaxp-gpio",
+-		.data       = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
++		.data	    = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
+ 	},
+ 	{
+ 		/* sentinel */
+@@ -668,7 +674,8 @@ static int mvebu_gpio_probe(struct platf
+ 	else
+ 		soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
+ 
+-	mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL);
++	mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
++			      GFP_KERNEL);
+ 	if (!mvchip)
+ 		return -ENOMEM;
+ 
+@@ -767,8 +774,8 @@ static int mvebu_gpio_probe(struct platf
+ 	 * interrupt handlers, with each handler dealing with 8 GPIO
+ 	 * pins. */
+ 	for (i = 0; i < 4; i++) {
+-		int irq;
+-		irq = platform_get_irq(pdev, i);
++		int irq = platform_get_irq(pdev, i);
++
+ 		if (irq < 0)
+ 			continue;
+ 		irq_set_handler_data(irq, mvchip);
+@@ -827,7 +834,7 @@ static int mvebu_gpio_probe(struct platf
+ 
+ static struct platform_driver mvebu_gpio_driver = {
+ 	.driver		= {
+-		.name	        = "mvebu-gpio",
++		.name		= "mvebu-gpio",
+ 		.of_match_table = mvebu_gpio_of_match,
+ 	},
+ 	.probe		= mvebu_gpio_probe,
diff --git a/target/linux/mvebu/patches-3.18/201-gpio_mvebu_fix_probe_cleanup_on_error.patch b/target/linux/mvebu/patches-3.18/201-gpio_mvebu_fix_probe_cleanup_on_error.patch
new file mode 100644
index 0000000000..aecf8e3c12
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/201-gpio_mvebu_fix_probe_cleanup_on_error.patch
@@ -0,0 +1,63 @@
+Ensure that when there is an error during probe that the gpiochip is
+removed and the generic irq chip is removed.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ drivers/gpio/gpio-mvebu.c | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpio/gpio-mvebu.c
++++ b/drivers/gpio/gpio-mvebu.c
+@@ -667,6 +667,7 @@ static int mvebu_gpio_probe(struct platf
+ 	unsigned int ngpios;
+ 	int soc_variant;
+ 	int i, cpu, id;
++	int err;
+ 
+ 	match = of_match_device(mvebu_gpio_of_match, &pdev->dev);
+ 	if (match)
+@@ -785,14 +786,16 @@ static int mvebu_gpio_probe(struct platf
+ 	mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
+ 	if (mvchip->irqbase < 0) {
+ 		dev_err(&pdev->dev, "no irqs\n");
+-		return mvchip->irqbase;
++		err = mvchip->irqbase;
++		goto err_gpiochip_add;
+ 	}
+ 
+ 	gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
+ 				    mvchip->membase, handle_level_irq);
+ 	if (!gc) {
+ 		dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
+-		return -ENOMEM;
++		err = -ENOMEM;
++		goto err_gpiochip_add;
+ 	}
+ 
+ 	gc->private = mvchip;
+@@ -823,13 +826,21 @@ static int mvebu_gpio_probe(struct platf
+ 	if (!mvchip->domain) {
+ 		dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
+ 			mvchip->chip.label);
+-		irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
+-					IRQ_LEVEL | IRQ_NOPROBE);
+-		kfree(gc);
+-		return -ENODEV;
++		err = -ENODEV;
++		goto err_generic_chip;
+ 	}
+ 
+ 	return 0;
++
++err_generic_chip:
++	irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
++				IRQ_LEVEL | IRQ_NOPROBE);
++	kfree(gc);
++
++err_gpiochip_add:
++	gpiochip_remove(&mvchip->chip);
++
++	return err;
+ }
+ 
+ static struct platform_driver mvebu_gpio_driver = {
diff --git a/target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch b/target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch
new file mode 100644
index 0000000000..52ae90bb2d
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch
@@ -0,0 +1,433 @@
+Armada 370/XP devices can 'blink' gpio lines with a configurable on
+and off period. This can be modelled as a PWM.
+
+However, there are only two sets of PWM configuration registers for
+all the gpio lines. This driver simply allows a single gpio line per
+gpio chip of 32 lines to be used as a PWM. Attempts to use more return
+EBUSY.
+
+Due to the interleaving of registers it is not simple to separate the
+PWM driver from the gpio driver. Thus the gpio driver has been
+extended with a PWM driver.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ drivers/gpio/Kconfig          |   5 ++
+ drivers/gpio/Makefile         |   1 +
+ drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/gpio/gpio-mvebu.c     |  37 +++-----
+ drivers/gpio/gpio-mvebu.h     |  79 +++++++++++++++++
+ 5 files changed, 299 insertions(+), 25 deletions(-)
+ create mode 100644 drivers/gpio/gpio-mvebu-pwm.c
+ create mode 100644 drivers/gpio/gpio-mvebu.h
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -223,6 +223,11 @@ config GPIO_MVEBU
+ 	select GPIO_GENERIC
+ 	select GENERIC_IRQ_CHIP
+ 
++config GPIO_MVEBU_PWM
++	def_bool y
++	depends on GPIO_MVEBU
++	depends on PWM
++
+ config GPIO_MXC
+ 	def_bool y
+ 	depends on ARCH_MXC
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -58,6 +58,7 @@ obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
+ obj-$(CONFIG_GPIO_MSM_V1)	+= gpio-msm-v1.o
+ obj-$(CONFIG_GPIO_MSM_V2)	+= gpio-msm-v2.o
+ obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
++obj-$(CONFIG_GPIO_MVEBU_PWM)	+= gpio-mvebu-pwm.o
+ obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
+ obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
+ obj-$(CONFIG_GPIO_OCTEON)	+= gpio-octeon.o
+--- /dev/null
++++ b/drivers/gpio/gpio-mvebu-pwm.c
+@@ -0,0 +1,202 @@
++#include "asm/io.h"
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/gpio.h>
++#include <linux/pwm.h>
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include "gpio-mvebu.h"
++#include "gpiolib.h"
++static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip)
++{
++	return mvchip->membase + GPIO_BLINK_CNT_SELECT;
++}
++
++static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
++{
++	return container_of(chip, struct mvebu_pwm, chip);
++}
++
++static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm)
++{
++	return container_of(pwm, struct mvebu_gpio_chip, pwm);
++}
++
++static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++	struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
++	unsigned long flags;
++	int ret = 0;
++
++	spin_lock_irqsave(&pwm->lock, flags);
++	if (pwm->used) {
++		ret = -EBUSY;
++	} else {
++		if (!desc) {
++			ret = -ENODEV;
++			goto out;
++		}
++		ret = gpiod_request(desc, "mvebu-pwm");
++		if (ret)
++			goto out;
++
++		ret = gpiod_direction_output(desc, 0);
++		if (ret) {
++			gpiod_free(desc);
++			goto out;
++		}
++
++		pwm->pin = pwmd->pwm - mvchip->chip.base;
++		pwm->used = true;
++	}
++
++out:
++	spin_unlock_irqrestore(&pwm->lock, flags);
++	return ret;
++}
++
++static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
++	unsigned long flags;
++
++	spin_lock_irqsave(&pwm->lock, flags);
++	gpiod_free(desc);
++	pwm->used = false;
++	spin_unlock_irqrestore(&pwm->lock, flags);
++}
++
++static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd,
++			    int duty_ns, int period_ns)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++	unsigned int on, off;
++	unsigned long long val;
++	u32 u;
++
++	val = (unsigned long long) pwm->clk_rate * duty_ns;
++	do_div(val, NSEC_PER_SEC);
++	if (val > UINT_MAX)
++		return -EINVAL;
++	if (val)
++		on = val;
++	else
++		on = 1;
++
++	val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns);
++	do_div(val, NSEC_PER_SEC);
++	if (val > UINT_MAX)
++		return -EINVAL;
++	if (val)
++		off = val;
++	else
++		off = 1;
++
++	u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
++	u &= ~(1 << pwm->pin);
++	u |= (pwm->id << pwm->pin);
++	writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip));
++
++	writel_relaxed(on, pwm->membase + BLINK_ON_DURATION);
++	writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION);
++
++	return 0;
++}
++
++static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++
++	mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1);
++
++	return 0;
++}
++
++static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++
++	mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0);
++}
++
++static const struct pwm_ops mvebu_pwm_ops = {
++	.request = mvebu_pwm_request,
++	.free = mvebu_pwm_free,
++	.config = mvebu_pwm_config,
++	.enable = mvebu_pwm_enable,
++	.disable = mvebu_pwm_disable,
++	.owner = THIS_MODULE,
++};
++
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
++{
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++
++	pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
++	pwm->blink_on_duration =
++		readl_relaxed(pwm->membase + BLINK_ON_DURATION);
++	pwm->blink_off_duration =
++		readl_relaxed(pwm->membase + BLINK_OFF_DURATION);
++}
++
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
++{
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++
++	writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip));
++	writel_relaxed(pwm->blink_on_duration,
++		       pwm->membase + BLINK_ON_DURATION);
++	writel_relaxed(pwm->blink_off_duration,
++		       pwm->membase + BLINK_OFF_DURATION);
++}
++
++/*
++ * Armada 370/XP has simple PWM support for gpio lines. Other SoCs
++ * don't have this hardware. So if we don't have the necessary
++ * resource, it is not an error.
++ */
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id)
++{
++	struct device *dev = &pdev->dev;
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++	struct resource *res;
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
++	if (!res)
++		return 0;
++
++	mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(mvchip->pwm.membase))
++		return PTR_ERR(mvchip->percpu_membase);
++
++	if (id < 0 || id > 1)
++		return -EINVAL;
++	pwm->id = id;
++
++	if (IS_ERR(mvchip->clk))
++		return PTR_ERR(mvchip->clk);
++
++	pwm->clk_rate = clk_get_rate(mvchip->clk);
++	if (!pwm->clk_rate) {
++		dev_err(dev, "failed to get clock rate\n");
++		return -EINVAL;
++	}
++
++	pwm->chip.dev = dev;
++	pwm->chip.ops = &mvebu_pwm_ops;
++	pwm->chip.base = mvchip->chip.base;
++	pwm->chip.npwm = mvchip->chip.ngpio;
++	pwm->chip.can_sleep = false;
++
++	spin_lock_init(&pwm->lock);
++
++	return pwmchip_add(&pwm->chip);
++}
+--- a/drivers/gpio/gpio-mvebu.c
++++ b/drivers/gpio/gpio-mvebu.c
+@@ -42,10 +42,11 @@
+ #include <linux/io.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_device.h>
++#include <linux/pwm.h>
+ #include <linux/clk.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/irqchip/chained_irq.h>
+-
++#include "gpio-mvebu.h"
+ /*
+  * GPIO unit register offsets.
+  */
+@@ -75,24 +76,6 @@
+ 
+ #define MVEBU_MAX_GPIO_PER_BANK		32
+ 
+-struct mvebu_gpio_chip {
+-	struct gpio_chip   chip;
+-	spinlock_t	   lock;
+-	void __iomem	  *membase;
+-	void __iomem	  *percpu_membase;
+-	int		   irqbase;
+-	struct irq_domain *domain;
+-	int		   soc_variant;
+-
+-	/* Used to preserve GPIO registers across suspend/resume */
+-	u32                out_reg;
+-	u32                io_conf_reg;
+-	u32                blink_en_reg;
+-	u32                in_pol_reg;
+-	u32                edge_mask_regs[4];
+-	u32                level_mask_regs[4];
+-};
+-
+ /*
+  * Functions returning addresses of individual registers for a given
+  * GPIO controller.
+@@ -228,7 +211,7 @@ static int mvebu_gpio_get(struct gpio_ch
+ 	return (u >> pin) & 1;
+ }
+ 
+-static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
+ {
+ 	struct mvebu_gpio_chip *mvchip =
+ 		container_of(chip, struct mvebu_gpio_chip, chip);
+@@ -609,6 +592,8 @@ static int mvebu_gpio_suspend(struct pla
+ 		BUG();
+ 	}
+ 
++	mvebu_pwm_suspend(mvchip);
++
+ 	return 0;
+ }
+ 
+@@ -652,6 +637,8 @@ static int mvebu_gpio_resume(struct plat
+ 		BUG();
+ 	}
+ 
++	mvebu_pwm_resume(mvchip);
++
+ 	return 0;
+ }
+ 
+@@ -663,7 +650,6 @@ static int mvebu_gpio_probe(struct platf
+ 	struct resource *res;
+ 	struct irq_chip_generic *gc;
+ 	struct irq_chip_type *ct;
+-	struct clk *clk;
+ 	unsigned int ngpios;
+ 	int soc_variant;
+ 	int i, cpu, id;
+@@ -693,10 +679,10 @@ static int mvebu_gpio_probe(struct platf
+ 		return id;
+ 	}
+ 
+-	clk = devm_clk_get(&pdev->dev, NULL);
++	mvchip->clk = devm_clk_get(&pdev->dev, NULL);
+ 	/* Not all SoCs require a clock.*/
+-	if (!IS_ERR(clk))
+-		clk_prepare_enable(clk);
++	if (!IS_ERR(mvchip->clk))
++		clk_prepare_enable(mvchip->clk);
+ 
+ 	mvchip->soc_variant = soc_variant;
+ 	mvchip->chip.label = dev_name(&pdev->dev);
+@@ -830,7 +816,8 @@ static int mvebu_gpio_probe(struct platf
+ 		goto err_generic_chip;
+ 	}
+ 
+-	return 0;
++	/* Armada 370/XP has simple PWM support for gpio lines */
++	return mvebu_pwm_probe(pdev, mvchip, id);
+ 
+ err_generic_chip:
+ 	irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
+--- /dev/null
++++ b/drivers/gpio/gpio-mvebu.h
+@@ -0,0 +1,79 @@
++/*
++ * Interface between MVEBU GPIO driver and PWM driver for GPIO pins
++ *
++ * Copyright (C) 2015, Andrew Lunn <andrew@lunn.ch>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef MVEBU_GPIO_PWM_H
++#define MVEBU_GPIO_PWM_H
++
++#define BLINK_ON_DURATION	0x0
++#define BLINK_OFF_DURATION	0x4
++#define GPIO_BLINK_CNT_SELECT	0x0020
++
++struct mvebu_pwm {
++	void __iomem	*membase;
++	unsigned long	 clk_rate;
++	bool		 used;
++	unsigned	 pin;
++	struct pwm_chip	 chip;
++	int		 id;
++	spinlock_t	 lock;
++
++	/* Used to preserve GPIO/PWM registers across suspend /
++	 * resume */
++	u32		 blink_select;
++	u32		 blink_on_duration;
++	u32		 blink_off_duration;
++};
++
++struct mvebu_gpio_chip {
++	struct gpio_chip   chip;
++	spinlock_t	   lock;
++	void __iomem	  *membase;
++	void __iomem	  *percpu_membase;
++	int		   irqbase;
++	struct irq_domain *domain;
++	int		   soc_variant;
++	struct clk	  *clk;
++#ifdef CONFIG_PWM
++	struct mvebu_pwm pwm;
++#endif
++	/* Used to preserve GPIO registers across suspend/resume */
++	u32		   out_reg;
++	u32		   io_conf_reg;
++	u32		   blink_en_reg;
++	u32		   in_pol_reg;
++	u32		   edge_mask_regs[4];
++	u32		   level_mask_regs[4];
++};
++
++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value);
++
++#ifdef CONFIG_PWM
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id);
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip);
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip);
++#else
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id)
++{
++	return 0;
++}
++
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
++{
++}
++
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
++{
++}
++#endif
++#endif
diff --git a/target/linux/mvebu/patches-3.18/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch b/target/linux/mvebu/patches-3.18/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
new file mode 100644
index 0000000000..48f93944bf
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
@@ -0,0 +1,52 @@
+Document the optional parameters needed for PWM operation of gpio
+lines.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ .../devicetree/bindings/gpio/gpio-mvebu.txt        | 31 ++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
++++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
+@@ -38,6 +38,23 @@ Required properties:
+ - #gpio-cells: Should be two. The first cell is the pin number. The
+   second cell is reserved for flags, unused at the moment.
+ 
++Optional properties:
++
++In order to use the gpio lines in PWM mode, some additional optional
++properties are required. Only Armada 370 and XP supports these
++properties.
++
++- reg: an additional register set is needed, for the GPIO Blink
++  Counter on/off registers.
++
++- reg-names: Must contain an entry "pwm" corresponding to the
++  additional register range needed for pwm operation.
++
++- #pwm-cells: Should be two. The first cell is the pin number. The
++  second cell is reserved for flags, unused at the moment.
++
++- clocks: Must be a phandle to the clock for the gpio controller.
++
+ Example:
+ 
+ 		gpio0: gpio@d0018100 {
+@@ -51,3 +68,17 @@ Example:
+ 			#interrupt-cells = <2>;
+ 			interrupts = <16>, <17>, <18>, <19>;
+ 		};
++
++		gpio1: gpio@18140 {
++			compatible = "marvell,orion-gpio";
++			reg = <0x18140 0x40>, <0x181c8 0x08>;
++			reg-names = "gpio", "pwm";
++			ngpios = <17>;
++			gpio-controller;
++			#gpio-cells = <2>;
++			#pwm-cells = <2>;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			interrupts = <87>, <88>, <89>;
++			clocks = <&coreclk 0>;
++		};
diff --git a/target/linux/mvebu/patches-3.18/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch b/target/linux/mvebu/patches-3.18/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
new file mode 100644
index 0000000000..9272d99072
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
@@ -0,0 +1,149 @@
+Add properties to the gpio nodes to allow them to be also used
+as pwm lines.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ arch/arm/boot/dts/armada-370.dtsi        | 10 ++++++++--
+ arch/arm/boot/dts/armada-xp-mv78230.dtsi | 10 ++++++++--
+ arch/arm/boot/dts/armada-xp-mv78260.dtsi |  8 ++++++--
+ arch/arm/boot/dts/armada-xp-mv78460.dtsi | 10 ++++++++--
+ 4 files changed, 30 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -109,24 +109,30 @@
+ 
+ 			gpio0: gpio@18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio@18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio@18180 {
+--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+@@ -169,24 +169,30 @@
+ 		internal-regs {
+ 			gpio0: gpio@18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio@18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <17>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>;
++				clocks = <&coreclk 0>;
+ 			};
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+@@ -253,24 +253,28 @@
+ 		internal-regs {
+ 			gpio0: gpio@18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio@18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio@18180 {
+--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+@@ -291,24 +291,30 @@
+ 		internal-regs {
+ 			gpio0: gpio@18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio@18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio@18180 {
diff --git a/target/linux/mvebu/patches-3.18/205-arm_mvebu_enable_pwm_in_defconfig.patch b/target/linux/mvebu/patches-3.18/205-arm_mvebu_enable_pwm_in_defconfig.patch
new file mode 100644
index 0000000000..1ff724c075
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/205-arm_mvebu_enable_pwm_in_defconfig.patch
@@ -0,0 +1,18 @@
+Now that the gpio driver also supports PWM operation, enable
+the PWM framework in mvebu_v7_defconfig.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ arch/arm/configs/mvebu_v7_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/configs/mvebu_v7_defconfig
++++ b/arch/arm/configs/mvebu_v7_defconfig
+@@ -109,6 +109,7 @@ CONFIG_DMADEVICES=y
+ CONFIG_MV_XOR=y
+ # CONFIG_IOMMU_SUPPORT is not set
+ CONFIG_MEMORY=y
++CONFIG_PWM=y
+ CONFIG_EXT4_FS=y
+ CONFIG_ISO9660_FS=y
+ CONFIG_JOLIET=y
diff --git a/target/linux/mvebu/patches-3.18/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch b/target/linux/mvebu/patches-3.18/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch
new file mode 100644
index 0000000000..8af5eba5b0
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch
@@ -0,0 +1,28 @@
+The mvebu gpio driver can also perform PWM on some pins. Us the
+pwm-fan driver to control the fan of the WRT1900AC, giving us fine
+grain control over its speed and hence noise.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ arch/arm/boot/dts/armada-xp-wrt1900ac.dts | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-xp-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-mamba.dts
+@@ -302,13 +302,11 @@
+ 		};
+ 	};
+ 
+-	gpio_fan {
++	pwm_fan {
+ 		/* SUNON HA4010V4-0000-C99 */
+-		compatible = "gpio-fan";
+-		gpios = <&gpio0 24 0>;
+ 
+-		gpio-fan,speed-map = <0    0
+-				      4500 1>;
++		compatible = "pwm-fan";
++		pwms = <&gpio0 24 4000 0>;
+ 	};
+ 
+ 	mvsw61xx {
-- 
cgit v1.2.3