aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux
diff options
context:
space:
mode:
authorImre Kaloz <kaloz@openwrt.org>2015-01-15 13:59:01 +0000
committerImre Kaloz <kaloz@openwrt.org>2015-01-15 13:59:01 +0000
commit9a8bf84879ec239200b8ea13292a19d6b7a053e2 (patch)
tree32df949967b6cc76177b26cedcf1faf100c89595 /target/linux
parentf1c5232f4d5c06b341341304de02596ca3d83c62 (diff)
downloadupstream-9a8bf84879ec239200b8ea13292a19d6b7a053e2.tar.gz
upstream-9a8bf84879ec239200b8ea13292a19d6b7a053e2.tar.bz2
upstream-9a8bf84879ec239200b8ea13292a19d6b7a053e2.zip
mvebu: add the new tlc59116 driver and migrate the Mamba to it
Signed-off-by: Imre Kaloz <kaloz@openwrt.org> SVN-Revision: 43975
Diffstat (limited to 'target/linux')
-rw-r--r--target/linux/mvebu/files/arch/arm/boot/dts/armada-xp-mamba.dts55
-rw-r--r--target/linux/mvebu/patches-3.18/003-add_leds_tlc59116_driver.patch529
-rw-r--r--target/linux/mvebu/patches-3.18/050-leds_tlc59116_document_binding.patch51
-rw-r--r--target/linux/mvebu/patches-3.18/051-leds_tlc59116_add_driver.patch297
4 files changed, 401 insertions, 531 deletions
diff --git a/target/linux/mvebu/files/arch/arm/boot/dts/armada-xp-mamba.dts b/target/linux/mvebu/files/arch/arm/boot/dts/armada-xp-mamba.dts
index b544389031..8593fde63b 100644
--- a/target/linux/mvebu/files/arch/arm/boot/dts/armada-xp-mamba.dts
+++ b/target/linux/mvebu/files/arch/arm/boot/dts/armada-xp-mamba.dts
@@ -153,10 +153,61 @@
};
tlc59116@68 {
+ #address-cells = <1>;
+ #size-cells = <0>;
#gpio-cells = <2>;
- compatible = "gpio,tlc59116";
+ compatible = "ti,tlc59116";
reg = <0x68>;
- gpio-controller;
+
+ wan_amber@0 {
+ label = "mamba:amber:wan";
+ reg = <0x0>;
+ };
+
+ wan_white@1 {
+ label = "mamba:white:wan";
+ reg = <0x1>;
+ };
+
+ wlan_2g@2 {
+ label = "mamba:white:wlan_2g";
+ reg = <0x2>;
+ };
+
+ wlan_5g@3 {
+ label = "mamba:white:wlan_5g";
+ reg = <0x3>;
+ };
+
+ esata@4 {
+ label = "mamba:white:esata";
+ reg = <0x4>;
+ };
+
+ usb2@5 {
+ label = "mamba:white:usb2";
+ reg = <0x5>;
+ };
+
+ usb3_1@6 {
+ label = "mamba:white:usb3_1";
+ reg = <0x6>;
+ };
+
+ usb3_2@7 {
+ label = "mamba:white:usb3_2";
+ reg = <0x7>;
+ };
+
+ wps_white@8 {
+ label = "mamba:white:wps";
+ reg = <0x8>;
+ };
+
+ wps_amber@9 {
+ label = "mamba:amber:wps";
+ reg = <0x9>;
+ };
};
};
diff --git a/target/linux/mvebu/patches-3.18/003-add_leds_tlc59116_driver.patch b/target/linux/mvebu/patches-3.18/003-add_leds_tlc59116_driver.patch
deleted file mode 100644
index cb9f1572d1..0000000000
--- a/target/linux/mvebu/patches-3.18/003-add_leds_tlc59116_driver.patch
+++ /dev/null
@@ -1,529 +0,0 @@
---- a/drivers/leds/Kconfig
-+++ b/drivers/leds/Kconfig
-@@ -505,6 +505,15 @@ config LEDS_VERSATILE
- This option enabled support for the LEDs on the ARM Versatile
- and RealView boards. Say Y to enabled these.
-
-+config LEDS_TLC59116
-+ tristate "LED driver for TLC59116F dimmer"
-+ depends on LEDS_CLASS
-+ depends on I2C
-+ help
-+ This option enables support for Texas Instruments TLC59116F
-+ LED controller. It is generally only useful
-+ as a platform driver
-+
- comment "LED Triggers"
- source "drivers/leds/trigger/Kconfig"
-
---- a/drivers/leds/Makefile
-+++ b/drivers/leds/Makefile
-@@ -56,6 +56,7 @@ obj-$(CONFIG_LEDS_BLINKM) += leds-blink
- obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
- obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
- obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
-+obj-$(CONFIG_LEDS_TLC59116) += leds-tlc59116.o
-
- # LED SPI Drivers
- obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
---- /dev/null
-+++ b/drivers/leds/leds-tlc59116.c
-@@ -0,0 +1,498 @@
-+/*
-+ * Copyright 2014 Belkin Inc.
-+ *
-+ * Author: Belkin Inc.
-+ *
-+ * This file is subject to the terms and conditions of version 2 of
-+ * the GNU General Public License. See the file COPYING in the main
-+ * directory of this archive for more details.
-+ *
-+ * LED driver for various TLC59116 I2C LED drivers
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/delay.h>
-+#include <linux/string.h>
-+#include <linux/ctype.h>
-+#include <linux/leds.h>
-+#include <linux/err.h>
-+#include <linux/i2c.h>
-+#include <linux/workqueue.h>
-+
-+/* LED select registers determine the source that drives LED outputs */
-+#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 TLC59116_PINS 16
-+#define TLC59116_REG_MODE1 0x00 /* Mode register 0 */
-+#define MODE1_RESPON_ADDR_MASK 0xF0
-+#define MODE1_NORMAL_MODE (0 << 4)
-+#define MODE1_SPEED_MODE (1 << 4)
-+
-+#define TLC59116_REG_MODE2 0x01 /* Mode register 1 */
-+#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_PWM0 0x02
-+#define TLC59116_REG_PWM1 0x03
-+#define TLC59116_REG_PWM2 0x04
-+#define TLC59116_REG_PWM3 0x05
-+#define TLC59116_REG_PWM4 0x06
-+#define TLC59116_REG_PWM5 0x07
-+#define TLC59116_REG_PWM6 0x08
-+#define TLC59116_REG_PWM7 0x09
-+#define TLC59116_REG_PWM8 0x0a
-+#define TLC59116_REG_PWM9 0x0b
-+#define TLC59116_REG_PWM10 0x0c
-+#define TLC59116_REG_PWM11 0x0d
-+#define TLC59116_REG_PWM12 0x0e
-+#define TLC59116_REG_PWM13 0x0f
-+#define TLC59116_REG_PWM14 0x10
-+#define TLC59116_REG_PWM15 0x01
-+
-+#define TLC59116_REG_GRPPWM 0x12
-+#define TLC59116_REG_GRPFREQ 0x13
-+
-+#define TLC59116_PERIOD_MIN 41 /* 41ms */
-+#define TLC59116_PERIOD_MAX 10730 /* 10.73s */
-+
-+#define TLC59116_REG_LEDOUT0 0x14 /* LED [3:0] driver output state registers */
-+#define TLC59116_REG_LEDOUT1 0x15 /* LED [7:4] driver output state registers */
-+#define TLC59116_REG_LEDOUT2 0x16 /* LED [11:8] driver output state registers */
-+#define TLC59116_REG_LEDOUT3 0x17 /* LED [15:12] driver output state registers */
-+
-+#define GPIO0_MASK 0x3
-+
-+#define DEBUG 0
-+
-+#if DEBUG > 1
-+#define led_dbg(fmt, arg...) printk(KERN_DEBUG "tlc59116:%s " fmt "\n", __func__ , ## arg)
-+#else
-+#define led_dbg(fmt, arg...)
-+#endif
-+
-+
-+#if DEBUG > 0
-+#define led_info(fmt, arg...) printk("tlc59116:%s " fmt "\n", __func__ , ## arg)
-+#else
-+#define led_info(fmt, arg...)
-+#endif
-+
-+enum tlc59116_type {
-+ tlc59116,
-+};
-+
-+struct tlc59116_chipdef {
-+ int bits;
-+ u8 slv_addr; /* 7-bit slave address mask */
-+ int slv_addr_shift; /* Number of bits to ignore */
-+};
-+
-+static struct tlc59116_chipdef tlc59116_chipdefs[] = {
-+ [tlc59116] = {
-+ .bits = 16,
-+ .slv_addr = /* 1100xxx */ 0x68,
-+ .slv_addr_shift = 3,
-+ },
-+};
-+
-+static const struct i2c_device_id tlc59116_id[] = {
-+ { "tlc59116", tlc59116 },
-+ { }
-+};
-+MODULE_DEVICE_TABLE(i2c, tlc59116_id);
-+
-+struct tlc59116_led {
-+ struct tlc59116_chipdef *chipdef;
-+ struct i2c_client *client;
-+ struct work_struct work;
-+ spinlock_t lock;
-+ enum led_brightness brightness;
-+ struct led_classdev led_cdev;
-+ int led_num; /* 0 .. 8 potentially */
-+ char name[32];
-+};
-+
-+//#define DUMP_REGS
-+
-+#ifdef DUMP_REGS
-+void dump_regs(struct i2c_client *client)
-+{
-+ int i, j = 0;
-+ u8 data;
-+ printk ("\n-------------------------------------\n");
-+ for (i = 0; i< 0x20; i++)
-+ {
-+ data = i2c_smbus_read_byte_data(client, i);
-+ printk ("[0x%x] = 0x%x ",i, data);
-+ j++;
-+ if (j == 5)
-+ {
-+ printk ("\n");
-+ j = 0;
-+ }
-+ }
-+ printk ("\n");
-+}
-+#endif
-+
-+static int tlc59116_set_mode(struct i2c_client *client, uint8_t mode)
-+{
-+ uint8_t val = 0;
-+
-+ if ((mode != MODE2_DIM) && (mode != MODE2_BLINK))
-+ mode = MODE2_DIM;
-+
-+ /* Configure MODE1 register */
-+ val &= 0x0;
-+ val &= MODE1_RESPON_ADDR_MASK;
-+ val |= MODE1_NORMAL_MODE;
-+ i2c_smbus_write_byte_data(client, TLC59116_REG_MODE1, val);
-+
-+ /* Configure MODE2 Reg */
-+ val &= 0x00;
-+ val |= MODE2_OCH_STOP;
-+
-+ val |= mode;
-+
-+ i2c_smbus_write_byte_data(client, TLC59116_REG_MODE2, val);
-+ mdelay(100);
-+
-+ return 0;
-+}
-+
-+static int tlc59116_set_gpio_act(struct i2c_client *client, u8 gpio_no, u8 act_mode)
-+{
-+ char data, addr = 0, i;
-+
-+ if ((gpio_no >= 0) && (gpio_no < 4))
-+ addr = TLC59116_REG_LEDOUT0;
-+ else if ((gpio_no >= 4) && (gpio_no < 8))
-+ addr = TLC59116_REG_LEDOUT1;
-+ else if ((gpio_no >= 8) && (gpio_no < 12))
-+ addr = TLC59116_REG_LEDOUT2;
-+ else if ((gpio_no >=12 ) && (gpio_no < 16))
-+ addr = TLC59116_REG_LEDOUT3;
-+
-+ data = i2c_smbus_read_byte_data(client, addr);
-+
-+ i = (gpio_no % 4) * 2;
-+
-+ data &= ~(GPIO0_MASK << i);
-+ act_mode = act_mode << i;
-+ data |= act_mode;
-+
-+ if(i2c_smbus_write_byte_data(client, addr, data) != 0) {
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+static int tlc59116_set_gpio(struct i2c_client *client, uint8_t gpio_no, uint8_t val)
-+{
-+ val &= 0x03;
-+ tlc59116_set_gpio_act(client, gpio_no, val);
-+#ifdef DUMP_REGS
-+ dump_regs(client);
-+#endif
-+ return 0;
-+}
-+
-+static int tlc59116_get_gpio(struct i2c_client *client, uint8_t gpio_no)
-+{
-+ uint8_t val, reg, data;
-+
-+ led_dbg("gpio = %d\n", gpio_no);
-+ reg = TLC59116_REG_LEDOUT0;
-+
-+ if ((gpio_no >= 0) && (gpio_no < 4))
-+ reg = TLC59116_REG_LEDOUT0;
-+ else if ((gpio_no >= 4) && (gpio_no < 8)) {
-+ reg = TLC59116_REG_LEDOUT1;
-+ gpio_no = gpio_no - 4;
-+ }
-+ else if ((gpio_no >= 8) && (gpio_no < 12)) {
-+ reg = TLC59116_REG_LEDOUT2;
-+ gpio_no = gpio_no - 8;
-+ }
-+ else if ((gpio_no >=12 ) && (gpio_no < 16)) {
-+ reg = TLC59116_REG_LEDOUT3;
-+ gpio_no = gpio_no - 12;
-+ }
-+
-+ val = i2c_smbus_read_byte_data(client, reg);
-+
-+ data = (val >> (gpio_no * 2)) & 0x03;
-+ return data;
-+}
-+
-+/*
-+ * gpio_no [0..7]
-+ * duty_cycle [0..99]%
-+ *
-+ * */
-+static int tlc59116_individual_brighness_control(struct i2c_client *client, uint8_t gpio_no, uint8_t brightness)
-+{
-+ uint8_t pwm;
-+
-+ pwm = gpio_no + TLC59116_REG_PWM0;
-+ i2c_smbus_write_byte_data(client, pwm, brightness);
-+
-+ return 0;
-+}
-+
-+static void tlc59116_led_work(struct work_struct *work)
-+{
-+ struct tlc59116_led *tlc59116;
-+
-+ tlc59116 = container_of(work, struct tlc59116_led, work);
-+
-+ led_dbg("\nbrighness = %d \n", tlc59116->brightness);
-+ switch (tlc59116->brightness) {
-+ case LED_OFF:
-+ led_info("\nLed off\n");
-+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_LED_OFF);
-+ break;
-+
-+ case LED_FULL:
-+ led_info("\nLed on\n");
-+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_LED_ON);
-+ break;
-+ default:
-+ led_info("\nBrightness is %d\n", tlc59116->brightness);
-+ if (TLC59116_BLINK != tlc59116_get_gpio(tlc59116->client, tlc59116->led_num))
-+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_DIM);
-+
-+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
-+ break;
-+ }
-+
-+}
-+
-+static void tlc59116_led_set(struct led_classdev *led_cdev, enum led_brightness value)
-+{
-+ struct tlc59116_led *tlc59116;
-+
-+ tlc59116 = container_of(led_cdev, struct tlc59116_led, led_cdev);
-+
-+ spin_lock(&tlc59116->lock);
-+ tlc59116->brightness = value;
-+
-+ /*
-+ * Must use workqueue for the actual I/O since I2C operations
-+ * can sleep.
-+ */
-+ schedule_work(&tlc59116->work);
-+
-+ spin_unlock(&tlc59116->lock);
-+}
-+
-+/*
-+ * delay_on, delay_off: units are in ms
-+ *
-+ */
-+
-+static int tlc59116_set_blink(struct led_classdev *led_cdev,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct tlc59116_led *tlc59116;
-+ uint16_t period;
-+ uint16_t duty_cycle;
-+ uint8_t gdc;
-+ uint8_t gfrq;
-+
-+ tlc59116 = container_of(led_cdev, struct tlc59116_led, led_cdev);
-+ led_info ("Blinking: delay_on = %ldms, delay_off = %ldms, brightness = %d\n",
-+ *delay_on, *delay_off, tlc59116->brightness);
-+
-+ // Hardware blinking only for tricolor leds . The rest will have software blinking
-+ if (tlc59116->led_num > 2)
-+ return 1;
-+
-+ if ((*delay_on == 0) && (*delay_off ==0)) {
-+ spin_lock(&tlc59116->lock);
-+
-+ /* MODE2[DMBLNK] = 1 */
-+ tlc59116_set_mode(tlc59116->client, MODE2_BLINK);
-+
-+ /* Set LDRx = 11 */
-+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_BLINK);
-+
-+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
-+ spin_unlock(&tlc59116->lock);
-+ return 0;
-+ }
-+
-+
-+ if ((*delay_on + *delay_off) > TLC59116_PERIOD_MAX)
-+ {
-+ led_dbg ("Max period is %dms\n", TLC59116_PERIOD_MAX);
-+ return -EINVAL;
-+ }
-+
-+ if ((*delay_on + *delay_off) < TLC59116_PERIOD_MIN)
-+ {
-+ *delay_on = TLC59116_PERIOD_MIN/2 + 1;
-+ *delay_off = TLC59116_PERIOD_MIN/2 + 1;
-+ }
-+ period = (*delay_on) + (*delay_off);
-+
-+ duty_cycle = (100 * (*delay_on)) / period;
-+
-+ spin_lock(&tlc59116->lock);
-+
-+ /* MODE2[DMBLNK] = 1 */
-+ tlc59116_set_mode(tlc59116->client, MODE2_BLINK);
-+
-+ /* Set LDRx = 11 */
-+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_BLINK);
-+
-+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
-+
-+ gdc = (duty_cycle * 256)/100;
-+ i2c_smbus_write_byte_data(tlc59116->client, TLC59116_REG_GRPPWM, gdc);
-+
-+ gfrq = (24 * period)/1000 - 1; /* unit is in second (convert from ms to second) */
-+ i2c_smbus_write_byte_data(tlc59116->client, TLC59116_REG_GRPFREQ, gfrq);
-+
-+
-+#ifdef DUMP_REGS
-+ dump_regs(tlc59116->client);
-+#endif
-+
-+ spin_unlock(&tlc59116->lock);
-+ return 0;
-+}
-+
-+static int tlc59116_probe(struct i2c_client *client,
-+ const struct i2c_device_id *id)
-+{
-+ struct tlc59116_led *tlc59116;
-+ struct tlc59116_chipdef *chip;
-+ struct i2c_adapter *adapter;
-+ struct led_platform_data *pdata;
-+ int i, err;
-+
-+ chip = &tlc59116_chipdefs[id->driver_data];
-+ adapter = to_i2c_adapter(client->dev.parent);
-+ pdata = client->dev.platform_data;
-+
-+ /* Make sure the slave address / chip type combo given is possible */
-+ if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
-+ chip->slv_addr) {
-+ dev_err(&client->dev, "invalid slave address %02x\n",
-+ client->addr);
-+ return -ENODEV;
-+ }
-+
-+ printk(KERN_INFO "leds-tlc59116: Using %s %d-bit LED driver at "
-+ "slave address 0x%02x\n",
-+ id->name, chip->bits, client->addr);
-+
-+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
-+ return -EIO;
-+
-+ if (pdata) {
-+ if (pdata->num_leds != chip->bits) {
-+ dev_err(&client->dev, "board info claims %d LEDs"
-+ " on a %d-bit chip\n",
-+ pdata->num_leds, chip->bits);
-+ return -ENODEV;
-+ }
-+ }
-+
-+ tlc59116 = devm_kzalloc(&client->dev, sizeof(*tlc59116) * chip->bits, GFP_KERNEL);
-+ if (!tlc59116)
-+ return -ENOMEM;
-+
-+ i2c_set_clientdata(client, tlc59116);
-+
-+ for (i = 0; i < chip->bits; i++) {
-+ tlc59116[i].chipdef = chip;
-+ tlc59116[i].client = client;
-+ tlc59116[i].led_num = i;
-+
-+ /* Platform data can specify LED names and default triggers */
-+ if (pdata) {
-+ if (pdata->leds[i].name)
-+ snprintf(tlc59116[i].name,
-+ sizeof(tlc59116[i].name), "tlc59116:%s",
-+ pdata->leds[i].name);
-+ if (pdata->leds[i].default_trigger)
-+ tlc59116[i].led_cdev.default_trigger =
-+ pdata->leds[i].default_trigger;
-+ } else {
-+ snprintf(tlc59116[i].name, sizeof(tlc59116[i].name),
-+ "tlc59116:%d", i);
-+ }
-+
-+ spin_lock_init(&tlc59116[i].lock);
-+
-+ tlc59116[i].led_cdev.name = tlc59116[i].name;
-+ tlc59116[i].led_cdev.brightness_set = tlc59116_led_set;
-+ tlc59116[i].led_cdev.blink_set = tlc59116_set_blink;
-+ tlc59116[i].led_cdev.brightness = 0;
-+
-+ INIT_WORK(&tlc59116[i].work, tlc59116_led_work);
-+
-+ err = led_classdev_register(&client->dev, &tlc59116[i].led_cdev);
-+ if (err < 0)
-+ goto exit;
-+ }
-+
-+ tlc59116_set_mode(client, MODE2_DIM);
-+
-+ /* Turn off LEDs */
-+ for (i = 0; i < chip->bits; i++)
-+ tlc59116_set_gpio(client, i, TLC59116_LED_OFF);
-+
-+ return 0;
-+
-+exit:
-+ while (i--) {
-+ led_classdev_unregister(&tlc59116[i].led_cdev);
-+ cancel_work_sync(&tlc59116[i].work);
-+ }
-+
-+ devm_kfree(&client->dev, tlc59116);
-+ i2c_set_clientdata(client, NULL);
-+
-+ return err;
-+}
-+
-+static int tlc59116_remove(struct i2c_client *client)
-+{
-+ struct tlc59116_led *tlc59116 = i2c_get_clientdata(client);
-+ int i;
-+
-+ for (i = 0; i < tlc59116->chipdef->bits; i++) {
-+ led_classdev_unregister(&tlc59116[i].led_cdev);
-+ cancel_work_sync(&tlc59116[i].work);
-+ }
-+
-+ devm_kfree(&client->dev, tlc59116);
-+ i2c_set_clientdata(client, NULL);
-+
-+ return 0;
-+}
-+
-+static struct i2c_driver tlc59116_driver = {
-+ .driver = {
-+ .name = "leds-tlc59116",
-+ .owner = THIS_MODULE,
-+ },
-+ .probe = tlc59116_probe,
-+ .remove = tlc59116_remove,
-+ .id_table = tlc59116_id,
-+};
-+
-+module_i2c_driver(tlc59116_driver);
-+
-+MODULE_AUTHOR("Belkin Inc.");
-+MODULE_DESCRIPTION("TLC59116 LED driver");
-+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/mvebu/patches-3.18/050-leds_tlc59116_document_binding.patch b/target/linux/mvebu/patches-3.18/050-leds_tlc59116_document_binding.patch
new file mode 100644
index 0000000000..e55eca3e04
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/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.18/051-leds_tlc59116_add_driver.patch b/target/linux/mvebu/patches-3.18/051-leds_tlc59116_add_driver.patch
new file mode 100644
index 0000000000..fd22aef580
--- /dev/null
+++ b/target/linux/mvebu/patches-3.18/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
+@@ -446,6 +446,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
+@@ -29,6 +29,7 @@ obj-$(CONFIG_LEDS_LP5562) += leds-lp556
+ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
+ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.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");