aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorLinus Torvalds2017-09-07 14:33:13 -0700
committerLinus Torvalds2017-09-07 14:33:13 -0700
commit5f9cc57036165bbd96f3a432fd2569c812503a92 (patch)
tree950466c2a3fa8d1723d51d766549f7cbb9897f1e /drivers/leds
parentcd7b34fe1c2d93c54b368c295de88612c0b7120b (diff)
parent1591caf2d5eafdfb3b300691f8f99e5bb97d5406 (diff)
Merge tag 'leds_for_4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds
Pull LED updates from Jacek Anaszewski: "LED class drivers improvements: leds-pca955x: - add Device Tree support and bindings - use devm_led_classdev_register() - add GPIO support - prevent crippled LED class device name - check for I2C errors leds-gpio: - add optional retain-state-shutdown DT property - allow LED to retain state at shutdown leds-tlc591xx: - merge conditional tests - add missing of_node_put leds-powernv: - delete an error message for a failed memory allocation in powernv_led_create() leds-is31fl32xx.c - convert to using custom %pOF printf format specifier Constify attribute_group structures in: - leds-blinkm - leds-lm3533 Make several arrays static const in: - leds-aat1290 - leds-lp5521 - leds-lp5562 - leds-lp8501" * tag 'leds_for_4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: leds: pca955x: check for I2C errors leds: gpio: Allow LED to retain state at shutdown dt-bindings: leds: gpio: Add optional retain-state-shutdown property leds: powernv: Delete an error message for a failed memory allocation in powernv_led_create() leds: lp8501: make several arrays static const leds: lp5562: make several arrays static const leds: lp5521: make several arrays static const leds: aat1290: make array max_mm_current_percent static const leds: pca955x: Prevent crippled LED device name leds: lm3533: constify attribute_group structure dt-bindings: leds: add pca955x leds: pca955x: add GPIO support leds: pca955x: use devm_led_classdev_register leds: pca955x: add device tree support leds: Convert to using %pOF instead of full_name leds: blinkm: constify attribute_group structures. leds: tlc591xx: add missing of_node_put leds: tlc591xx: merge conditional tests
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig11
-rw-r--r--drivers/leds/leds-aat1290.c6
-rw-r--r--drivers/leds/leds-blinkm.c2
-rw-r--r--drivers/leds/leds-gpio.c7
-rw-r--r--drivers/leds/leds-is31fl32xx.c4
-rw-r--r--drivers/leds/leds-lm3533.c2
-rw-r--r--drivers/leds/leds-lp5521.c8
-rw-r--r--drivers/leds/leds-lp5562.c10
-rw-r--r--drivers/leds/leds-lp8501.c6
-rw-r--r--drivers/leds/leds-pca955x.c350
-rw-r--r--drivers/leds/leds-powernv.c6
-rw-r--r--drivers/leds/leds-tlc591xx.c11
12 files changed, 329 insertions, 94 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 27dc8002b121..52ea34e337cd 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -386,6 +386,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-aat1290.c b/drivers/leds/leds-aat1290.c
index 424898e6c69d..43bd8a43f36c 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);
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,
};
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/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", &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;
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
};
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,
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,
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,
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 9a873118ea5f..905729191d3e 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -41,14 +41,19 @@
*/
#include <linux/acpi.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/string.h>
#include <linux/ctype.h>
-#include <linux/leds.h>
+#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <dt-bindings/leds/leds-pca955x.h>
/* LED select registers determine the source that drives LED outputs */
#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
@@ -115,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 {
@@ -122,6 +130,13 @@ struct pca955x_led {
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
char name[32];
+ u32 type;
+ const char *default_trigger;
+};
+
+struct pca955x_platform_data {
+ struct pca955x_led *leds;
+ int num_leds;
};
/* 8 bits per input register */
@@ -150,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;
}
/*
@@ -166,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,
@@ -208,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;
@@ -217,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:
@@ -237,18 +278,159 @@ 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 ret;
+}
+
+#ifdef CONFIG_LEDS_PCA955X_GPIO
+/*
+ * Read the INPUT register, which contains the state of LEDs.
+ */
+static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)
+{
+ 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)
+{
+ 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 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)
+ return pca955x_led_set(&led->led_cdev, LED_FULL);
+ else
+ 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 = 0;
+
+ /* There is nothing we can do about errors */
+ pca955x_read_input(pca955x->client, led->led_num / 8, &reg);
+
+ 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 */
+ return pca955x_set_value(gc, offset, 0);
+}
+
+static int pca955x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ return pca955x_set_value(gc, offset, val);
}
+#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)
+{
+ 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", &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);
+
+ 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);
+ }
+
+ 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 +439,9 @@ 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;
+ int ngpios = 0;
if (id) {
chip = &pca955x_chipdefs[id->driver_data];
@@ -272,6 +455,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 +476,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);
@@ -316,60 +502,92 @@ 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;
+
+ 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[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);
- /* Platform data can specify LED names and default triggers */
- if (pdata) {
- if (pdata->leds[i].name)
- 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;
- } 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;
+ 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 */
- for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++)
- pca955x_write_ls(client, i, 0x55);
+ /* Turn off LED */
+ 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);
-
- 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);
+ 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) {
+ 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;
}
@@ -378,9 +596,9 @@ 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,
.id_table = pca955x_id,
};
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;
diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
index 304531644938..f5357f6d9e58 100644
--- a/drivers/leds/leds-tlc591xx.c
+++ b/drivers/leds/leds-tlc591xx.c
@@ -230,12 +230,15 @@ tlc591xx_probe(struct i2c_client *client,
for_each_child_of_node(np, child) {
err = of_property_read_u32(child, "reg", &reg);
- if (err)
+ if (err) {
+ of_node_put(child);
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) {
+ of_node_put(child);
return -EINVAL;
+ }
priv->leds[reg].active = true;
priv->leds[reg].ldev.name =
of_get_property(child, "label", NULL) ? : child->name;