From 1055790b0df7202e363a55817688e3edbe1498a4 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 Jul 2017 11:58:18 +0200 Subject: [PATCH 01/18] leds: tlc591xx: merge conditional tests Merge conditionals that have the same then branch, to prepare for extending that branch with of_node_put. Signed-off-by: Julia Lawall Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-tlc591xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 304531644938..fcc87ee82453 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -232,9 +232,8 @@ tlc591xx_probe(struct i2c_client *client, err = of_property_read_u32(child, "reg", ®); if (err) return err; - if (reg < 0 || reg >= tlc591xx->max_leds) - return -EINVAL; - if (priv->leds[reg].active) + if (reg < 0 || reg >= tlc591xx->max_leds || + priv->leds[reg].active) return -EINVAL; priv->leds[reg].active = true; priv->leds[reg].ldev.name = From c68729119f4d2993bec3c9cb999ad76de5aeddba Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 Jul 2017 11:58:19 +0200 Subject: [PATCH 02/18] leds: tlc591xx: add missing of_node_put for_each_child_of_node performs an of_node_get on each iteration, so a return from the loop requires an of_node_put. The semantic patch that fixes this problem is as follows (http://coccinelle.lip6.fr): // @@ local idexpression n; expression e,e1; iterator name for_each_child_of_node; @@ for_each_child_of_node(e1,n) { ... ( of_node_put(n); | e = n | return n; | + of_node_put(n); ? return ...; ) ... } // Signed-off-by: Julia Lawall Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-tlc591xx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index fcc87ee82453..f5357f6d9e58 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -230,11 +230,15 @@ tlc591xx_probe(struct i2c_client *client, for_each_child_of_node(np, child) { err = of_property_read_u32(child, "reg", ®); - if (err) + if (err) { + of_node_put(child); return err; + } if (reg < 0 || reg >= tlc591xx->max_leds || - priv->leds[reg].active) + priv->leds[reg].active) { + of_node_put(child); return -EINVAL; + } priv->leds[reg].active = true; priv->leds[reg].ldev.name = of_get_property(child, "label", NULL) ? : child->name; From 7358df4d5b01d9b5cccad6149b63804e8cdcf1dc Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Fri, 4 Aug 2017 11:55:52 +0530 Subject: [PATCH 03/18] leds: blinkm: constify attribute_group structures. attribute_group are not supposed to change at runtime. All functions working with attribute_group provided by work with const attribute_group. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-blinkm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 617fe975bf6e..d03ed6b4176b 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -298,7 +298,7 @@ static struct attribute *blinkm_attrs[] = { NULL, }; -static struct attribute_group blinkm_group = { +static const struct attribute_group blinkm_group = { .name = "blinkm", .attrs = blinkm_attrs, }; From 0571753e65b5bf7fe9a50004e9e1367cc4e51d0a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2017 16:43:11 -0500 Subject: [PATCH 04/18] leds: Convert to using %pOF instead of full_name Now that we have a custom printf format specifier, convert users of full_name to use %pOF instead. This is preparation to remove storing of the full path string for each node. Signed-off-by: Rob Herring Cc: Richard Purdie Cc: linux-leds@vger.kernel.org Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-is31fl32xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index 478844c5cead..31a9d749c8be 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -348,8 +348,8 @@ static int is31fl32xx_parse_child_dt(const struct device *dev, ret = of_property_read_u32(child, "reg", ®); if (ret || reg < 1 || reg > led_data->priv->cdef->channels) { dev_err(dev, - "Child node %s does not have a valid reg property\n", - child->full_name); + "Child node %pOF does not have a valid reg property\n", + child); return -EINVAL; } led_data->channel = reg; From ed1f4b9676a8eb9c38cf44cdc06300604bfbb43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 8 Aug 2017 15:42:37 +0200 Subject: [PATCH 05/18] leds: pca955x: add device tree support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used in a following patch to define different operation modes for each pin. Signed-off-by: Cédric Le Goater Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca955x.c | 101 +++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 12 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 9a873118ea5f..2d34009d00e6 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -41,14 +41,17 @@ */ #include -#include -#include -#include #include -#include +#include #include +#include #include +#include +#include +#include +#include #include +#include /* LED select registers determine the source that drives LED outputs */ #define PCA955X_LS_LED_ON 0x0 /* Output LOW */ @@ -122,6 +125,12 @@ struct pca955x_led { struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ char name[32]; + const char *default_trigger; +}; + +struct pca955x_platform_data { + struct pca955x_led *leds; + int num_leds; }; /* 8 bits per input register */ @@ -250,6 +259,70 @@ static int pca955x_led_set(struct led_classdev *led_cdev, return 0; } +#if IS_ENABLED(CONFIG_OF) +static struct pca955x_platform_data * +pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) +{ + struct device_node *np = client->dev.of_node; + struct device_node *child; + struct pca955x_platform_data *pdata; + int count; + + count = of_get_child_count(np); + if (!count || count > chip->bits) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->leds = devm_kzalloc(&client->dev, + sizeof(struct pca955x_led) * chip->bits, + GFP_KERNEL); + if (!pdata->leds) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(np, child) { + const char *name; + u32 reg; + int res; + + res = of_property_read_u32(child, "reg", ®); + if ((res != 0) || (reg >= chip->bits)) + continue; + + if (of_property_read_string(child, "label", &name)) + name = child->name; + + snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), + "%s", name); + + of_property_read_string(child, "linux,default-trigger", + &pdata->leds[reg].default_trigger); + } + + pdata->num_leds = chip->bits; + + return pdata; +} + +static const struct of_device_id of_pca955x_match[] = { + { .compatible = "nxp,pca9550", .data = (void *)pca9550 }, + { .compatible = "nxp,pca9551", .data = (void *)pca9551 }, + { .compatible = "nxp,pca9552", .data = (void *)pca9552 }, + { .compatible = "nxp,pca9553", .data = (void *)pca9553 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_pca955x_match); +#else +static struct pca955x_platform_data * +pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) +{ + return ERR_PTR(-ENODEV); +} +#endif + static int pca955x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -257,8 +330,8 @@ static int pca955x_probe(struct i2c_client *client, struct pca955x_led *pca955x_led; struct pca955x_chipdef *chip; struct i2c_adapter *adapter; - struct led_platform_data *pdata; int i, err; + struct pca955x_platform_data *pdata; if (id) { chip = &pca955x_chipdefs[id->driver_data]; @@ -272,6 +345,11 @@ static int pca955x_probe(struct i2c_client *client, } adapter = to_i2c_adapter(client->dev.parent); pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = pca955x_pdata_of_init(client, chip); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } /* Make sure the slave address / chip type combo given is possible */ if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != @@ -288,13 +366,11 @@ static int pca955x_probe(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 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; - } + 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; } pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL); @@ -378,6 +454,7 @@ static struct i2c_driver pca955x_driver = { .driver = { .name = "leds-pca955x", .acpi_match_table = ACPI_PTR(pca955x_acpi_ids), + .of_match_table = of_match_ptr(of_pca955x_match), }, .probe = pca955x_probe, .remove = pca955x_remove, From 91940bb4ca7b7f1b5426cc14bdbd0c7f8347683f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 8 Aug 2017 15:42:38 +0200 Subject: [PATCH 06/18] leds: pca955x: use devm_led_classdev_register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us remove the loop doing the cleanup in case of failure and also the remove handler of the i2c_driver. Signed-off-by: Cédric Le Goater Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca955x.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 2d34009d00e6..5a5d3765cfd3 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -410,10 +410,10 @@ static int pca955x_probe(struct i2c_client *client, pca955x_led->led_cdev.name = pca955x_led->name; pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set; - err = led_classdev_register(&client->dev, - &pca955x_led->led_cdev); - if (err < 0) - goto exit; + err = devm_led_classdev_register(&client->dev, + &pca955x_led->led_cdev); + if (err) + return err; } /* Turn off LEDs */ @@ -431,23 +431,6 @@ static int pca955x_probe(struct i2c_client *client, pca955x_write_psc(client, 1, 0); return 0; - -exit: - while (i--) - led_classdev_unregister(&pca955x->leds[i].led_cdev); - - return err; -} - -static int pca955x_remove(struct i2c_client *client) -{ - struct pca955x *pca955x = i2c_get_clientdata(client); - int i; - - for (i = 0; i < pca955x->chipdef->bits; i++) - led_classdev_unregister(&pca955x->leds[i].led_cdev); - - return 0; } static struct i2c_driver pca955x_driver = { @@ -457,7 +440,6 @@ static struct i2c_driver pca955x_driver = { .of_match_table = of_match_ptr(of_pca955x_match), }, .probe = pca955x_probe, - .remove = pca955x_remove, .id_table = pca955x_id, }; From 561099a1a2e992a482a8318c0c9c5af26222e5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 8 Aug 2017 15:42:39 +0200 Subject: [PATCH 07/18] leds: pca955x: add GPIO support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA955x family of chips are I2C LED blinkers whose pins not used to control LEDs can be used as general purpose I/Os (GPIOs). The following adds such a support by defining different operation modes for the pins (See bindings documentation for more details). The pca955x driver is then extended with a gpio_chip when some of pins are operating as GPIOs. The default operating mode is to behave as a LED. The GPIO support is conditioned by CONFIG_LEDS_PCA955X_GPIO. Signed-off-by: Cédric Le Goater Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 11 ++ drivers/leds/leds-pca955x.c | 137 +++++++++++++++++++++--- include/dt-bindings/leds/leds-pca955x.h | 16 +++ 3 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 include/dt-bindings/leds/leds-pca955x.h diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 594b24d410c3..3013cd35c65e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -377,6 +377,17 @@ config LEDS_PCA955X LED driver chips accessed via the I2C bus. Supported devices include PCA9550, PCA9551, PCA9552, and PCA9553. +config LEDS_PCA955X_GPIO + bool "Enable GPIO support for PCA955X" + depends on LEDS_PCA955X + depends on GPIOLIB + help + Allow unused pins on PCA955X to be used as gpio. + + To use a pin as gpio the pin type should be set to + PCA955X_TYPE_GPIO in the device tree. + + config LEDS_PCA963X tristate "LED support for PCA963x I2C chip" depends on LEDS_CLASS diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 5a5d3765cfd3..f062d1e7640f 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -53,6 +53,8 @@ #include #include +#include + /* LED select registers determine the source that drives LED outputs */ #define PCA955X_LS_LED_ON 0x0 /* Output LOW */ #define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ @@ -118,6 +120,9 @@ struct pca955x { struct pca955x_led *leds; struct pca955x_chipdef *chipdef; struct i2c_client *client; +#ifdef CONFIG_LEDS_PCA955X_GPIO + struct gpio_chip gpio; +#endif }; struct pca955x_led { @@ -125,6 +130,7 @@ struct pca955x_led { struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ char name[32]; + u32 type; const char *default_trigger; }; @@ -259,6 +265,65 @@ static int pca955x_led_set(struct led_classdev *led_cdev, return 0; } +#ifdef CONFIG_LEDS_PCA955X_GPIO +/* + * Read the INPUT register, which contains the state of LEDs. + */ +static u8 pca955x_read_input(struct i2c_client *client, int n) +{ + return (u8)i2c_smbus_read_byte_data(client, n); +} + +static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset) +{ + struct pca955x *pca955x = gpiochip_get_data(gc); + struct pca955x_led *led = &pca955x->leds[offset]; + + if (led->type == PCA955X_TYPE_GPIO) + return 0; + + return -EBUSY; +} + +static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) +{ + struct pca955x *pca955x = gpiochip_get_data(gc); + struct pca955x_led *led = &pca955x->leds[offset]; + + if (val) + pca955x_led_set(&led->led_cdev, LED_FULL); + else + pca955x_led_set(&led->led_cdev, LED_OFF); +} + +static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ + struct pca955x *pca955x = gpiochip_get_data(gc); + struct pca955x_led *led = &pca955x->leds[offset]; + u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8); + + return !!(reg & (1 << (led->led_num % 8))); +} + +static int pca955x_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + /* To use as input ensure pin is not driven */ + pca955x_gpio_set_value(gc, offset, 0); + + return 0; +} + +static int pca955x_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int val) +{ + pca955x_gpio_set_value(gc, offset, val); + + return 0; +} +#endif /* CONFIG_LEDS_PCA955X_GPIO */ + #if IS_ENABLED(CONFIG_OF) static struct pca955x_platform_data * pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) @@ -297,6 +362,8 @@ pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), "%s", name); + pdata->leds[reg].type = PCA955X_TYPE_LED; + of_property_read_u32(child, "type", &pdata->leds[reg].type); of_property_read_string(child, "linux,default-trigger", &pdata->leds[reg].default_trigger); } @@ -332,6 +399,7 @@ static int pca955x_probe(struct i2c_client *client, struct i2c_adapter *adapter; int i, err; struct pca955x_platform_data *pdata; + int ngpios = 0; if (id) { chip = &pca955x_chipdefs[id->driver_data]; @@ -392,9 +460,19 @@ static int pca955x_probe(struct i2c_client *client, pca955x_led = &pca955x->leds[i]; pca955x_led->led_num = i; pca955x_led->pca955x = pca955x; + pca955x_led->type = pdata->leds[i].type; - /* Platform data can specify LED names and default triggers */ - if (pdata) { + switch (pca955x_led->type) { + case PCA955X_TYPE_NONE: + break; + case PCA955X_TYPE_GPIO: + ngpios++; + break; + case PCA955X_TYPE_LED: + /* + * Platform data can specify LED names and + * default triggers + */ if (pdata->leds[i].name) snprintf(pca955x_led->name, sizeof(pca955x_led->name), "pca955x:%s", @@ -402,24 +480,21 @@ static int pca955x_probe(struct i2c_client *client, if (pdata->leds[i].default_trigger) pca955x_led->led_cdev.default_trigger = pdata->leds[i].default_trigger; - } else { - snprintf(pca955x_led->name, sizeof(pca955x_led->name), - "pca955x:%d", i); + + pca955x_led->led_cdev.name = pca955x_led->name; + pca955x_led->led_cdev.brightness_set_blocking = + pca955x_led_set; + + err = devm_led_classdev_register(&client->dev, + &pca955x_led->led_cdev); + if (err) + return err; + + /* Turn off LED */ + pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); } - - pca955x_led->led_cdev.name = pca955x_led->name; - pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set; - - err = devm_led_classdev_register(&client->dev, - &pca955x_led->led_cdev); - if (err) - return err; } - /* Turn off LEDs */ - for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) - pca955x_write_ls(client, i, 0x55); - /* PWM0 is used for half brightness or 50% duty cycle */ pca955x_write_pwm(client, 0, 255-LED_HALF); @@ -430,6 +505,34 @@ static int pca955x_probe(struct i2c_client *client, pca955x_write_psc(client, 0, 0); pca955x_write_psc(client, 1, 0); +#ifdef CONFIG_LEDS_PCA955X_GPIO + if (ngpios) { + pca955x->gpio.label = "gpio-pca955x"; + pca955x->gpio.direction_input = pca955x_gpio_direction_input; + pca955x->gpio.direction_output = pca955x_gpio_direction_output; + pca955x->gpio.set = pca955x_gpio_set_value; + pca955x->gpio.get = pca955x_gpio_get_value; + pca955x->gpio.request = pca955x_gpio_request_pin; + pca955x->gpio.can_sleep = 1; + pca955x->gpio.base = -1; + pca955x->gpio.ngpio = ngpios; + pca955x->gpio.parent = &client->dev; + pca955x->gpio.owner = THIS_MODULE; + + err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio, + pca955x); + if (err) { + /* Use data->gpio.dev as a flag for freeing gpiochip */ + pca955x->gpio.parent = NULL; + dev_warn(&client->dev, "could not add gpiochip\n"); + return err; + } + dev_info(&client->dev, "gpios %i...%i\n", + pca955x->gpio.base, pca955x->gpio.base + + pca955x->gpio.ngpio - 1); + } +#endif + return 0; } diff --git a/include/dt-bindings/leds/leds-pca955x.h b/include/dt-bindings/leds/leds-pca955x.h new file mode 100644 index 000000000000..78cb7e979de7 --- /dev/null +++ b/include/dt-bindings/leds/leds-pca955x.h @@ -0,0 +1,16 @@ +/* + * This header provides constants for pca955x LED bindings. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _DT_BINDINGS_LEDS_PCA955X_H +#define _DT_BINDINGS_LEDS_PCA955X_H + +#define PCA955X_TYPE_NONE 0 +#define PCA955X_TYPE_LED 1 +#define PCA955X_TYPE_GPIO 2 + +#endif /* _DT_BINDINGS_LEDS_PCA955X_H */ From 15201b5de9d62fd6ece51ba9005c441d588fcb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 8 Aug 2017 15:42:40 +0200 Subject: [PATCH 08/18] dt-bindings: leds: add pca955x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the devicetree bindings for the PCA955x I2C LED blinkers. Signed-off-by: Cédric Le Goater Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- .../devicetree/bindings/leds/leds-pca955x.txt | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/leds-pca955x.txt diff --git a/Documentation/devicetree/bindings/leds/leds-pca955x.txt b/Documentation/devicetree/bindings/leds/leds-pca955x.txt new file mode 100644 index 000000000000..7984efb767b4 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-pca955x.txt @@ -0,0 +1,88 @@ +* NXP - pca955x LED driver + +The PCA955x family of chips are I2C LED blinkers whose pins not used +to control LEDs can be used as general purpose I/Os. The GPIO pins can +be input or output, and output pins can also be pulse-width controlled. + +Required properties: +- compatible : should be one of : + "nxp,pca9550" + "nxp,pca9551" + "nxp,pca9552" + "nxp,pca9553" +- #address-cells: must be 1 +- #size-cells: must be 0 +- reg: I2C slave address. depends on the model. + +Optional properties: +- gpio-controller: allows pins to be used as GPIOs. +- #gpio-cells: must be 2. +- gpio-line-names: define the names of the GPIO lines + +LED sub-node properties: +- reg : number of LED line. + from 0 to 1 for the pca9550 + from 0 to 7 for the pca9551 + from 0 to 15 for the pca9552 + from 0 to 3 for the pca9553 +- type: (optional) either + PCA9532_TYPE_NONE + PCA9532_TYPE_LED + PCA9532_TYPE_GPIO + see dt-bindings/leds/leds-pca955x.h (default to LED) +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt + +Examples: + +pca9552: pca9552@60 { + compatible = "nxp,pca9552"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x60>; + + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "GPIO12", "GPIO13", "GPIO14", "GPIO15"; + + gpio@12 { + reg = <12>; + type = ; + }; + gpio@13 { + reg = <13>; + type = ; + }; + gpio@14 { + reg = <14>; + type = ; + }; + gpio@15 { + reg = <15>; + type = ; + }; + + led@0 { + label = "red:power"; + linux,default-trigger = "default-on"; + reg = <0>; + type = ; + }; + led@1 { + label = "green:power"; + reg = <1>; + type = ; + }; + led@2 { + label = "pca9552:yellow"; + reg = <2>; + type = ; + }; + led@3 { + label = "pca9552:white"; + reg = <3>; + type = ; + }; +}; From 430e48ecf31f4f897047f22e02abdfa75730cad8 Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Thu, 10 Aug 2017 12:28:09 -0400 Subject: [PATCH 09/18] leds: lm3533: constify attribute_group structure Functions working with attribute_groups provided by work with const attribute_group. These attribute_group structures do not change at runtime so mark them as const. File size before: text data bss dec hex filename 8272 4608 64 12944 3290 drivers/leds/leds-lm3533.o File size after: text data bss dec hex filename 8368 4512 64 12944 3290 drivers/leds/leds-lm3533.o This change was made with the help of Coccinelle. Signed-off-by: Amitoj Kaur Chawla Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-lm3533.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 5b529dc013d2..72224b599ffc 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -626,7 +626,7 @@ static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, return mode; }; -static struct attribute_group lm3533_led_attribute_group = { +static const struct attribute_group lm3533_led_attribute_group = { .is_visible = lm3533_led_attr_is_visible, .attrs = lm3533_led_attributes }; From 390c97dc6e346b65df61c18fade67577896382fd Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 17 Aug 2017 22:16:48 +0200 Subject: [PATCH 10/18] leds: pca955x: Prevent crippled LED device name In case platform data provided empty LED name string the resulting LED class device name would be crippled. Use corresponding LED chip bit in place of "function" segment of LED class device name then to make the LEDs at least distinguishable. Signed-off-by: Jacek Anaszewski Reported-by: Colin King Reported-by: Dan Carpenter Suggested-by: Nate Case --- drivers/leds/leds-pca955x.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index f062d1e7640f..09303fd1fdc6 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -473,10 +473,14 @@ static int pca955x_probe(struct i2c_client *client, * Platform data can specify LED names and * default triggers */ - if (pdata->leds[i].name) - snprintf(pca955x_led->name, - sizeof(pca955x_led->name), "pca955x:%s", - pdata->leds[i].name); + if (pdata->leds[i].name[0] == '\0') + snprintf(pdata->leds[i].name, + sizeof(pdata->leds[i].name), "%d", i); + + snprintf(pca955x_led->name, + sizeof(pca955x_led->name), "pca955x:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) pca955x_led->led_cdev.default_trigger = pdata->leds[i].default_trigger; From 17224244bbbe2ac91bc2645b5198f742a63e1d82 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Aug 2017 14:00:52 +0100 Subject: [PATCH 11/18] leds: aat1290: make array max_mm_current_percent static const Don't populate the array max_mm_current_percent on the stack, instead make it static const. Makes the object code smaller by over 280 bytes: Before: text data bss dec hex filename 7225 1936 64 9225 2409 ./drivers/leds/leds-aat1290.o After: text data bss dec hex filename 6847 2032 64 8943 22ef ./drivers/leds/leds-aat1290.o` Signed-off-by: Colin Ian King Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-aat1290.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index a21e19297745..35fcb0ca9092 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c @@ -314,8 +314,10 @@ static void aat1290_led_validate_mm_current(struct aat1290_led *led, static int init_mm_current_scale(struct aat1290_led *led, struct aat1290_led_config_data *cfg) { - int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, - 63, 71, 79, 89, 100 }; + static const int max_mm_current_percent[] = { + 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, + 63, 71, 79, 89, 100 + }; int i, max_mm_current = AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); From f01a59ef7ada971698972ec6ecdf4719119bb0ac Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Aug 2017 14:00:53 +0100 Subject: [PATCH 12/18] leds: lp5521: make several arrays static const Don't populate the arrays on the stack, instead make them static const. Makes the object code smaller by over 120 bytes: Before: text data bss dec hex filename 8999 4176 64 13239 33b7 drivers/leds/leds-lp5521.o After: text data bss dec hex filename 8554 4496 64 13114 333a drivers/leds/leds-lp5521.o Signed-off-by: Colin Ian King Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-lp5521.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f53c8cda1bde..55c0517fbe03 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -134,13 +134,13 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; - u8 mask[] = { + static const u8 mask[] = { [LP55XX_ENGINE_1] = LP5521_MODE_R_M, [LP55XX_ENGINE_2] = LP5521_MODE_G_M, [LP55XX_ENGINE_3] = LP5521_MODE_B_M, }; - u8 val[] = { + static const u8 val[] = { [LP55XX_ENGINE_1] = LP5521_LOAD_R, [LP55XX_ENGINE_2] = LP5521_LOAD_G, [LP55XX_ENGINE_3] = LP5521_LOAD_B, @@ -160,7 +160,7 @@ static void lp5521_stop_all_engines(struct lp55xx_chip *chip) static void lp5521_stop_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; - u8 mask[] = { + static const u8 mask[] = { [LP55XX_ENGINE_1] = LP5521_MODE_R_M, [LP55XX_ENGINE_2] = LP5521_MODE_G_M, [LP55XX_ENGINE_3] = LP5521_MODE_B_M, @@ -226,7 +226,7 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip, { enum lp55xx_engine_index idx = chip->engine_idx; u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; - u8 addr[] = { + static const u8 addr[] = { [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, From 85775013ec49b3d114adfbd618d721247bf1b431 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Aug 2017 14:00:54 +0100 Subject: [PATCH 13/18] leds: lp5562: make several arrays static const Don't populate the arrays on the stack, instead make them static const. Makes the object code smaller by over 150 bytes: Before: text data bss dec hex filename 7725 2448 64 10237 27fd drivers/leds/leds-lp5562.o After: text data bss dec hex filename 7184 2832 64 10080 2760 drivers/leds/leds-lp5562.o Signed-off-by: Colin Ian King Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-lp5562.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 90892585bcb5..05ffa34fb6ad 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -116,7 +116,7 @@ static inline void lp5562_wait_enable_done(void) static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current) { - u8 addr[] = { + static const u8 addr[] = { LP5562_REG_R_CURRENT, LP5562_REG_G_CURRENT, LP5562_REG_B_CURRENT, @@ -130,13 +130,13 @@ static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current) static void lp5562_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; - u8 mask[] = { + static const u8 mask[] = { [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M, [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M, [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M, }; - u8 val[] = { + static const u8 val[] = { [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1, [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2, [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3, @@ -211,7 +211,7 @@ static int lp5562_update_firmware(struct lp55xx_chip *chip, { enum lp55xx_engine_index idx = chip->engine_idx; u8 pattern[LP5562_PROGRAM_LENGTH] = {0}; - u8 addr[] = { + static const u8 addr[] = { [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1, [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2, [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3, @@ -314,7 +314,7 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip) static int lp5562_led_brightness(struct lp55xx_led *led) { struct lp55xx_chip *chip = led->chip; - u8 addr[] = { + static const u8 addr[] = { LP5562_REG_R_PWM, LP5562_REG_G_PWM, LP5562_REG_B_PWM, From 34b558a5ed21ff97186265d0d753a3425744d7eb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Aug 2017 14:00:55 +0100 Subject: [PATCH 14/18] leds: lp8501: make several arrays static const Don't populate the arrays on the stack, instead make them static const. Makes the object code smaller by 50 bytes: Before: text data bss dec hex filename 5058 1552 64 6674 1a12 drivers/leds/leds-lp8501.o After: text data bss dec hex filename 4788 1776 64 6628 19e4 drivers/leds/leds-lp8501.o Signed-off-by: Colin Ian King Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-lp8501.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index 3f9675bd214a..3adb113cf02e 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -118,19 +118,19 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip) static void lp8501_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; - u8 mask[] = { + static const u8 mask[] = { [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, }; - u8 val[] = { + static const u8 val[] = { [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, }; - u8 page_sel[] = { + static const u8 page_sel[] = { [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, From 30f21ef22b7b7e1156cc2a7f9e714c002938f056 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 27 Aug 2017 22:00:22 +0200 Subject: [PATCH 15/18] leds: powernv: Delete an error message for a failed memory allocation in powernv_led_create() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-powernv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index b2a98c7b521b..b1adbd70ce2e 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -224,12 +224,8 @@ static int powernv_led_create(struct device *dev, powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", powernv_led->loc_code, led_type_desc); - if (!powernv_led->cdev.name) { - dev_err(dev, - "%s: Memory allocation failed for classdev name\n", - __func__); + if (!powernv_led->cdev.name) return -ENOMEM; - } powernv_led->cdev.brightness_set_blocking = powernv_brightness_set; powernv_led->cdev.brightness_get = powernv_brightness_get; From 0f34f958768c48037ce19cdc5d863a7316a86d44 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 28 Aug 2017 09:47:10 +0930 Subject: [PATCH 16/18] dt-bindings: leds: gpio: Add optional retain-state-shutdown property On Baseboard Management Controller (BMC) systems it's sometimes necessary for a LED to retain its state across a BMC reset (which is independent of the host system state). Add a devicetree property to describe this behaviour. The property would typically be used in conjunction with 'default-state = "keep"'. Signed-off-by: Andrew Jeffery Acked-by: Pavel Machek Tested-by: Brandon Wyman Signed-off-by: Jacek Anaszewski --- Documentation/devicetree/bindings/leds/leds-gpio.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt index 76535ca37120..a48dda268f81 100644 --- a/Documentation/devicetree/bindings/leds/leds-gpio.txt +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -18,6 +18,9 @@ LED sub-node properties: see Documentation/devicetree/bindings/leds/common.txt - retain-state-suspended: (optional) The suspend state can be retained.Such as charge-led gpio. +- retain-state-shutdown: (optional) Retain the state of the LED on shutdown. + Useful in BMC systems, for example when the BMC is rebooted while the host + remains up. - panic-indicator : (optional) see Documentation/devicetree/bindings/leds/common.txt From f5808ac158f2b16b686a3d3c0879c5d6048aba14 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 28 Aug 2017 09:47:11 +0930 Subject: [PATCH 17/18] leds: gpio: Allow LED to retain state at shutdown In some systems, such as Baseboard Management Controllers (BMCs), we want to retain the state of LEDs across a reboot of the BMC (whilst the host remains up). Implement support for the retain-state-shutdown devicetree property in leds-gpio. Signed-off-by: Andrew Jeffery Acked-by: Pavel Machek Tested-by: Brandon Wyman Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-gpio.c | 7 ++++++- include/linux/leds.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index e753ba93ba1e..764c31301f90 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -134,6 +134,8 @@ static int create_gpio_led(const struct gpio_led *template, led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; if (template->panic_indicator) led_dat->cdev.flags |= LED_PANIC_INDICATOR; + if (template->retain_state_shutdown) + led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN; ret = gpiod_direction_output(led_dat->gpiod, state); if (ret < 0) @@ -205,6 +207,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) if (fwnode_property_present(child, "retain-state-suspended")) led.retain_state_suspended = 1; + if (fwnode_property_present(child, "retain-state-shutdown")) + led.retain_state_shutdown = 1; if (fwnode_property_present(child, "panic-indicator")) led.panic_indicator = 1; @@ -267,7 +271,8 @@ static void gpio_led_shutdown(struct platform_device *pdev) for (i = 0; i < priv->num_leds; i++) { struct gpio_led_data *led = &priv->leds[i]; - gpio_led_set(&led->cdev, LED_OFF); + if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN)) + gpio_led_set(&led->cdev, LED_OFF); } } diff --git a/include/linux/leds.h b/include/linux/leds.h index 64c56d454f7d..bf6db4fe895b 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -49,6 +49,7 @@ struct led_classdev { #define LED_HW_PLUGGABLE (1 << 19) #define LED_PANIC_INDICATOR (1 << 20) #define LED_BRIGHT_HW_CHANGED (1 << 21) +#define LED_RETAIN_AT_SHUTDOWN (1 << 22) /* set_brightness_work / blink_timer flags, atomic, private. */ unsigned long work_flags; @@ -392,6 +393,7 @@ struct gpio_led { unsigned retain_state_suspended : 1; unsigned panic_indicator : 1; unsigned default_state : 2; + unsigned retain_state_shutdown : 1; /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ struct gpio_desc *gpiod; }; From 1591caf2d5eafdfb3b300691f8f99e5bb97d5406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 30 Aug 2017 13:25:51 +0200 Subject: [PATCH 18/18] leds: pca955x: check for I2C errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should also allow probing to fail when a pca955x chip is not found on a I2C bus. Signed-off-by: Cédric Le Goater Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca955x.c | 114 ++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 31 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 09303fd1fdc6..905729191d3e 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -165,13 +165,18 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) * Write to frequency prescaler register, used to program the * period of the PWM output. period = (PSCx + 1) / 38 */ -static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) +static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + int ret; - i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, val); + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, n, val, ret); + return ret; } /* @@ -181,38 +186,56 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) * * Duty cycle is (256 - PWMx) / 256 */ -static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) +static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + int ret; - i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, val); + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, n, val, ret); + return ret; } /* * Write to LED selector register, which determines the source that * drives the LED output. */ -static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) +static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + int ret; - i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, val); + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, n, val, ret); + return ret; } /* * Read the LED selector register, which determines the source that * drives the LED output. */ -static u8 pca955x_read_ls(struct i2c_client *client, int n) +static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) { struct pca955x *pca955x = i2c_get_clientdata(client); + int ret; - return (u8) i2c_smbus_read_byte_data(client, + ret = i2c_smbus_read_byte_data(client, pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); + if (ret < 0) { + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, n, ret); + return ret; + } + *val = (u8)ret; + return 0; } static int pca955x_led_set(struct led_classdev *led_cdev, @@ -223,6 +246,7 @@ static int pca955x_led_set(struct led_classdev *led_cdev, u8 ls; int chip_ls; /* which LSx to use (0-3 potentially) */ int ls_led; /* which set of bits within LSx to use (0-3) */ + int ret; pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev); pca955x = pca955x_led->pca955x; @@ -232,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev, mutex_lock(&pca955x->lock); - ls = pca955x_read_ls(pca955x->client, chip_ls); + ret = pca955x_read_ls(pca955x->client, chip_ls, &ls); + if (ret) + goto out; switch (value) { case LED_FULL: @@ -252,26 +278,37 @@ static int pca955x_led_set(struct led_classdev *led_cdev, * OFF, HALF, or FULL. But, this is probably better than * just turning off for all other values. */ - pca955x_write_pwm(pca955x->client, 1, - 255 - value); + ret = pca955x_write_pwm(pca955x->client, 1, 255 - value); + if (ret) + goto out; ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); break; } - pca955x_write_ls(pca955x->client, chip_ls, ls); + ret = pca955x_write_ls(pca955x->client, chip_ls, ls); +out: mutex_unlock(&pca955x->lock); - return 0; + return ret; } #ifdef CONFIG_LEDS_PCA955X_GPIO /* * Read the INPUT register, which contains the state of LEDs. */ -static u8 pca955x_read_input(struct i2c_client *client, int n) +static int pca955x_read_input(struct i2c_client *client, int n, u8 *val) { - return (u8)i2c_smbus_read_byte_data(client, n); + int ret = i2c_smbus_read_byte_data(client, n); + + if (ret < 0) { + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, n, ret); + return ret; + } + *val = (u8)ret; + return 0; + } static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset) @@ -285,23 +322,32 @@ static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset) return -EBUSY; } -static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset, - int val) +static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset, + int val) { struct pca955x *pca955x = gpiochip_get_data(gc); struct pca955x_led *led = &pca955x->leds[offset]; if (val) - pca955x_led_set(&led->led_cdev, LED_FULL); + return pca955x_led_set(&led->led_cdev, LED_FULL); else - pca955x_led_set(&led->led_cdev, LED_OFF); + return pca955x_led_set(&led->led_cdev, LED_OFF); +} + +static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) +{ + pca955x_set_value(gc, offset, val); } static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset) { struct pca955x *pca955x = gpiochip_get_data(gc); struct pca955x_led *led = &pca955x->leds[offset]; - u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8); + u8 reg = 0; + + /* There is nothing we can do about errors */ + pca955x_read_input(pca955x->client, led->led_num / 8, ®); return !!(reg & (1 << (led->led_num % 8))); } @@ -310,17 +356,13 @@ static int pca955x_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) { /* To use as input ensure pin is not driven */ - pca955x_gpio_set_value(gc, offset, 0); - - return 0; + return pca955x_set_value(gc, offset, 0); } static int pca955x_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int val) { - pca955x_gpio_set_value(gc, offset, val); - - return 0; + return pca955x_set_value(gc, offset, val); } #endif /* CONFIG_LEDS_PCA955X_GPIO */ @@ -495,19 +537,29 @@ static int pca955x_probe(struct i2c_client *client, return err; /* Turn off LED */ - pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); + err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); + if (err) + return err; } } /* PWM0 is used for half brightness or 50% duty cycle */ - pca955x_write_pwm(client, 0, 255-LED_HALF); + err = pca955x_write_pwm(client, 0, 255 - LED_HALF); + if (err) + return err; /* PWM1 is used for variable brightness, default to OFF */ - pca955x_write_pwm(client, 1, 0); + err = pca955x_write_pwm(client, 1, 0); + if (err) + return err; /* Set to fast frequency so we do not see flashing */ - pca955x_write_psc(client, 0, 0); - pca955x_write_psc(client, 1, 0); + err = pca955x_write_psc(client, 0, 0); + if (err) + return err; + err = pca955x_write_psc(client, 1, 0); + if (err) + return err; #ifdef CONFIG_LEDS_PCA955X_GPIO if (ngpios) {