From fa4191a609f219262a18dd8b02ab7dc30896b707 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Tue, 14 Jun 2016 15:36:17 +0800 Subject: leds: pca9532: Add device tree support This patch adds basic device tree support for the pca9532 LEDs. Signed-off-by: Phil Reid Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca9532.c | 75 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index e3d3b1aaa9e0..09a7cffbc46f 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include /* m = num_leds*/ #define PCA9532_REG_INPUT(i) ((i) >> 3) @@ -86,9 +88,22 @@ static const struct pca9532_chip_info pca9532_chip_info_tbl[] = { }, }; +#ifdef CONFIG_OF +static const struct of_device_id of_pca9532_leds_match[] = { + { .compatible = "nxp,pca9530", .data = (void *)pca9530 }, + { .compatible = "nxp,pca9531", .data = (void *)pca9531 }, + { .compatible = "nxp,pca9532", .data = (void *)pca9532 }, + { .compatible = "nxp,pca9533", .data = (void *)pca9533 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_pca9532_leds_match); +#endif + static struct i2c_driver pca9532_driver = { .driver = { .name = "leds-pca953x", + .of_match_table = of_match_ptr(of_pca9532_leds_match), }, .probe = pca9532_probe, .remove = pca9532_remove, @@ -354,6 +369,7 @@ static int pca9532_configure(struct i2c_client *client, led->state = pled->state; led->name = pled->name; led->ldev.name = led->name; + led->ldev.default_trigger = led->default_trigger; led->ldev.brightness = LED_OFF; led->ldev.brightness_set_blocking = pca9532_set_brightness; @@ -432,15 +448,66 @@ exit: return err; } +static struct pca9532_platform_data * +pca9532_of_populate_pdata(struct device *dev, struct device_node *np) +{ + struct pca9532_platform_data *pdata; + struct device_node *child; + const struct of_device_id *match; + int devid, maxleds; + int i = 0; + + match = of_match_device(of_pca9532_leds_match, dev); + if (!match) + return ERR_PTR(-ENODEV); + + devid = (int)(uintptr_t)match->data; + maxleds = pca9532_chip_info_tbl[devid].num_leds; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(np, child) { + if (of_property_read_string(child, "label", + &pdata->leds[i].name)) + pdata->leds[i].name = child->name; + of_property_read_u32(child, "type", &pdata->leds[i].type); + of_property_read_string(child, "linux,default-trigger", + &pdata->leds[i].default_trigger); + if (++i >= maxleds) { + of_node_put(child); + break; + } + } + + return pdata; +} + static int pca9532_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int devid; struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_platform_data *pca9532_pdata = dev_get_platdata(&client->dev); - - if (!pca9532_pdata) - return -EIO; + struct device_node *np = client->dev.of_node; + + if (!pca9532_pdata) { + if (np) { + pca9532_pdata = + pca9532_of_populate_pdata(&client->dev, np); + if (IS_ERR(pca9532_pdata)) + return PTR_ERR(pca9532_pdata); + } else { + dev_err(&client->dev, "no platform data\n"); + return -EINVAL; + } + devid = (int)(uintptr_t)of_match_device( + of_pca9532_leds_match, &client->dev)->data; + } else { + devid = id->driver_data; + } if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -450,7 +517,7 @@ static int pca9532_probe(struct i2c_client *client, if (!data) return -ENOMEM; - data->chip_info = &pca9532_chip_info_tbl[id->driver_data]; + data->chip_info = &pca9532_chip_info_tbl[devid]; dev_info(&client->dev, "setting platform data\n"); i2c_set_clientdata(client, data); -- cgit v1.2.3 From eb25cb9956cc9384b7fa0d75dec908c9fac8c444 Mon Sep 17 00:00:00 2001 From: Stephan Linz Date: Fri, 10 Jun 2016 07:59:56 +0200 Subject: leds: convert IDE trigger to common disk trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch converts the IDE specific LED trigger to a generic disk activity LED trigger. The libata core is now a trigger source just like before the IDE disk driver. It's merely a replacement of the string ide by disk. The patch is taken from http://dev.gentoo.org/~josejx/ata.patch and is widely used by any ibook/powerbook owners with great satisfaction. Likewise, it is very often used successfully on different ARM platforms. Unlike the original patch, the existing 'ide-disk' trigger is still available for backward compatibility. That reduce the amount of patches in affected device trees out of the mainline kernel. For further development, the new name 'disk-activity' should be used. Cc: Joseph Jezak Cc: Jörg Sommer Cc: Richard Purdie Signed-off-by: Stephan Linz Signed-off-by: Jacek Anaszewski --- drivers/ata/libata-core.c | 4 ++++ drivers/ide/ide-disk.c | 2 +- drivers/leds/leds-hp6xx.c | 2 +- drivers/leds/trigger/Kconfig | 8 +++---- drivers/leds/trigger/Makefile | 2 +- drivers/leds/trigger/ledtrig-disk.c | 41 +++++++++++++++++++++++++++++++++ drivers/leds/trigger/ledtrig-ide-disk.c | 36 ----------------------------- include/linux/leds.h | 6 ++--- 8 files changed, 55 insertions(+), 46 deletions(-) create mode 100644 drivers/leds/trigger/ledtrig-disk.c delete mode 100644 drivers/leds/trigger/ledtrig-ide-disk.c (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 6be7770f68e9..2eca572f4df6 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -5072,6 +5073,9 @@ void ata_qc_complete(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + /* Trigger the LED (if available) */ + ledtrig_disk_activity(); + /* XXX: New EH and old EH use different mechanisms to * synchronize EH with regular execution path. * diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 05dbcce70b0e..5ceb176dc148 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -186,7 +186,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq, BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); BUG_ON(rq->cmd_type != REQ_TYPE_FS); - ledtrig_ide_activity(); + ledtrig_disk_activity(); pr_debug("%s: %sing: block=%llu, sectors=%u\n", drive->name, rq_data_dir(rq) == READ ? "read" : "writ", diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c index a6b8db0e27f1..137969fcecbb 100644 --- a/drivers/leds/leds-hp6xx.c +++ b/drivers/leds/leds-hp6xx.c @@ -50,7 +50,7 @@ static struct led_classdev hp6xx_red_led = { static struct led_classdev hp6xx_green_led = { .name = "hp6xx:green", - .default_trigger = "ide-disk", + .default_trigger = "disk-activity", .brightness_set = hp6xxled_green_set, .flags = LED_CORE_SUSPENDRESUME, }; diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 9893d911390d..3f9ddb9fafa7 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -33,12 +33,12 @@ config LEDS_TRIGGER_ONESHOT If unsure, say Y. -config LEDS_TRIGGER_IDE_DISK - bool "LED IDE Disk Trigger" - depends on IDE_GD_ATA +config LEDS_TRIGGER_DISK + bool "LED Disk Trigger" + depends on IDE_GD_ATA || ATA depends on LEDS_TRIGGERS help - This allows LEDs to be controlled by IDE disk activity. + This allows LEDs to be controlled by disk activity. If unsure, say Y. config LEDS_TRIGGER_MTD diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 8cc64a4f4e25..a72c43cffebf 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o -obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o +obj-$(CONFIG_LEDS_TRIGGER_DISK) += ledtrig-disk.o obj-$(CONFIG_LEDS_TRIGGER_MTD) += ledtrig-mtd.o obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o diff --git a/drivers/leds/trigger/ledtrig-disk.c b/drivers/leds/trigger/ledtrig-disk.c new file mode 100644 index 000000000000..cd525b4125eb --- /dev/null +++ b/drivers/leds/trigger/ledtrig-disk.c @@ -0,0 +1,41 @@ +/* + * LED Disk Activity Trigger + * + * Copyright 2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * 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. + * + */ + +#include +#include +#include + +#define BLINK_DELAY 30 + +DEFINE_LED_TRIGGER(ledtrig_disk); +DEFINE_LED_TRIGGER(ledtrig_ide); + +void ledtrig_disk_activity(void) +{ + unsigned long blink_delay = BLINK_DELAY; + + led_trigger_blink_oneshot(ledtrig_disk, + &blink_delay, &blink_delay, 0); + led_trigger_blink_oneshot(ledtrig_ide, + &blink_delay, &blink_delay, 0); +} +EXPORT_SYMBOL(ledtrig_disk_activity); + +static int __init ledtrig_disk_init(void) +{ + led_trigger_register_simple("disk-activity", &ledtrig_disk); + led_trigger_register_simple("ide-disk", &ledtrig_ide); + + return 0; +} +device_initcall(ledtrig_disk_init); diff --git a/drivers/leds/trigger/ledtrig-ide-disk.c b/drivers/leds/trigger/ledtrig-ide-disk.c deleted file mode 100644 index 15123d389240..000000000000 --- a/drivers/leds/trigger/ledtrig-ide-disk.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * LED IDE-Disk Activity Trigger - * - * Copyright 2006 Openedhand Ltd. - * - * Author: Richard Purdie - * - * 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. - * - */ - -#include -#include -#include - -#define BLINK_DELAY 30 - -DEFINE_LED_TRIGGER(ledtrig_ide); - -void ledtrig_ide_activity(void) -{ - unsigned long ide_blink_delay = BLINK_DELAY; - - led_trigger_blink_oneshot(ledtrig_ide, - &ide_blink_delay, &ide_blink_delay, 0); -} -EXPORT_SYMBOL(ledtrig_ide_activity); - -static int __init ledtrig_ide_init(void) -{ - led_trigger_register_simple("ide-disk", &ledtrig_ide); - return 0; -} -device_initcall(ledtrig_ide_init); diff --git a/include/linux/leds.h b/include/linux/leds.h index e5e7f2e80a54..0aa3701f2bf4 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -325,10 +325,10 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev) #endif /* CONFIG_LEDS_TRIGGERS */ /* Trigger specific functions */ -#ifdef CONFIG_LEDS_TRIGGER_IDE_DISK -extern void ledtrig_ide_activity(void); +#ifdef CONFIG_LEDS_TRIGGER_DISK +extern void ledtrig_disk_activity(void); #else -static inline void ledtrig_ide_activity(void) {} +static inline void ledtrig_disk_activity(void) {} #endif #ifdef CONFIG_LEDS_TRIGGER_MTD -- cgit v1.2.3 From 83e2c70e84e2e0335deae63fa52bee14481fecbe Mon Sep 17 00:00:00 2001 From: Stephan Linz Date: Fri, 10 Jun 2016 07:59:58 +0200 Subject: powerpc: use the new LED disk activity trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dts: rename 'ide-disk' to 'disk-activity' - defconfig: rename 'ADB_PMU_LED_IDE' to 'ADB_PMU_LED_DISK' Cc: Joseph Jezak Cc: Jörg Sommer Signed-off-by: Stephan Linz Signed-off-by: Jacek Anaszewski --- arch/powerpc/boot/dts/mpc8315erdb.dts | 2 +- arch/powerpc/boot/dts/mpc8377_rdb.dts | 2 +- arch/powerpc/boot/dts/mpc8378_rdb.dts | 2 +- arch/powerpc/boot/dts/mpc8379_rdb.dts | 2 +- arch/powerpc/configs/pmac32_defconfig | 2 +- arch/powerpc/configs/ppc6xx_defconfig | 2 +- drivers/macintosh/Kconfig | 13 ++++++------- drivers/macintosh/via-pmu-led.c | 4 ++-- 8 files changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index 43546844ea5a..ca5139ee5074 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -472,7 +472,7 @@ hdd { gpios = <&mcu_pio 1 0>; - linux,default-trigger = "ide-disk"; + linux,default-trigger = "disk-activity"; }; }; }; diff --git a/arch/powerpc/boot/dts/mpc8377_rdb.dts b/arch/powerpc/boot/dts/mpc8377_rdb.dts index 2b4b6532d69c..e32613963ab0 100644 --- a/arch/powerpc/boot/dts/mpc8377_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8377_rdb.dts @@ -496,7 +496,7 @@ hdd { gpios = <&mcu_pio 1 0>; - linux,default-trigger = "ide-disk"; + linux,default-trigger = "disk-activity"; }; }; }; diff --git a/arch/powerpc/boot/dts/mpc8378_rdb.dts b/arch/powerpc/boot/dts/mpc8378_rdb.dts index 74b6a535a413..71842fcd621f 100644 --- a/arch/powerpc/boot/dts/mpc8378_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8378_rdb.dts @@ -480,7 +480,7 @@ hdd { gpios = <&mcu_pio 1 0>; - linux,default-trigger = "ide-disk"; + linux,default-trigger = "disk-activity"; }; }; }; diff --git a/arch/powerpc/boot/dts/mpc8379_rdb.dts b/arch/powerpc/boot/dts/mpc8379_rdb.dts index 3b5cbac85368..e442a29b2fe0 100644 --- a/arch/powerpc/boot/dts/mpc8379_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8379_rdb.dts @@ -446,7 +446,7 @@ hdd { gpios = <&mcu_pio 1 0>; - linux,default-trigger = "ide-disk"; + linux,default-trigger = "disk-activity"; }; }; }; diff --git a/arch/powerpc/configs/pmac32_defconfig b/arch/powerpc/configs/pmac32_defconfig index ea8705ffcd76..3f6c9a6c815c 100644 --- a/arch/powerpc/configs/pmac32_defconfig +++ b/arch/powerpc/configs/pmac32_defconfig @@ -158,7 +158,7 @@ CONFIG_ADB=y CONFIG_ADB_CUDA=y CONFIG_ADB_PMU=y CONFIG_ADB_PMU_LED=y -CONFIG_ADB_PMU_LED_IDE=y +CONFIG_ADB_PMU_LED_DISK=y CONFIG_PMAC_APM_EMU=m CONFIG_PMAC_MEDIABAY=y CONFIG_PMAC_BACKLIGHT=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 99ccbebabfd3..1dde0be2be30 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -442,7 +442,7 @@ CONFIG_ADB=y CONFIG_ADB_CUDA=y CONFIG_ADB_PMU=y CONFIG_ADB_PMU_LED=y -CONFIG_ADB_PMU_LED_IDE=y +CONFIG_ADB_PMU_LED_DISK=y CONFIG_PMAC_APM_EMU=y CONFIG_PMAC_MEDIABAY=y CONFIG_PMAC_BACKLIGHT=y diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 3e8b29e41420..d28690f6e262 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -96,19 +96,18 @@ config ADB_PMU_LED Support the front LED on Power/iBooks as a generic LED that can be triggered by any of the supported triggers. To get the behaviour of the old CONFIG_BLK_DEV_IDE_PMAC_BLINK, select this - and the ide-disk LED trigger and configure appropriately through - sysfs. + and the disk LED trigger and configure appropriately through sysfs. -config ADB_PMU_LED_IDE - bool "Use front LED as IDE LED by default" +config ADB_PMU_LED_DISK + bool "Use front LED as DISK LED by default" depends on ADB_PMU_LED depends on LEDS_CLASS depends on IDE_GD_ATA select LEDS_TRIGGERS - select LEDS_TRIGGER_IDE_DISK + select LEDS_TRIGGER_DISK help - This option makes the front LED default to the IDE trigger - so that it blinks on IDE activity. + This option makes the front LED default to the disk trigger + so that it blinks on disk activity. config PMAC_SMU bool "Support for SMU based PowerMacs" diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index 19c371809d77..ae067ab2373d 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -73,8 +73,8 @@ static void pmu_led_set(struct led_classdev *led_cdev, static struct led_classdev pmu_led = { .name = "pmu-led::front", -#ifdef CONFIG_ADB_PMU_LED_IDE - .default_trigger = "ide-disk", +#ifdef CONFIG_ADB_PMU_LED_DISK + .default_trigger = "disk-activity", #endif .brightness_set = pmu_led_set, }; -- cgit v1.2.3 From b1ae40a5db6191c42e2e45d726407096f030ee08 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 21 Jun 2016 14:35:07 +0200 Subject: leds: Only descend into leds directory when CONFIG_NEW_LEDS is set When CONFIG_NEW_LEDS is not set make will still descend into the leds directory but nothing will be built. This produces unneeded build artifacts and messages in addition to slowing the build. Fix this here. Signed-off-by: Andrew F. Davis Signed-off-by: Jacek Anaszewski --- drivers/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index 0b6f3d60193d..6e7d458ac7d4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -122,7 +122,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ -obj-y += leds/ +obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ -- cgit v1.2.3 From a3eac76cdf7225aa1f87aa4514664f0372b9a1c1 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 1 Jul 2016 23:08:54 +0200 Subject: leds: triggers: return error if invalid trigger name is provided via sysfs If an invalid trigger name is provided via sysfs currently no error is returned. Therefore it's not possible to determine whether the trigger was set successfully. Fix this by returning -EINVAL if no trigger is matched. Signed-off-by: Heiner Kallweit Signed-off-by: Jacek Anaszewski --- drivers/leds/led-triggers.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 55fa65e1ae03..c92702a684ce 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -60,6 +60,8 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, goto unlock; } } + /* we come here only if buf matches no trigger */ + ret = -EINVAL; up_read(&triggers_list_lock); unlock: -- cgit v1.2.3 From bff23714bc36a1322d0f14519022df0d1a4b21f3 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 8 Jul 2016 14:53:38 +0200 Subject: leds: leds-gpio: Set of_node for created LED devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When working with Device Tree we iterate over children of "gpio-leds" compatible node and create LED device for each of them. We take care of all common DT properties (label, default trigger, state, etc.), however some triggers may want to support more of them. It could be useful for timer trigger to support setting delay on/off values with Device Tree property. Or for transient trigger to support e.g. duration property. We obviously should handle such properties in triggers, not in generic code. To let trigger drivers read properties from DT node we need to set of_node to point the relevant node. This change allows using all kind of of helpers in e.g. "activate" callbacks. Signed-off-by: Rafał Miłecki Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-gpio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 8229f063b483..9b991d46ed84 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -165,6 +165,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) return ERR_PTR(-ENOMEM); device_for_each_child_node(dev, child) { + struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; struct gpio_led led = {}; const char *state = NULL; @@ -205,12 +206,12 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) if (fwnode_property_present(child, "panic-indicator")) led.panic_indicator = 1; - ret = create_gpio_led(&led, &priv->leds[priv->num_leds], - dev, NULL); + ret = create_gpio_led(&led, led_dat, dev, NULL); if (ret < 0) { fwnode_handle_put(child); goto err; } + led_dat->cdev.dev->of_node = np; priv->num_leds++; } -- cgit v1.2.3 From d966c943e4dbb66e3afd9f300501c9f3b1c802fd Mon Sep 17 00:00:00 2001 From: Tony Makkiel Date: Mon, 11 Jul 2016 11:48:49 +0100 Subject: leds: LED driver for TI LP3952 6-Channel Color LED The chip can drive 2 sets of RGB leds. Controller can be controlled via PWM, I2C and audio synchronisation. This driver uses I2C to communicate with the chip. Datasheet: http://www.ti.com/lit/gpn/lp3952 Signed-off-by: Tony Makkiel Reviewed-by: Mika Westerberg Reviewed-by: Axel Lin Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 14 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-lp3952.c | 301 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/leds-lp3952.h | 125 ++++++++++++++++++ 4 files changed, 441 insertions(+) create mode 100644 drivers/leds/leds-lp3952.c create mode 100644 include/linux/leds-lp3952.h (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 5ae28340a98b..9dcc9b13d495 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -228,6 +228,20 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP3952 + tristate "LED Support for TI LP3952 2 channel LED driver" + depends on LEDS_CLASS + depends on I2C + depends on ACPI + depends on GPIOLIB + select REGMAP_I2C + help + This option enables support for LEDs connected to the Texas + Instruments LP3952 LED driver. + + To compile this driver as a module, choose M here: the + module will be called leds-lp3952. + config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index cb2013df52d9..0684c865a1c0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP3952) += leds-lp3952.o obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c new file mode 100644 index 000000000000..a73c8ff08530 --- /dev/null +++ b/drivers/leds/leds-lp3952.c @@ -0,0 +1,301 @@ +/* + * LED driver for TI lp3952 controller + * + * Copyright (C) 2016, DAQRI, LLC. + * Author: Tony Makkiel + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int lp3952_register_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + struct lp3952_led_array *priv = i2c_get_clientdata(client); + + ret = regmap_write(priv->regmap, reg, val); + + if (ret) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, reg, val, ret); + return ret; +} + +static void lp3952_on_off(struct lp3952_led_array *priv, + enum lp3952_leds led_id, bool on) +{ + int ret, val; + + dev_dbg(&priv->client->dev, "%s LED %d to %d\n", __func__, led_id, on); + + val = 1 << led_id; + if (led_id == LP3952_LED_ALL) + val = LP3952_LED_MASK_ALL; + + ret = regmap_update_bits(priv->regmap, LP3952_REG_LED_CTRL, val, + on ? val : 0); + if (ret) + dev_err(&priv->client->dev, "%s, Error %d\n", __func__, ret); +} + +/* + * Using Imax to control brightness. There are 4 possible + * setting 25, 50, 75 and 100 % of Imax. Possible values are + * values 0-4. 0 meaning turn off. + */ +static int lp3952_set_brightness(struct led_classdev *cdev, + enum led_brightness value) +{ + unsigned int reg, shift_val; + struct lp3952_ctrl_hdl *led = container_of(cdev, + struct lp3952_ctrl_hdl, + cdev); + struct lp3952_led_array *priv = (struct lp3952_led_array *)led->priv; + + dev_dbg(cdev->dev, "Brightness request: %d on %d\n", value, + led->channel); + + if (value == LED_OFF) { + lp3952_on_off(priv, led->channel, false); + return 0; + } + + if (led->channel > LP3952_RED_1) { + dev_err(cdev->dev, " %s Invalid LED requested", __func__); + return -EINVAL; + } + + if (led->channel >= LP3952_BLUE_1) { + reg = LP3952_REG_RGB1_MAX_I_CTRL; + shift_val = (led->channel - LP3952_BLUE_1) * 2; + } else { + reg = LP3952_REG_RGB2_MAX_I_CTRL; + shift_val = led->channel * 2; + } + + /* Enable the LED in case it is not enabled already */ + lp3952_on_off(priv, led->channel, true); + + return regmap_update_bits(priv->regmap, reg, 3 << shift_val, + --value << shift_val); +} + +static int lp3952_get_label(struct device *dev, const char *label, char *dest) +{ + int ret; + const char *str; + + ret = device_property_read_string(dev, label, &str); + if (!ret) + strncpy(dest, str, LP3952_LABEL_MAX_LEN); + + return ret; +} + +static int lp3952_register_led_classdev(struct lp3952_led_array *priv) +{ + int i, acpi_ret, ret = -ENODEV; + static const char *led_name_hdl[LP3952_LED_ALL] = { + "blue2", + "green2", + "red2", + "blue1", + "green1", + "red1" + }; + + for (i = 0; i < LP3952_LED_ALL; i++) { + acpi_ret = lp3952_get_label(&priv->client->dev, led_name_hdl[i], + priv->leds[i].name); + if (acpi_ret) + continue; + + priv->leds[i].cdev.name = priv->leds[i].name; + priv->leds[i].cdev.brightness = LED_OFF; + priv->leds[i].cdev.max_brightness = LP3952_BRIGHT_MAX; + priv->leds[i].cdev.brightness_set_blocking = + lp3952_set_brightness; + priv->leds[i].channel = i; + priv->leds[i].priv = priv; + + ret = devm_led_classdev_register(&priv->client->dev, + &priv->leds[i].cdev); + if (ret < 0) { + dev_err(&priv->client->dev, + "couldn't register LED %s\n", + priv->leds[i].cdev.name); + break; + } + } + return ret; +} + +static int lp3952_set_pattern_gen_cmd(struct lp3952_led_array *priv, + u8 cmd_index, u8 r, u8 g, u8 b, + enum lp3952_tt tt, enum lp3952_cet cet) +{ + int ret; + struct ptrn_gen_cmd line = { + { + { + .r = r, + .g = g, + .b = b, + .cet = cet, + .tt = tt + } + } + }; + + if (cmd_index >= LP3952_CMD_REG_COUNT) + return -EINVAL; + + ret = lp3952_register_write(priv->client, + LP3952_REG_CMD_0 + cmd_index * 2, + line.bytes.msb); + if (ret) + return ret; + + return lp3952_register_write(priv->client, + LP3952_REG_CMD_0 + cmd_index * 2 + 1, + line.bytes.lsb); +} + +static int lp3952_configure(struct lp3952_led_array *priv) +{ + int ret; + + /* Disable any LEDs on from any previous conf. */ + ret = lp3952_register_write(priv->client, LP3952_REG_LED_CTRL, 0); + if (ret) + return ret; + + /* enable rgb patter, loop */ + ret = lp3952_register_write(priv->client, LP3952_REG_PAT_GEN_CTRL, + LP3952_PATRN_LOOP | LP3952_PATRN_GEN_EN); + if (ret) + return ret; + + /* Update Bit 6 (Active mode), Select both Led sets, Bit [1:0] */ + ret = lp3952_register_write(priv->client, LP3952_REG_ENABLES, + LP3952_ACTIVE_MODE | LP3952_INT_B00ST_LDR); + if (ret) + return ret; + + /* Set Cmd1 for RGB intensity,cmd and transition time */ + return lp3952_set_pattern_gen_cmd(priv, 0, I46, I71, I100, TT0, + CET197); +} + +static const struct regmap_config lp3952_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, + .cache_type = REGCACHE_RBTREE, +}; + +static int lp3952_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int status; + struct lp3952_led_array *priv; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->enable_gpio = devm_gpiod_get(&client->dev, "nrst", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->enable_gpio)) { + status = PTR_ERR(priv->enable_gpio); + dev_err(&client->dev, "Failed to enable gpio: %d\n", status); + return status; + } + + priv->regmap = devm_regmap_init_i2c(client, &lp3952_regmap); + if (IS_ERR(priv->regmap)) { + int err = PTR_ERR(priv->regmap); + + dev_err(&client->dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + i2c_set_clientdata(client, priv); + + status = lp3952_configure(priv); + if (status) { + dev_err(&client->dev, "Probe failed. Device not found (%d)\n", + status); + return status; + } + + status = lp3952_register_led_classdev(priv); + if (status) { + dev_err(&client->dev, "Unable to register led_classdev: %d\n", + status); + return status; + } + + return 0; +} + +static int lp3952_remove(struct i2c_client *client) +{ + struct lp3952_led_array *priv; + + priv = i2c_get_clientdata(client); + lp3952_on_off(priv, LP3952_LED_ALL, false); + gpiod_set_value(priv->enable_gpio, 0); + + return 0; +} + +static const struct i2c_device_id lp3952_id[] = { + {LP3952_NAME, 0}, + {} +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id lp3952_acpi_match[] = { + {"TXNW3952", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, lp3952_acpi_match); +#endif + +static struct i2c_driver lp3952_i2c_driver = { + .driver = { + .name = LP3952_NAME, + .acpi_match_table = ACPI_PTR(lp3952_acpi_match), + }, + .probe = lp3952_probe, + .remove = lp3952_remove, + .id_table = lp3952_id, +}; + +module_i2c_driver(lp3952_i2c_driver); + +MODULE_AUTHOR("Tony Makkiel "); +MODULE_DESCRIPTION("lp3952 I2C LED controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/leds-lp3952.h b/include/linux/leds-lp3952.h new file mode 100644 index 000000000000..49b37ed8d456 --- /dev/null +++ b/include/linux/leds-lp3952.h @@ -0,0 +1,125 @@ +/* + * LED driver for TI lp3952 controller + * + * Copyright (C) 2016, DAQRI, LLC. + * Author: Tony Makkiel + * + * 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 LEDS_LP3952_H_ +#define LEDS_LP3952_H_ + +#define LP3952_NAME "lp3952" +#define LP3952_CMD_REG_COUNT 8 +#define LP3952_BRIGHT_MAX 4 +#define LP3952_LABEL_MAX_LEN 15 + +#define LP3952_REG_LED_CTRL 0x00 +#define LP3952_REG_R1_BLNK_TIME_CTRL 0x01 +#define LP3952_REG_R1_BLNK_CYCLE_CTRL 0x02 +#define LP3952_REG_G1_BLNK_TIME_CTRL 0x03 +#define LP3952_REG_G1_BLNK_CYCLE_CTRL 0x04 +#define LP3952_REG_B1_BLNK_TIME_CTRL 0x05 +#define LP3952_REG_B1_BLNK_CYCLE_CTRL 0x06 +#define LP3952_REG_ENABLES 0x0B +#define LP3952_REG_PAT_GEN_CTRL 0x11 +#define LP3952_REG_RGB1_MAX_I_CTRL 0x12 +#define LP3952_REG_RGB2_MAX_I_CTRL 0x13 +#define LP3952_REG_CMD_0 0x50 +#define LP3952_REG_RESET 0x60 +#define REG_MAX LP3952_REG_RESET + +#define LP3952_PATRN_LOOP BIT(1) +#define LP3952_PATRN_GEN_EN BIT(2) +#define LP3952_INT_B00ST_LDR BIT(2) +#define LP3952_ACTIVE_MODE BIT(6) +#define LP3952_LED_MASK_ALL 0x3f + +/* Transition Time in ms */ +enum lp3952_tt { + TT0, + TT55, + TT110, + TT221, + TT422, + TT885, + TT1770, + TT3539 +}; + +/* Command Execution Time in ms */ +enum lp3952_cet { + CET197, + CET393, + CET590, + CET786, + CET1180, + CET1376, + CET1573, + CET1769, + CET1966, + CET2163, + CET2359, + CET2556, + CET2763, + CET2949, + CET3146 +}; + +/* Max Current in % */ +enum lp3952_colour_I_log_0 { + I0, + I7, + I14, + I21, + I32, + I46, + I71, + I100 +}; + +enum lp3952_leds { + LP3952_BLUE_2, + LP3952_GREEN_2, + LP3952_RED_2, + LP3952_BLUE_1, + LP3952_GREEN_1, + LP3952_RED_1, + LP3952_LED_ALL +}; + +struct lp3952_ctrl_hdl { + struct led_classdev cdev; + char name[LP3952_LABEL_MAX_LEN]; + enum lp3952_leds channel; + void *priv; +}; + +struct ptrn_gen_cmd { + union { + struct { + u16 tt:3; + u16 b:3; + u16 cet:4; + u16 g:3; + u16 r:3; + }; + struct { + u8 lsb; + u8 msb; + } bytes; + }; +} __packed; + +struct lp3952_led_array { + struct regmap *regmap; + struct i2c_client *client; + struct gpio_desc *enable_gpio; + struct lp3952_ctrl_hdl leds[LP3952_LED_ALL]; +}; + +#endif /* LEDS_LP3952_H_ */ -- cgit v1.2.3 From 1d991b71b4fbbeab319154e9aa2f1e9c94976311 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Mon, 18 Jul 2016 19:03:30 +0200 Subject: leds: is31fl32xx: fix typo in id and match table names Signed-off-by: H. Nikolaus Schaller Tested-by: David Rivshin Acked-by: David Rivshin Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-is31fl32xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index c901d132d80c..a53b71e7a50d 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -422,7 +422,7 @@ err: return ret; } -static const struct of_device_id of_is31fl31xx_match[] = { +static const struct of_device_id of_is31fl32xx_match[] = { { .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, }, { .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, }, { .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, }, @@ -432,7 +432,7 @@ static const struct of_device_id of_is31fl31xx_match[] = { {}, }; -MODULE_DEVICE_TABLE(of, of_is31fl31xx_match); +MODULE_DEVICE_TABLE(of, of_is31fl32xx_match); static int is31fl32xx_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -444,7 +444,7 @@ static int is31fl32xx_probe(struct i2c_client *client, int count; int ret = 0; - of_dev_id = of_match_device(of_is31fl31xx_match, dev); + of_dev_id = of_match_device(of_is31fl32xx_match, dev); if (!of_dev_id) return -EINVAL; @@ -485,20 +485,20 @@ static int is31fl32xx_remove(struct i2c_client *client) * i2c-core requires that id_table be non-NULL, even though * it is not used for DeviceTree based instantiation. */ -static const struct i2c_device_id is31fl31xx_id[] = { +static const struct i2c_device_id is31fl32xx_id[] = { {}, }; -MODULE_DEVICE_TABLE(i2c, is31fl31xx_id); +MODULE_DEVICE_TABLE(i2c, is31fl32xx_id); static struct i2c_driver is31fl32xx_driver = { .driver = { .name = "is31fl32xx", - .of_match_table = of_is31fl31xx_match, + .of_match_table = of_is31fl32xx_match, }, .probe = is31fl32xx_probe, .remove = is31fl32xx_remove, - .id_table = is31fl31xx_id, + .id_table = is31fl32xx_id, }; module_i2c_driver(is31fl32xx_driver); -- cgit v1.2.3 From 5706c01fcfb313cef7aed6a283b43c5452cf8336 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Mon, 18 Jul 2016 19:03:31 +0200 Subject: leds: is31fl32xx: define complete i2c_device_id table Otherwise the driver module will not be automatically probed. Fixes: 9d7cffaf99f5 ("leds: Add driver for the ISSI IS31FL32xx family of LED controllers") Fixes: e0442d7def8f ("leds: Add SN3218 and SN3216 support to the IS31FL32XX driver") Signed-off-by: H. Nikolaus Schaller Tested-by: David Rivshin Acked-by: David Rivshin Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-is31fl32xx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index a53b71e7a50d..478844c5cead 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -482,10 +482,16 @@ static int is31fl32xx_remove(struct i2c_client *client) } /* - * i2c-core requires that id_table be non-NULL, even though - * it is not used for DeviceTree based instantiation. + * i2c-core (and modalias) requires that id_table be properly filled, + * even though it is not used for DeviceTree based instantiation. */ static const struct i2c_device_id is31fl32xx_id[] = { + { "is31fl3236" }, + { "is31fl3235" }, + { "is31fl3218" }, + { "sn3218" }, + { "is31fl3216" }, + { "sn3216" }, {}, }; -- cgit v1.2.3