From fc19967bcb8f1ab49594191ee0d352d763dc170e Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Thu, 19 Sep 2019 15:53:13 +0300 Subject: leds: add LED driver for EL15203000 board This patch adds a LED class driver for the LEDs found on the Crane Merchandising System EL15203000 LEDs board (aka RED LEDs board). Signed-off-by: Oleh Kravchenko Reviewed-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 13 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-el15203000.c | 357 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/leds/leds-el15203000.c (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1988de1d64c0..6e7703fd03d0 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -132,6 +132,19 @@ config LEDS_CR0014114 To compile this driver as a module, choose M here: the module will be called leds-cr0014114. +config LEDS_EL15203000 + tristate "LED Support for Crane EL15203000" + depends on LEDS_CLASS + depends on SPI + depends on OF + help + This option enables support for EL15203000 LED Board + (aka RED LED board) which is widely used in coffee vending + machines produced by Crane Merchandising Systems. + + To compile this driver as a module, choose M here: the module + will be called leds-el15203000. + config LEDS_LM3530 tristate "LCD Backlight driver for LM3530" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 41fb073a39c1..2da39e896ce8 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o # LED SPI Drivers obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o +obj-$(CONFIG_LEDS_EL15203000) += leds-el15203000.o # LED Userspace Drivers obj-$(CONFIG_LEDS_USER) += uleds.o diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c new file mode 100644 index 000000000000..298b13e4807a --- /dev/null +++ b/drivers/leds/leds-el15203000.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Crane Merchandising Systems. All rights reserved. +// Copyright (C) 2019 Oleh Kravchenko + +#include +#include +#include +#include +#include + +/* + * EL15203000 SPI protocol description: + * +-----+---------+ + * | LED | COMMAND | + * +-----+---------+ + * | 1 | 1 | + * +-----+---------+ + * (*) LEDs MCU board expects 20 msec delay per byte. + * + * LEDs: + * +----------+--------------+-------------------------------------------+ + * | ID | NAME | DESCRIPTION | + * +----------+--------------+-------------------------------------------+ + * | 'P' 0x50 | Pipe | Consists from 5 LEDs, controlled by board | + * +----------+--------------+-------------------------------------------+ + * | 'S' 0x53 | Screen frame | Light tube around the screen | + * +----------+--------------+-------------------------------------------+ + * | 'V' 0x56 | Vending area | Highlights a cup of coffee | + * +----------+--------------+-------------------------------------------+ + * + * COMMAND: + * +----------+-----------------+--------------+--------------+ + * | VALUES | PIPE | SCREEN FRAME | VENDING AREA | + * +----------+-----------------+--------------+--------------+ + * | '0' 0x30 | Off | + * +----------+-----------------------------------------------+ + * | '1' 0x31 | On | + * +----------+-----------------+--------------+--------------+ + * | '2' 0x32 | Cascade | Breathing | + * +----------+-----------------+--------------+ + * | '3' 0x33 | Inverse cascade | + * +----------+-----------------+ + * | '4' 0x34 | Bounce | + * +----------+-----------------+ + * | '5' 0x35 | Inverse bounce | + * +----------+-----------------+ + */ + +/* EL15203000 default settings */ +#define EL_FW_DELAY_USEC 20000ul +#define EL_PATTERN_DELAY_MSEC 800u +#define EL_PATTERN_LEN 10u +#define EL_PATTERN_HALF_LEN (EL_PATTERN_LEN / 2) + +enum el15203000_command { + /* for all LEDs */ + EL_OFF = '0', + EL_ON = '1', + + /* for Screen LED */ + EL_SCREEN_BREATHING = '2', + + /* for Pipe LED */ + EL_PIPE_CASCADE = '2', + EL_PIPE_INV_CASCADE = '3', + EL_PIPE_BOUNCE = '4', + EL_PIPE_INV_BOUNCE = '5', +}; + +struct el15203000_led { + struct el15203000 *priv; + struct led_classdev ldev; + u32 reg; +}; + +struct el15203000 { + struct device *dev; + struct mutex lock; + struct spi_device *spi; + unsigned long delay; + size_t count; + struct el15203000_led leds[]; +}; + +static int el15203000_cmd(struct el15203000_led *led, u8 brightness) +{ + int ret; + u8 cmd[2]; + size_t i; + + mutex_lock(&led->priv->lock); + + dev_dbg(led->priv->dev, "Set brightness of 0x%02x(%c) to 0x%02x(%c)", + led->reg, led->reg, brightness, brightness); + + /* to avoid SPI mistiming with firmware we should wait some time */ + if (time_after(led->priv->delay, jiffies)) { + dev_dbg(led->priv->dev, "Wait %luus to sync", + EL_FW_DELAY_USEC); + + usleep_range(EL_FW_DELAY_USEC, + EL_FW_DELAY_USEC + 1); + } + + cmd[0] = led->reg; + cmd[1] = brightness; + + for (i = 0; i < ARRAY_SIZE(cmd); i++) { + if (i) + usleep_range(EL_FW_DELAY_USEC, + EL_FW_DELAY_USEC + 1); + + ret = spi_write(led->priv->spi, &cmd[i], sizeof(cmd[i])); + if (ret) { + dev_err(led->priv->dev, + "spi_write() error %d", ret); + break; + } + } + + led->priv->delay = jiffies + usecs_to_jiffies(EL_FW_DELAY_USEC); + + mutex_unlock(&led->priv->lock); + + return ret; +} + +static int el15203000_set_blocking(struct led_classdev *ldev, + enum led_brightness brightness) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + return el15203000_cmd(led, brightness == LED_OFF ? EL_OFF : EL_ON); +} + +static int el15203000_pattern_set_S(struct led_classdev *ldev, + struct led_pattern *pattern, + u32 len, int repeat) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + if (repeat > 0 || len != 2 || + pattern[0].delta_t != 4000 || pattern[0].brightness != 0 || + pattern[1].delta_t != 4000 || pattern[1].brightness != 1) + return -EINVAL; + + dev_dbg(led->priv->dev, "Breathing mode for 0x%02x(%c)", + led->reg, led->reg); + + return el15203000_cmd(led, EL_SCREEN_BREATHING); +} + +static bool is_cascade(const struct led_pattern *pattern, u32 len, + bool inv, bool right) +{ + int val, t; + u32 i; + + if (len != EL_PATTERN_HALF_LEN) + return false; + + val = right ? BIT(4) : BIT(0); + + for (i = 0; i < len; i++) { + t = inv ? ~val & GENMASK(4, 0) : val; + + if (pattern[i].delta_t != EL_PATTERN_DELAY_MSEC || + pattern[i].brightness != t) + return false; + + val = right ? val >> 1 : val << 1; + } + + return true; +} + +static bool is_bounce(const struct led_pattern *pattern, u32 len, bool inv) +{ + if (len != EL_PATTERN_LEN) + return false; + + return is_cascade(pattern, EL_PATTERN_HALF_LEN, inv, false) && + is_cascade(pattern + EL_PATTERN_HALF_LEN, + EL_PATTERN_HALF_LEN, inv, true); +} + +static int el15203000_pattern_set_P(struct led_classdev *ldev, + struct led_pattern *pattern, + u32 len, int repeat) +{ + u8 cmd; + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + if (repeat > 0) + return -EINVAL; + + if (is_cascade(pattern, len, false, false)) { + dev_dbg(led->priv->dev, "Cascade mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_CASCADE; + } else if (is_cascade(pattern, len, true, false)) { + dev_dbg(led->priv->dev, "Inverse cascade mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_INV_CASCADE; + } else if (is_bounce(pattern, len, false)) { + dev_dbg(led->priv->dev, "Bounce mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_BOUNCE; + } else if (is_bounce(pattern, len, true)) { + dev_dbg(led->priv->dev, "Inverse bounce mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_INV_BOUNCE; + } else { + dev_err(led->priv->dev, "Invalid hw_pattern for 0x%02x(%c)!", + led->reg, led->reg); + + return -EINVAL; + } + + return el15203000_cmd(led, cmd); +} + +static int el15203000_pattern_clear(struct led_classdev *ldev) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + return el15203000_cmd(led, EL_OFF); +} + +static int el15203000_probe_dt(struct el15203000 *priv) +{ + struct el15203000_led *led = priv->leds; + struct fwnode_handle *child; + int ret; + + device_for_each_child_node(priv->dev, child) { + struct led_init_data init_data = {}; + + ret = fwnode_property_read_u32(child, "reg", &led->reg); + if (ret) { + dev_err(priv->dev, "LED without ID number"); + fwnode_handle_put(child); + + break; + } + + if (led->reg > U8_MAX) { + dev_err(priv->dev, "LED value %d is invalid", led->reg); + fwnode_handle_put(child); + + return -EINVAL; + } + + fwnode_property_read_string(child, "linux,default-trigger", + &led->ldev.default_trigger); + + led->priv = priv; + led->ldev.max_brightness = LED_ON; + led->ldev.brightness_set_blocking = el15203000_set_blocking; + + if (led->reg == 'S') { + led->ldev.pattern_set = el15203000_pattern_set_S; + led->ldev.pattern_clear = el15203000_pattern_clear; + } else if (led->reg == 'P') { + led->ldev.pattern_set = el15203000_pattern_set_P; + led->ldev.pattern_clear = el15203000_pattern_clear; + } + + init_data.fwnode = child; + ret = devm_led_classdev_register_ext(priv->dev, &led->ldev, + &init_data); + if (ret) { + dev_err(priv->dev, + "failed to register LED device %s, err %d", + led->ldev.name, ret); + fwnode_handle_put(child); + + break; + } + + led++; + } + + return ret; +} + +static int el15203000_probe(struct spi_device *spi) +{ + struct el15203000 *priv; + size_t count; + + count = device_get_child_node_count(&spi->dev); + if (!count) { + dev_err(&spi->dev, "LEDs are not defined in device tree!"); + return -ENODEV; + } + + priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + priv->count = count; + priv->dev = &spi->dev; + priv->spi = spi; + priv->delay = jiffies - + usecs_to_jiffies(EL_FW_DELAY_USEC); + + spi_set_drvdata(spi, priv); + + return el15203000_probe_dt(priv); +} + +static int el15203000_remove(struct spi_device *spi) +{ + struct el15203000 *priv = spi_get_drvdata(spi); + + mutex_destroy(&priv->lock); + + return 0; +} + +static const struct of_device_id el15203000_dt_ids[] = { + { .compatible = "crane,el15203000", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, el15203000_dt_ids); + +static struct spi_driver el15203000_driver = { + .probe = el15203000_probe, + .remove = el15203000_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = el15203000_dt_ids, + }, +}; + +module_spi_driver(el15203000_driver); + +MODULE_AUTHOR("Oleh Kravchenko "); +MODULE_DESCRIPTION("el15203000 LED driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:el15203000"); -- cgit From b46d2b4d3d8166ab51f491a2801e2cbed2e8a7aa Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 13 Sep 2019 20:07:49 +0200 Subject: drivers: leds: tlc591xx: check error during device init The driver currently ignores errors from register writes at probe time. It will hence register an LED class device no matter whether the pyhsical device is present or not. To fix this, make the device probe fail in case regmap operations return an error. Signed-off-by: Daniel Mack Signed-off-by: Pavel Machek --- drivers/leds/leds-tlc591xx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 59ff088c7d75..00702824d27c 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -147,7 +147,10 @@ tlc591xx_configure(struct device *dev, unsigned int i; int err = 0; - tlc591xx_set_mode(priv->regmap, MODE2_DIM); + err = tlc591xx_set_mode(priv->regmap, MODE2_DIM); + if (err < 0) + return err; + for (i = 0; i < TLC591XX_MAX_LEDS; i++) { struct tlc591xx_led *led = &priv->leds[i]; -- cgit From be9f18eef60197a0da8bc514f7bb9512dbc04c48 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 20 Sep 2019 14:30:31 +0200 Subject: leds: bcm6328: Use devm_platform_ioremap_resource() in bcm6328_leds_probe() Simplify this function implementation by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Pavel Machek --- drivers/leds/leds-bcm6328.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c index c50d34e2b098..42e1b7598c3a 100644 --- a/drivers/leds/leds-bcm6328.c +++ b/drivers/leds/leds-bcm6328.c @@ -346,16 +346,11 @@ static int bcm6328_leds_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *child; - struct resource *mem_r; void __iomem *mem; spinlock_t *lock; /* memory lock */ unsigned long val, *blink_leds, *blink_delay; - mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_r) - return -EINVAL; - - mem = devm_ioremap_resource(dev, mem_r); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); -- cgit From 8b4423d6c5e6515bf6ad7c6c51dfb0a4a95ec606 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 20 Sep 2019 14:44:06 +0200 Subject: leds: bcm6358: Use devm_platform_ioremap_resource() in bcm6358_leds_probe() Simplify this function implementation by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Pavel Machek --- drivers/leds/leds-bcm6358.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c index aec285fd21c0..94fefd456ba0 100644 --- a/drivers/leds/leds-bcm6358.c +++ b/drivers/leds/leds-bcm6358.c @@ -151,17 +151,12 @@ static int bcm6358_leds_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *child; - struct resource *mem_r; void __iomem *mem; spinlock_t *lock; /* memory lock */ unsigned long val; u32 clk_div; - mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_r) - return -EINVAL; - - mem = devm_ioremap_resource(dev, mem_r); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); -- cgit From 1ab4531ad13208c7721bd98300e2aa7a3a5500a3 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 20 Sep 2019 13:58:05 +0200 Subject: leds: tlc591xx: simplify driver by using the managed led API Use the managed API of the LED class (devm_led_classdev_register() instead of led_classdev_register()). This allows us to remove the code used to track-and-destroy the LED devices. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Tomi Valkeinen Signed-off-by: Pavel Machek --- drivers/leds/leds-tlc591xx.c | 84 ++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 62 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 00702824d27c..bbdaa3148317 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -128,54 +128,6 @@ tlc591xx_brightness_set(struct led_classdev *led_cdev, return err; } -static void -tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j) -{ - int i = j; - - while (--i >= 0) { - if (priv->leds[i].active) - led_classdev_unregister(&priv->leds[i].ldev); - } -} - -static int -tlc591xx_configure(struct device *dev, - struct tlc591xx_priv *priv, - const struct tlc591xx *tlc591xx) -{ - unsigned int i; - int err = 0; - - err = tlc591xx_set_mode(priv->regmap, MODE2_DIM); - if (err < 0) - return err; - - for (i = 0; i < TLC591XX_MAX_LEDS; i++) { - struct tlc591xx_led *led = &priv->leds[i]; - - if (!led->active) - continue; - - led->priv = priv; - led->led_no = i; - led->ldev.brightness_set_blocking = tlc591xx_brightness_set; - led->ldev.max_brightness = LED_FULL; - err = led_classdev_register(dev, &led->ldev); - if (err < 0) { - dev_err(dev, "couldn't register LED %s\n", - led->ldev.name); - goto exit; - } - } - - return 0; - -exit: - tlc591xx_destroy_devices(priv, i); - return err; -} - static const struct regmap_config tlc591xx_regmap = { .reg_bits = 8, .val_bits = 8, @@ -228,7 +180,13 @@ tlc591xx_probe(struct i2c_client *client, i2c_set_clientdata(client, priv); + err = tlc591xx_set_mode(priv->regmap, MODE2_DIM); + if (err < 0) + return err; + for_each_child_of_node(np, child) { + struct tlc591xx_led *led; + err = of_property_read_u32(child, "reg", ®); if (err) { of_node_put(child); @@ -239,22 +197,25 @@ tlc591xx_probe(struct i2c_client *client, of_node_put(child); return -EINVAL; } - priv->leds[reg].active = true; - priv->leds[reg].ldev.name = + led = &priv->leds[reg]; + + led->active = true; + led->ldev.name = of_get_property(child, "label", NULL) ? : child->name; - priv->leds[reg].ldev.default_trigger = + led->ldev.default_trigger = of_get_property(child, "linux,default-trigger", NULL); - } - return tlc591xx_configure(dev, priv, tlc591xx); -} - -static int -tlc591xx_remove(struct i2c_client *client) -{ - struct tlc591xx_priv *priv = i2c_get_clientdata(client); - - tlc591xx_destroy_devices(priv, TLC591XX_MAX_LEDS); + led->priv = priv; + led->led_no = reg; + led->ldev.brightness_set_blocking = tlc591xx_brightness_set; + led->ldev.max_brightness = LED_FULL; + err = devm_led_classdev_register(dev, &led->ldev); + if (err < 0) { + dev_err(dev, "couldn't register LED %s\n", + led->ldev.name); + return err; + } + } return 0; } @@ -271,7 +232,6 @@ static struct i2c_driver tlc591xx_driver = { .of_match_table = of_match_ptr(of_tlc591xx_leds_match), }, .probe = tlc591xx_probe, - .remove = tlc591xx_remove, .id_table = tlc591xx_id, }; -- cgit From 5b4b723c483f5027da77409f34b8b4602ee1e557 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 20 Sep 2019 13:58:06 +0200 Subject: leds: tlc591xx: use devm_led_classdev_register_ext() Use devm_led_classdev_register_ext() to pass the fwnode to the LED core. The fwnode can then be used by the firmware core to create meaningful names. Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Pavel Machek --- drivers/leds/leds-tlc591xx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index bbdaa3148317..8eadb673dc2e 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -186,6 +186,9 @@ tlc591xx_probe(struct i2c_client *client, for_each_child_of_node(np, child) { struct tlc591xx_led *led; + struct led_init_data init_data = {}; + + init_data.fwnode = of_fwnode_handle(child); err = of_property_read_u32(child, "reg", ®); if (err) { @@ -200,8 +203,6 @@ tlc591xx_probe(struct i2c_client *client, led = &priv->leds[reg]; led->active = true; - led->ldev.name = - of_get_property(child, "label", NULL) ? : child->name; led->ldev.default_trigger = of_get_property(child, "linux,default-trigger", NULL); @@ -209,7 +210,8 @@ tlc591xx_probe(struct i2c_client *client, led->led_no = reg; led->ldev.brightness_set_blocking = tlc591xx_brightness_set; led->ldev.max_brightness = LED_FULL; - err = devm_led_classdev_register(dev, &led->ldev); + err = devm_led_classdev_register_ext(dev, &led->ldev, + &init_data); if (err < 0) { dev_err(dev, "couldn't register LED %s\n", led->ldev.name); -- cgit From 1051da2cfc7ef37ffc3e7a595dc80fefb41f09ad Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 21 Sep 2019 14:12:08 -0700 Subject: leds: lm3692x: Print error value on dev_err MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives a way better idea what is going on. Signed-off-by: Guido Günther Reviewed-by: Dan Murphy Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 3d381f2f73d0..487228c2bed2 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -174,19 +174,20 @@ static int lm3692x_brightness_set(struct led_classdev *led_cdev, ret = lm3692x_fault_check(led); if (ret) { - dev_err(&led->client->dev, "Cannot read/clear faults\n"); + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", + ret); goto out; } ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val); if (ret) { - dev_err(&led->client->dev, "Cannot write MSB\n"); + dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret); goto out; } ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb); if (ret) { - dev_err(&led->client->dev, "Cannot write LSB\n"); + dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret); goto out; } out: @@ -203,7 +204,7 @@ static int lm3692x_init(struct lm3692x_led *led) ret = regulator_enable(led->regulator); if (ret) { dev_err(&led->client->dev, - "Failed to enable regulator\n"); + "Failed to enable regulator: %d\n", ret); return ret; } } @@ -213,7 +214,8 @@ static int lm3692x_init(struct lm3692x_led *led) ret = lm3692x_fault_check(led); if (ret) { - dev_err(&led->client->dev, "Cannot read/clear faults\n"); + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", + ret); goto out; } @@ -409,7 +411,8 @@ static int lm3692x_remove(struct i2c_client *client) ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0); if (ret) { - dev_err(&led->client->dev, "Failed to disable regulator\n"); + dev_err(&led->client->dev, "Failed to disable regulator: %d\n", + ret); return ret; } @@ -420,7 +423,7 @@ static int lm3692x_remove(struct i2c_client *client) ret = regulator_disable(led->regulator); if (ret) dev_err(&led->client->dev, - "Failed to disable regulator\n"); + "Failed to disable regulator: %d\n", ret); } mutex_destroy(&led->lock); -- cgit From d0f9cc49e9c8afae49f0efc7927156dbd555ee93 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 21 Sep 2019 14:12:09 -0700 Subject: leds: lm3692x: Don't overwrite return value in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver currently reports successful initialization on every failure as long as it's able to power off the regulator. Don't check the return value of regulator_disable to avoid that. Signed-off-by: Guido Günther Acked-by: Pavel Machek Reviewed-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 487228c2bed2..ad76e34455ff 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -198,7 +198,7 @@ out: static int lm3692x_init(struct lm3692x_led *led) { int enable_state; - int ret; + int ret, reg_ret; if (led->regulator) { ret = regulator_enable(led->regulator); @@ -313,14 +313,15 @@ out: gpiod_direction_output(led->enable_gpio, 0); if (led->regulator) { - ret = regulator_disable(led->regulator); - if (ret) + reg_ret = regulator_disable(led->regulator); + if (reg_ret) dev_err(&led->client->dev, - "Failed to disable regulator\n"); + "Failed to disable regulator: %d\n", reg_ret); } return ret; } + static int lm3692x_probe_dt(struct lm3692x_led *led) { struct fwnode_handle *child = NULL; -- cgit From 396128d2ffcba6e1954cfdc9a89293ff79cbfd7c Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 21 Sep 2019 14:12:10 -0700 Subject: leds: lm3692x: Handle failure to probe the regulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead use devm_regulator_get_optional since the regulator is optional and check for errors. Signed-off-by: Guido Günther Acked-by: Pavel Machek Reviewed-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index ad76e34455ff..54e9bd2d288b 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -337,9 +337,18 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) return ret; } - led->regulator = devm_regulator_get(&led->client->dev, "vled"); - if (IS_ERR(led->regulator)) + led->regulator = devm_regulator_get_optional(&led->client->dev, "vled"); + if (IS_ERR(led->regulator)) { + ret = PTR_ERR(led->regulator); + if (ret != -ENODEV) { + if (ret != -EPROBE_DEFER) + dev_err(&led->client->dev, + "Failed to get vled regulator: %d\n", + ret); + return ret; + } led->regulator = NULL; + } child = device_get_next_child_node(&led->client->dev, child); if (!child) { -- cgit From 846d0d14e7ec7935ab79e6cebf752396d05c2609 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 21 Sep 2019 14:12:11 -0700 Subject: leds: lm3692x: Use flags from LM3692X_BOOST_CTRL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current setup of LM3692X_BOOST_CTRL uses flags from LM3692X_BRT_CTRL. Use flags from LM3692X_BOOST_CTRL but leave the resulting register value unchanged. Signed-off-by: Guido Günther Reviewed-by: Dan Murphy Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 54e9bd2d288b..a57b1571e359 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -250,9 +250,9 @@ static int lm3692x_init(struct lm3692x_led *led) goto out; ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, - LM3692X_BRHT_MODE_RAMP_MULTI | - LM3692X_BL_ADJ_POL | - LM3692X_RAMP_RATE_250us); + LM3692X_BOOST_SW_1MHZ | + LM3692X_BOOST_SW_NO_SHIFT | + LM3692X_OCP_PROT_1_5A); if (ret) goto out; -- cgit From da61a66a829d67b854089aa5ec4c5a2031bb0864 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 21 Sep 2019 14:12:12 -0700 Subject: leds: lm3692x: Use flags from LM3692X_BRT_CTRL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use LM3692X_RAMP_EN instead of LM3692X_PWM_HYSTER_4LSB since the later is a flag for the PWM register. The actual register value remains unchanged. Signed-off-by: Guido Günther Reviewed-by: Dan Murphy Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index a57b1571e359..8b408102e138 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -269,7 +269,7 @@ static int lm3692x_init(struct lm3692x_led *led) goto out; ret = regmap_write(led->regmap, LM3692X_BRT_CTRL, - LM3692X_BL_ADJ_POL | LM3692X_PWM_HYSTER_4LSB); + LM3692X_BL_ADJ_POL | LM3692X_RAMP_EN); if (ret) goto out; -- cgit From a2cafdfd8cf5ad8adda6c0ce44a59f46431edf02 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Mon, 23 Sep 2019 12:02:50 +0200 Subject: leds: tlc591xx: update the maximum brightness The TLC chips actually offer 257 levels: - 0: led OFF - 1-255: Led dimmed is using a PWM. The duty cycle range from 0.4% to 99.6% - 256: led fully ON Fixes: e370d010a5fe ("leds: tlc591xx: Driver for the TI 8/16 Channel i2c LED driver") Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Pavel Machek --- drivers/leds/leds-tlc591xx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 8eadb673dc2e..a8911ebd30e5 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -13,6 +13,7 @@ #include #define TLC591XX_MAX_LEDS 16 +#define TLC591XX_MAX_BRIGHTNESS 256 #define TLC591XX_REG_MODE1 0x00 #define MODE1_RESPON_ADDR_MASK 0xF0 @@ -112,11 +113,11 @@ tlc591xx_brightness_set(struct led_classdev *led_cdev, struct tlc591xx_priv *priv = led->priv; int err; - switch (brightness) { + switch ((int)brightness) { case 0: err = tlc591xx_set_ledout(priv, led, LEDOUT_OFF); break; - case LED_FULL: + case TLC591XX_MAX_BRIGHTNESS: err = tlc591xx_set_ledout(priv, led, LEDOUT_ON); break; default: @@ -209,7 +210,7 @@ tlc591xx_probe(struct i2c_client *client, led->priv = priv; led->led_no = reg; led->ldev.brightness_set_blocking = tlc591xx_brightness_set; - led->ldev.max_brightness = LED_FULL; + led->ldev.max_brightness = TLC591XX_MAX_BRIGHTNESS; err = devm_led_classdev_register_ext(dev, &led->ldev, &init_data); if (err < 0) { -- cgit From 11f70002213774ed233950f71ea8803fa3700aa3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 29 Sep 2019 23:18:49 +0900 Subject: leds: remove PAGE_SIZE limit of /sys/class/leds//trigger Reading /sys/class/leds//trigger returns all available LED triggers. However, the size of this file is limited to PAGE_SIZE because of the limitation for sysfs attribute. Enabling LED CPU trigger on systems with thousands of CPUs easily hits PAGE_SIZE limit, and makes it impossible to see all available LED triggers and which trigger is currently activated. We work around it here by converting /sys/class/leds//trigger to binary attribute, which is not limited by length. This is _not_ good design, do not copy it. Signed-off-by: Akinobu Mita Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Dan Murphy A Reviewed-by: Greg Kroah-Hartman Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 8 ++-- drivers/leds/led-triggers.c | 90 ++++++++++++++++++++++++++++++++++----------- drivers/leds/leds.h | 6 +++ 3 files changed, 78 insertions(+), 26 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 647b1263c579..3f04334d59ee 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -74,13 +74,13 @@ static ssize_t max_brightness_show(struct device *dev, static DEVICE_ATTR_RO(max_brightness); #ifdef CONFIG_LEDS_TRIGGERS -static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); -static struct attribute *led_trigger_attrs[] = { - &dev_attr_trigger.attr, +static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0); +static struct bin_attribute *led_trigger_bin_attrs[] = { + &bin_attr_trigger, NULL, }; static const struct attribute_group led_trigger_group = { - .attrs = led_trigger_attrs, + .bin_attrs = led_trigger_bin_attrs, }; #endif diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 23963e5cb5d6..79e30d2cb7a5 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "leds.h" /* @@ -26,9 +27,11 @@ LIST_HEAD(trigger_list); /* Used by LED Class */ -ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count) { + struct device *dev = kobj_to_dev(kobj); struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; int ret = count; @@ -64,39 +67,82 @@ unlock: mutex_unlock(&led_cdev->led_access); return ret; } -EXPORT_SYMBOL_GPL(led_trigger_store); +EXPORT_SYMBOL_GPL(led_trigger_write); -ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, - char *buf) +__printf(3, 4) +static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + if (size <= 0) + i = vsnprintf(NULL, 0, fmt, args); + else + i = vscnprintf(buf, size, fmt, args); + va_end(args); + + return i; +} + +static int led_trigger_format(char *buf, size_t size, + struct led_classdev *led_cdev) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; - int len = 0; + int len = led_trigger_snprintf(buf, size, "%s", + led_cdev->trigger ? "none" : "[none]"); + + list_for_each_entry(trig, &trigger_list, next_trig) { + bool hit = led_cdev->trigger && + !strcmp(led_cdev->trigger->name, trig->name); + + len += led_trigger_snprintf(buf + len, size - len, + " %s%s%s", hit ? "[" : "", + trig->name, hit ? "]" : ""); + } + + len += led_trigger_snprintf(buf + len, size - len, "\n"); + + return len; +} + +/* + * It was stupid to create 10000 cpu triggers, but we are stuck with it now. + * Don't make that mistake again. We work around it here by creating binary + * attribute, which is not limited by length. This is _not_ good design, do not + * copy it. + */ +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *led_cdev = dev_get_drvdata(dev); + void *data; + int len; down_read(&triggers_list_lock); down_read(&led_cdev->trigger_lock); - if (!led_cdev->trigger) - len += scnprintf(buf+len, PAGE_SIZE - len, "[none] "); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "none "); - - list_for_each_entry(trig, &trigger_list, next_trig) { - if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, - trig->name)) - len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ", - trig->name); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "%s ", - trig->name); + len = led_trigger_format(NULL, 0, led_cdev); + data = kvmalloc(len + 1, GFP_KERNEL); + if (!data) { + up_read(&led_cdev->trigger_lock); + up_read(&triggers_list_lock); + return -ENOMEM; } + len = led_trigger_format(data, len + 1, led_cdev); + up_read(&led_cdev->trigger_lock); up_read(&triggers_list_lock); - len += scnprintf(len+buf, PAGE_SIZE - len, "\n"); + len = memory_read_from_buffer(buf, count, &pos, data, len); + + kvfree(data); + return len; } -EXPORT_SYMBOL_GPL(led_trigger_show); +EXPORT_SYMBOL_GPL(led_trigger_read); /* Caller must ensure led_cdev->trigger_lock held */ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 0b577cece8f7..2d9eb48bbed9 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -23,6 +23,12 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, enum led_brightness value); void led_set_brightness_nosleep(struct led_classdev *led_cdev, enum led_brightness value); +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t count); +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count); extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; -- cgit From f884e866537ccb10517299b01a5028598653d8d7 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 1 Oct 2019 13:04:35 -0500 Subject: leds: Kconfig: Be consistent with the usage of "LED" Update the Kconfig to be consistent in the case of using "LED" in the Kconfig. LED is an acronym and should be capitalized. Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6e7703fd03d0..4b68520ac251 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -17,7 +17,7 @@ if NEW_LEDS config LEDS_CLASS tristate "LED Class Support" help - This option enables the led sysfs class in /sys/class/leds. You'll + This option enables the LED sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. config LEDS_CLASS_FLASH @@ -35,7 +35,7 @@ config LEDS_BRIGHTNESS_HW_CHANGED depends on LEDS_CLASS help This option enables support for the brightness_hw_changed attribute - for led sysfs class devices under /sys/class/leds. + for LED sysfs class devices under /sys/class/leds. See Documentation/ABI/testing/sysfs-class-led for details. -- cgit From 20cdba9d9c165e475fcc5af97857b6fa7aec96a0 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 2 Oct 2019 07:40:38 -0500 Subject: leds: flash: Add devm_* functions to the flash class Add the missing device managed API for registration and unregistration for the LED flash class. Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/led-class-flash.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'drivers/leds') diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 60c3de5c6b9f..6eeb9effcf65 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -327,6 +327,56 @@ void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) } EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); +static void devm_led_classdev_flash_release(struct device *dev, void *res) +{ + led_classdev_flash_unregister(*(struct led_classdev_flash **)res); +} + +int devm_led_classdev_flash_register_ext(struct device *parent, + struct led_classdev_flash *fled_cdev, + struct led_init_data *init_data) +{ + struct led_classdev_flash **dr; + int ret; + + dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr), + GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data); + if (ret) { + devres_free(dr); + return ret; + } + + *dr = fled_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext); + +static int devm_led_classdev_flash_match(struct device *dev, + void *res, void *data) +{ + struct led_classdev_flash **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +void devm_led_classdev_flash_unregister(struct device *dev, + struct led_classdev_flash *fled_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_flash_release, + devm_led_classdev_flash_match, fled_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister); + static void led_clamp_align(struct led_flash_setting *s) { u32 v, offset; -- cgit From e63a744871a31cebc7860c5b38b3655d70cfc584 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 2 Oct 2019 07:40:39 -0500 Subject: leds: lm3601x: Convert class registration to device managed Convert LED flash class registration to device managed class registration API. Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3601x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/leds-lm3601x.c index b02972f1a341..fce89f2a2d92 100644 --- a/drivers/leds/leds-lm3601x.c +++ b/drivers/leds/leds-lm3601x.c @@ -350,8 +350,7 @@ static int lm3601x_register_leds(struct lm3601x_led *led, init_data.devicename = led->client->name; init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ? "torch" : "infrared"; - - return led_classdev_flash_register_ext(&led->client->dev, + return devm_led_classdev_flash_register_ext(&led->client->dev, &led->fled_cdev, &init_data); } @@ -445,7 +444,6 @@ static int lm3601x_remove(struct i2c_client *client) { struct lm3601x_led *led = i2c_get_clientdata(client); - led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, -- cgit From 4b83cf07d7a4ae70eae2e49086515cad0f42c629 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 2 Oct 2019 07:40:41 -0500 Subject: leds: core: Fix devm_classdev_match to reference correct structure Fix the devm_classdev_match pointer initialization to the correct structure type. Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 3f04334d59ee..438774315e6c 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -403,7 +403,7 @@ EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext); static int devm_led_classdev_match(struct device *dev, void *res, void *data) { - struct led_cdev **p = res; + struct led_classdev **p = res; if (WARN_ON(!p || !*p)) return 0; -- cgit From 66c41131daba0985464d8eb88092908c023ecb66 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 4 Oct 2019 14:43:25 -0700 Subject: leds: pca953x: Use of_device_get_match_data() This driver can use the of_device_get_match_data() API to simplify the code. Replace calls to of_match_device() with this newer API under the assumption that where it is called will be when we know the device is backed by a DT node. This nicely avoids referencing the match table when it is undefined with configurations where CONFIG_OF=n. Cc: Arnd Bergmann Cc: Geert Uytterhoeven Cc: Riku Voipio Cc: Rob Herring Cc: Frank Rowand Cc: Jacek Anaszewski Cc: Pavel Machek Cc: Dan Murphy Cc: Signed-off-by: Stephen Boyd Signed-off-by: Pavel Machek --- drivers/leds/leds-pca9532.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index c7c7199e8ebd..7d515d5e57bd 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -467,16 +467,11 @@ 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; const char *state; - match = of_match_device(of_pca9532_leds_match, dev); - if (!match) - return ERR_PTR(-ENODEV); - - devid = (int)(uintptr_t)match->data; + devid = (int)(uintptr_t)of_device_get_match_data(dev); maxleds = pca9532_chip_info_tbl[devid].num_leds; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -509,7 +504,6 @@ static int pca9532_probe(struct i2c_client *client, const struct i2c_device_id *id) { int devid; - const struct of_device_id *of_id; struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_platform_data *pca9532_pdata = dev_get_platdata(&client->dev); @@ -525,11 +519,7 @@ static int pca9532_probe(struct i2c_client *client, dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - of_id = of_match_device(of_pca9532_leds_match, - &client->dev); - if (unlikely(!of_id)) - return -EINVAL; - devid = (int)(uintptr_t) of_id->data; + devid = (int)(uintptr_t)of_device_get_match_data(&client->dev); } else { devid = id->driver_data; } -- cgit From 7c6082b903ac28dc3f383fba57c6f9e7e2594178 Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Wed, 16 Oct 2019 10:24:30 +0300 Subject: leds: mlxreg: Fix possible buffer overflow Error was detected by PVS-Studio: V512 A call of the 'sprintf' function will lead to overflow of the buffer 'led_data->led_cdev_name'. Acked-by: Jacek Anaszewski Acked-by: Pavel Machek Signed-off-by: Oleh Kravchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-mlxreg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index cabe379071a7..82aea1cd0c12 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -228,8 +228,8 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) brightness = LED_OFF; led_data->base_color = MLXREG_LED_GREEN_SOLID; } - sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg", - data->label); + snprintf(led_data->led_cdev_name, sizeof(led_data->led_cdev_name), + "mlxreg:%s", data->label); led_cdev->name = led_data->led_cdev_name; led_cdev->brightness = brightness; led_cdev->max_brightness = LED_ON; -- cgit From fc7b5028f2627133c7c18734715a08829eab4d1f Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 16 Oct 2019 20:54:03 +0800 Subject: leds: an30259a: add a check for devm_regmap_init_i2c an30259a_probe misses a check for devm_regmap_init_i2c and may cause problems. Add a check and print errors like other leds drivers. Signed-off-by: Chuhong Yuan Signed-off-by: Pavel Machek --- drivers/leds/leds-an30259a.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 250dc9d6f635..82350a28a564 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -305,6 +305,13 @@ static int an30259a_probe(struct i2c_client *client) chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + err); + goto exit; + } + for (i = 0; i < chip->num_leds; i++) { struct led_init_data init_data = {}; -- cgit From 5f820ed52371b4f5d8c43c93f03408d0dbc01e5b Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Fri, 25 Oct 2019 09:01:42 +0200 Subject: leds: trigger: netdev: fix handling on interface rename The NETDEV_CHANGENAME code is not "unneeded" like it is stated in commit 4cb6560514fa ("leds: trigger: netdev: fix refcnt leak on interface rename"). The event was accidentally misinterpreted equivalent to NETDEV_UNREGISTER, but should be equivalent to NETDEV_REGISTER. This was the case in the original code from the openwrt project. Otherwise, you are unable to set netdev led triggers for (non-existent) netdevices, which has to be renamed. This is the case, for example, for ppp interfaces in openwrt. Fixes: 06f502f57d0d ("leds: trigger: Introduce a NETDEV trigger") Fixes: 4cb6560514fa ("leds: trigger: netdev: fix refcnt leak on interface rename") Signed-off-by: Martin Schiller Signed-off-by: Pavel Machek --- drivers/leds/trigger/ledtrig-netdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 136f86a1627d..d5e774d83021 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -302,10 +302,12 @@ static int netdev_trig_notify(struct notifier_block *nb, container_of(nb, struct led_netdev_data, notifier); if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE - && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER) + && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER + && evt != NETDEV_CHANGENAME) return NOTIFY_DONE; if (!(dev == trigger_data->net_dev || + (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) || (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) return NOTIFY_DONE; @@ -315,6 +317,7 @@ static int netdev_trig_notify(struct notifier_block *nb, clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); switch (evt) { + case NETDEV_CHANGENAME: case NETDEV_REGISTER: if (trigger_data->net_dev) dev_put(trigger_data->net_dev); -- cgit