summaryrefslogtreecommitdiff
path: root/target/linux/mvebu/patches-3.19
diff options
context:
space:
mode:
authorImre Kaloz <kaloz@openwrt.org>2015-01-25 15:37:31 +0000
committerImre Kaloz <kaloz@openwrt.org>2015-01-25 15:37:31 +0000
commit18012a41981270d25a4628846470b127467ae028 (patch)
tree33a046c32e2a3e140c5c2f7dc78dcdf9f4579db7 /target/linux/mvebu/patches-3.19
parentd9c6ff8c2524c66801d9e1f9eedfc102a6464498 (diff)
downloadmtk-20170518-18012a41981270d25a4628846470b127467ae028.zip
mtk-20170518-18012a41981270d25a4628846470b127467ae028.tar.gz
mtk-20170518-18012a41981270d25a4628846470b127467ae028.tar.bz2
mvebu: preliminary 3.19 support
Signed-off-by: Imre Kaloz <kaloz@openwrt.org> SVN-Revision: 44130
Diffstat (limited to 'target/linux/mvebu/patches-3.19')
-rw-r--r--target/linux/mvebu/patches-3.19/001-build_mamba_dts.patch10
-rw-r--r--target/linux/mvebu/patches-3.19/050-leds_tlc59116_document_binding.patch51
-rw-r--r--target/linux/mvebu/patches-3.19/051-leds_tlc59116_add_driver.patch297
-rw-r--r--target/linux/mvebu/patches-3.19/100-find_active_root.patch153
-rw-r--r--target/linux/mvebu/patches-3.19/102-revert_i2c_delay.patch15
-rw-r--r--target/linux/mvebu/patches-3.19/200-gpio_mvebu_checkpatch_fixes.patch223
-rw-r--r--target/linux/mvebu/patches-3.19/201-gpio_mvebu_fix_probe_cleanup_on_error.patch63
-rw-r--r--target/linux/mvebu/patches-3.19/202-gpio_mvebu_add_limited_pwm_support.patch433
-rw-r--r--target/linux/mvebu/patches-3.19/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch52
-rw-r--r--target/linux/mvebu/patches-3.19/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch149
-rw-r--r--target/linux/mvebu/patches-3.19/205-arm_mvebu_enable_pwm_in_defconfig.patch18
-rw-r--r--target/linux/mvebu/patches-3.19/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch28
12 files changed, 1492 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-3.19/001-build_mamba_dts.patch b/target/linux/mvebu/patches-3.19/001-build_mamba_dts.patch
new file mode 100644
index 0000000..1569c70
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/001-build_mamba_dts.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -541,6 +541,7 @@ dtb-$(CONFIG_MACH_ARMADA_XP) += \
+ armada-xp-db.dtb \
+ armada-xp-gp.dtb \
+ armada-xp-lenovo-ix4-300d.dtb \
++ armada-xp-mamba.dtb \
+ armada-xp-matrix.dtb \
+ armada-xp-netgear-rn2120.dtb \
+ armada-xp-openblocks-ax3-4.dtb \
diff --git a/target/linux/mvebu/patches-3.19/050-leds_tlc59116_document_binding.patch b/target/linux/mvebu/patches-3.19/050-leds_tlc59116_document_binding.patch
new file mode 100644
index 0000000..e55eca3
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/050-leds_tlc59116_document_binding.patch
@@ -0,0 +1,51 @@
+Document the binding for the TLC59116 LED driver.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ .../devicetree/bindings/leds/leds-tlc59116.txt | 40 ++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/leds/leds-tlc59116.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/leds/leds-tlc59116.txt
+@@ -0,0 +1,40 @@
++LEDs connected to tcl59116
++
++Required properties
++- compatible: should be "ti,tlc59116"
++- #address-cells: must be 1
++- #size-cells: must be 0
++- reg: typically 0x68
++
++Each led is represented as a sub-node of the ti,,tlc59116.
++See Documentation/devicetree/bindings/leds/common.txt
++
++LED sub-node properties:
++- reg: number of LED line, 0 to 15
++- label: (optional) name of LED
++- linux,default-trigger : (optional)
++
++Examples:
++
++tlc59116@68 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "ti,tlc59116";
++ reg = <0x68>;
++
++ wan@0 {
++ label = "wrt1900ac:amber:wan";
++ reg = <0x0>;
++ };
++
++ 2g@2 {
++ label = "wrt1900ac:white:2g";
++ reg = <0x2>;
++ };
++
++ alive@9 {
++ label = "wrt1900ac:green:alive";
++ reg = <0x9>;
++ linux,default_trigger = "heartbeat";
++ };
++};
diff --git a/target/linux/mvebu/patches-3.19/051-leds_tlc59116_add_driver.patch b/target/linux/mvebu/patches-3.19/051-leds_tlc59116_add_driver.patch
new file mode 100644
index 0000000..6aa57a3
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/051-leds_tlc59116_add_driver.patch
@@ -0,0 +1,297 @@
+The TLC59116 is an I2C bus controlled 16-channel LED driver. Each LED
+output has its own 8-bit fixed-frequency PWM controller to control the
+brightness of the LED.
+
+This is based on a driver from Belkin, but has been extensively
+rewritten.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+---
+ drivers/leds/Kconfig | 8 ++
+ drivers/leds/Makefile | 1 +
+ drivers/leds/leds-tlc59116.c | 252 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 261 insertions(+)
+ create mode 100644 drivers/leds/leds-tlc59116.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -457,6 +457,14 @@ config LEDS_TCA6507
+ LED driver chips accessed via the I2C bus.
+ Driver support brightness control and hardware-assisted blinking.
+
++config LEDS_TLC59116
++ tristate "LED driver for TLC59116F controllers"
++ depends on LEDS_CLASS && I2C
++ select REGMAP_I2C
++ help
++ This option enables support for Texas Instruments TLC59116F
++ LED controller.
++
+ config LEDS_MAX8997
+ tristate "LED support for MAX8997 PMIC"
+ depends on LEDS_CLASS && MFD_MAX8997
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp850
+ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
+ obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
+ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
++obj-$(CONFIG_LEDS_TLC59116) += leds-tlc59116.o
+ obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
+ obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
+ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
+--- /dev/null
++++ b/drivers/leds/leds-tlc59116.c
+@@ -0,0 +1,252 @@
++/*
++ * Copyright 2014 Belkin Inc.
++ * Copyright 2014 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 as published by
++ * the Free Software Foundation; version 2 of the License.
++ */
++
++#include <linux/i2c.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#define TLC59116_LEDS 16
++
++#define TLC59116_REG_MODE1 0x00
++#define MODE1_RESPON_ADDR_MASK 0xF0
++#define MODE1_NORMAL_MODE (0 << 4)
++#define MODE1_SPEED_MODE (1 << 4)
++
++#define TLC59116_REG_MODE2 0x01
++#define MODE2_DIM (0 << 5)
++#define MODE2_BLINK (1 << 5)
++#define MODE2_OCH_STOP (0 << 3)
++#define MODE2_OCH_ACK (1 << 3)
++
++#define TLC59116_REG_PWM(x) (0x02 + (x))
++
++#define TLC59116_REG_GRPPWM 0x12
++#define TLC59116_REG_GRPFREQ 0x13
++
++/* LED Driver Output State, determine the source that drives LED outputs */
++#define TLC59116_REG_LEDOUT(x) (0x14 + ((x) >> 2))
++#define TLC59116_LED_OFF 0x0 /* Output LOW */
++#define TLC59116_LED_ON 0x1 /* Output HI-Z */
++#define TLC59116_DIM 0x2 /* Dimming */
++#define TLC59116_BLINK 0x3 /* Blinking */
++#define LED_MASK 0x3
++
++#define ldev_to_led(c) container_of(c, struct tlc59116_led, ldev)
++#define work_to_led(work) container_of(work, struct tlc59116_led, work)
++
++struct tlc59116_led {
++ bool active;
++ struct regmap *regmap;
++ unsigned int led_no;
++ struct led_classdev ldev;
++ struct work_struct work;
++};
++
++struct tlc59116_priv {
++ struct tlc59116_led leds[TLC59116_LEDS];
++};
++
++static int
++tlc59116_set_mode(struct regmap *regmap, u8 mode)
++{
++ int err;
++ u8 val;
++
++ if ((mode != MODE2_DIM) && (mode != MODE2_BLINK))
++ mode = MODE2_DIM;
++
++ /* Configure MODE1 register */
++ err = regmap_write(regmap, TLC59116_REG_MODE1, MODE1_NORMAL_MODE);
++ if (err)
++ return err;
++
++ /* Configure MODE2 Reg */
++ val = MODE2_OCH_STOP | mode;
++
++ return regmap_write(regmap, TLC59116_REG_MODE2, val);
++}
++
++static int
++tlc59116_set_led(struct tlc59116_led *led, u8 val)
++{
++ struct regmap *regmap = led->regmap;
++ unsigned int i = (led->led_no % 4) * 2;
++ unsigned int addr = TLC59116_REG_LEDOUT(led->led_no);
++ unsigned int mask = LED_MASK << i;
++
++ val = val << i;
++
++ return regmap_update_bits(regmap, addr, mask, val);
++}
++
++static void
++tlc59116_led_work(struct work_struct *work)
++{
++ struct tlc59116_led *led = work_to_led(work);
++ struct regmap *regmap = led->regmap;
++ int err;
++ u8 pwm;
++
++ pwm = TLC59116_REG_PWM(led->led_no);
++ err = regmap_write(regmap, pwm, led->ldev.brightness);
++ if (err)
++ dev_err(led->ldev.dev, "Failed setting brightness\n");
++}
++
++static void
++tlc59116_led_set(struct led_classdev *led_cdev, enum led_brightness value)
++{
++ struct tlc59116_led *led = ldev_to_led(led_cdev);
++
++ led->ldev.brightness = value;
++ schedule_work(&led->work);
++}
++
++static void
++tlc59116_destroy_devices(struct tlc59116_priv *priv, unsigned int i)
++{
++ while (--i >= 0) {
++ if (priv->leds[i].active) {
++ led_classdev_unregister(&priv->leds[i].ldev);
++ cancel_work_sync(&priv->leds[i].work);
++ }
++ }
++}
++
++static int
++tlc59116_configure(struct device *dev,
++ struct tlc59116_priv *priv,
++ struct regmap *regmap)
++{
++ unsigned int i;
++ int err = 0;
++
++ tlc59116_set_mode(regmap, MODE2_DIM);
++ for (i = 0; i < TLC59116_LEDS; i++) {
++ struct tlc59116_led *led = &priv->leds[i];
++
++ if (!led->active)
++ continue;
++
++ led->regmap = regmap;
++ led->led_no = i;
++ led->ldev.brightness_set = tlc59116_led_set;
++ led->ldev.max_brightness = LED_FULL;
++ INIT_WORK(&led->work, tlc59116_led_work);
++ err = led_classdev_register(dev, &led->ldev);
++ if (err < 0) {
++ dev_err(dev, "couldn't register LED %s\n",
++ led->ldev.name);
++ goto exit;
++ }
++ tlc59116_set_led(led, TLC59116_DIM);
++ }
++
++ return 0;
++
++exit:
++ tlc59116_destroy_devices(priv, i);
++ return err;
++}
++
++static const struct regmap_config tlc59116_regmap = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ .max_register = 0x1e,
++};
++
++static int
++tlc59116_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct tlc59116_priv *priv = i2c_get_clientdata(client);
++ struct device *dev = &client->dev;
++ struct device_node *np = client->dev.of_node, *child;
++ struct regmap *regmap;
++ int err, count, reg;
++
++ count = of_get_child_count(np);
++ if (!count || count > TLC59116_LEDS)
++ return -EINVAL;
++
++ if (!i2c_check_functionality(client->adapter,
++ I2C_FUNC_SMBUS_BYTE_DATA))
++ return -EIO;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ regmap = devm_regmap_init_i2c(client, &tlc59116_regmap);
++ if (IS_ERR(regmap)) {
++ err = PTR_ERR(regmap);
++ dev_err(dev, "Failed to allocate register map: %d\n", err);
++ return err;
++ }
++
++ i2c_set_clientdata(client, priv);
++
++ for_each_child_of_node(np, child) {
++ err = of_property_read_u32(child, "reg", &reg);
++ if (err)
++ return err;
++ if (reg < 0 || reg >= TLC59116_LEDS)
++ return -EINVAL;
++ if (priv->leds[reg].active)
++ return -EINVAL;
++ priv->leds[reg].active = true;
++ priv->leds[reg].ldev.name =
++ of_get_property(child, "label", NULL) ? : child->name;
++ priv->leds[reg].ldev.default_trigger =
++ of_get_property(child, "linux,default-trigger", NULL);
++ }
++ return tlc59116_configure(dev, priv, regmap);
++}
++
++static int
++tlc59116_remove(struct i2c_client *client)
++{
++ struct tlc59116_priv *priv = i2c_get_clientdata(client);
++
++ tlc59116_destroy_devices(priv, TLC59116_LEDS);
++
++ return 0;
++}
++
++static const struct of_device_id of_tlc59116_leds_match[] = {
++ { .compatible = "ti,tlc59116", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, of_tlc59116_leds_match);
++
++static const struct i2c_device_id tlc59116_id[] = {
++ { "tlc59116" },
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, tlc59116_id);
++
++static struct i2c_driver tlc59116_driver = {
++ .driver = {
++ .name = "tlc59116",
++ .of_match_table = of_match_ptr(of_tlc59116_leds_match),
++ },
++ .probe = tlc59116_probe,
++ .remove = tlc59116_remove,
++ .id_table = tlc59116_id,
++};
++
++module_i2c_driver(tlc59116_driver);
++
++MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("TLC59116 LED driver");
diff --git a/target/linux/mvebu/patches-3.19/100-find_active_root.patch b/target/linux/mvebu/patches-3.19/100-find_active_root.patch
new file mode 100644
index 0000000..68826fc
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/100-find_active_root.patch
@@ -0,0 +1,153 @@
+The WRT1900AC among other Linksys routers uses a dual-firmware layout.
+The bootloader passes the active rootfs in bootargs and also sets the
+rootfstype to jffs2 - which is clearly something we don't want.
+
+Rename both root= and rootfstype= variables to avoid issues and also
+use the former to dynamically rename the active partition to "ubi".
+
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+
+--- a/arch/arm/boot/compressed/atags_to_fdt.c
++++ b/arch/arm/boot/compressed/atags_to_fdt.c
+@@ -66,6 +66,18 @@ static uint32_t get_cell_size(const void
+ return cell_size;
+ }
+
++static void mangle_bootargs(void *fdt, const char *fdt_cmdline)
++{
++ char *rootdev;
++ char *rootfs;
++
++ rootdev = strstr(fdt_cmdline, "root=/dev/mtdblock");
++ strncpy(rootdev, "uboot_active_root=", 18);
++
++ rootfs = strstr(fdt_cmdline, "rootfstype");
++ strncpy(rootfs, "origfstype", 10);
++}
++
+ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
+ {
+ char cmdline[COMMAND_LINE_SIZE];
+@@ -135,6 +147,9 @@ int atags_to_fdt(void *atag_list, void *
+
+ for_each_tag(atag, atag_list) {
+ if (atag->hdr.tag == ATAG_CMDLINE) {
++ /* Rename the original root= and rootfstype= options */
++ mangle_bootargs(fdt,
++ atag->u.cmdline.cmdline);
+ /* Append the ATAGS command line to the device tree
+ * command line.
+ * NB: This means that if the same parameter is set in
+--- a/arch/arm/boot/compressed/string.c
++++ b/arch/arm/boot/compressed/string.c
+@@ -111,6 +111,53 @@ char *strchr(const char *s, int c)
+ return (char *)s;
+ }
+
++/**
++ * strncpy - Copy a length-limited, %NUL-terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ * @count: The maximum number of bytes to copy
++ *
++ * The result is not %NUL-terminated if the source exceeds
++ * @count bytes.
++ *
++ * In the case where the length of @src is less than that of
++ * count, the remainder of @dest will be padded with %NUL.
++ *
++ */
++char *strncpy(char *dest, const char *src, size_t count)
++{
++ char *tmp = dest;
++
++ while (count) {
++ if ((*tmp = *src) != 0)
++ src++;
++ tmp++;
++ count--;
++ }
++ return dest;
++}
++
++/**
++ * strstr - Find the first substring in a %NUL terminated string
++ * @s1: The string to be searched
++ * @s2: The string to search for
++ */
++char *strstr(const char *s1, const char *s2)
++{
++ size_t l1, l2;
++
++ l2 = strlen(s2);
++ if (!l2)
++ return (char *)s1;
++ l1 = strlen(s1);
++ while (l1 >= l2) {
++ l1--;
++ if (!memcmp(s1, s2, l2))
++ return (char *)s1;
++ s1++;
++ }
++ return NULL;
++}
+ #undef memset
+
+ void *memset(void *s, int c, size_t count)
+--- a/drivers/mtd/ofpart.c
++++ b/drivers/mtd/ofpart.c
+@@ -25,12 +25,15 @@ static bool node_has_compatible(struct d
+ return of_get_property(pp, "compatible", NULL);
+ }
+
++static int uboot_active_root;
++
+ static int parse_ofpart_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+ {
+ struct device_node *node;
+ const char *partname;
++ const char *owrtpart = "ubi";
+ struct device_node *pp;
+ int nr_parts, i;
+
+@@ -78,9 +81,15 @@ static int parse_ofpart_partitions(struc
+ (*pparts)[i].offset = of_read_number(reg, a_cells);
+ (*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
+
+- partname = of_get_property(pp, "label", &len);
+- if (!partname)
+- partname = of_get_property(pp, "name", &len);
++ if (uboot_active_root && (i == uboot_active_root)) {
++ partname = owrtpart;
++ } else {
++ partname = of_get_property(pp, "label", &len);
++
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ }
++
+ (*pparts)[i].name = partname;
+
+ if (of_get_property(pp, "read-only", &len))
+@@ -178,6 +187,18 @@ static int __init ofpart_parser_init(voi
+ return 0;
+ }
+
++static int __init active_root(char *str)
++{
++ get_option(&str, &uboot_active_root);
++
++ if (!uboot_active_root)
++ return 1;
++
++ return 1;
++}
++
++__setup("uboot_active_root=", active_root);
++
+ static void __exit ofpart_parser_exit(void)
+ {
+ deregister_mtd_parser(&ofpart_parser);
diff --git a/target/linux/mvebu/patches-3.19/102-revert_i2c_delay.patch b/target/linux/mvebu/patches-3.19/102-revert_i2c_delay.patch
new file mode 100644
index 0000000..02e913e
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/102-revert_i2c_delay.patch
@@ -0,0 +1,15 @@
+--- a/arch/arm/boot/dts/armada-xp.dtsi
++++ b/arch/arm/boot/dts/armada-xp.dtsi
+@@ -54,12 +54,10 @@
+ };
+
+ i2c0: i2c@11000 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x100>;
+ };
+
+ i2c1: i2c@11100 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x100>;
+ };
+
diff --git a/target/linux/mvebu/patches-3.19/200-gpio_mvebu_checkpatch_fixes.patch b/target/linux/mvebu/patches-3.19/200-gpio_mvebu_checkpatch_fixes.patch
new file mode 100644
index 0000000..844df4f
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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.19/201-gpio_mvebu_fix_probe_cleanup_on_error.patch b/target/linux/mvebu/patches-3.19/201-gpio_mvebu_fix_probe_cleanup_on_error.patch
new file mode 100644
index 0000000..aecf8e3
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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.19/202-gpio_mvebu_add_limited_pwm_support.patch b/target/linux/mvebu/patches-3.19/202-gpio_mvebu_add_limited_pwm_support.patch
new file mode 100644
index 0000000..19376f8
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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
+@@ -239,6 +239,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
+@@ -60,6 +60,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 <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.19/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch b/target/linux/mvebu/patches-3.19/203-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
new file mode 100644
index 0000000..48f9394
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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.19/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch b/target/linux/mvebu/patches-3.19/204-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
new file mode 100644
index 0000000..2f81504
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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
+@@ -123,24 +123,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.19/205-arm_mvebu_enable_pwm_in_defconfig.patch b/target/linux/mvebu/patches-3.19/205-arm_mvebu_enable_pwm_in_defconfig.patch
new file mode 100644
index 0000000..9c46e68
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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
+@@ -116,6 +116,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.19/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch b/target/linux/mvebu/patches-3.19/206-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch
new file mode 100644
index 0000000..8af5eba
--- /dev/null
+++ b/target/linux/mvebu/patches-3.19/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 {