Merge tag 'leds_for_4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszews...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 21:33:13 +0000 (14:33 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 21:33:13 +0000 (14:33 -0700)
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

16 files changed:
Documentation/devicetree/bindings/leds/leds-gpio.txt
Documentation/devicetree/bindings/leds/leds-pca955x.txt [new file with mode: 0644]
drivers/leds/Kconfig
drivers/leds/leds-aat1290.c
drivers/leds/leds-blinkm.c
drivers/leds/leds-gpio.c
drivers/leds/leds-is31fl32xx.c
drivers/leds/leds-lm3533.c
drivers/leds/leds-lp5521.c
drivers/leds/leds-lp5562.c
drivers/leds/leds-lp8501.c
drivers/leds/leds-pca955x.c
drivers/leds/leds-powernv.c
drivers/leds/leds-tlc591xx.c
include/dt-bindings/leds/leds-pca955x.h [new file with mode: 0644]
include/linux/leds.h

index 76535ca..a48dda2 100644 (file)
@@ -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
 
diff --git a/Documentation/devicetree/bindings/leds/leds-pca955x.txt b/Documentation/devicetree/bindings/leds/leds-pca955x.txt
new file mode 100644 (file)
index 0000000..7984efb
--- /dev/null
@@ -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 = <PCA955X_TYPE_GPIO>;
+       };
+       gpio@13 {
+               reg = <13>;
+               type = <PCA955X_TYPE_GPIO>;
+       };
+       gpio@14 {
+               reg = <14>;
+               type = <PCA955X_TYPE_GPIO>;
+       };
+       gpio@15 {
+               reg = <15>;
+               type = <PCA955X_TYPE_GPIO>;
+       };
+
+       led@0 {
+               label = "red:power";
+               linux,default-trigger = "default-on";
+               reg = <0>;
+               type = <PCA955X_TYPE_LED>;
+       };
+       led@1 {
+               label = "green:power";
+               reg = <1>;
+               type = <PCA955X_TYPE_LED>;
+       };
+       led@2 {
+               label = "pca9552:yellow";
+               reg = <2>;
+               type = <PCA955X_TYPE_LED>;
+       };
+       led@3 {
+               label = "pca9552:white";
+               reg = <3>;
+               type = <PCA955X_TYPE_LED>;
+       };
+};
index 27dc800..52ea34e 100644 (file)
@@ -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
index 424898e..43bd8a4 100644 (file)
@@ -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);
 
index 617fe97..d03ed6b 100644 (file)
@@ -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,
 };
index e753ba9..764c313 100644 (file)
@@ -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);
        }
 }
 
index 478844c..31a9d74 100644 (file)
@@ -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;
index 5b529dc..72224b5 100644 (file)
@@ -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
 };
index f53c8cd..55c0517 100644 (file)
@@ -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,
index 9089258..05ffa34 100644 (file)
@@ -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,
index 3f9675b..3adb113 100644 (file)
@@ -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,
index 9a87311..9057291 100644 (file)
  */
 
 #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,
 };
 
index b2a98c7..b1adbd7 100644 (file)
@@ -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;
index 3045316..f5357f6 100644 (file)
@@ -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;
diff --git a/include/dt-bindings/leds/leds-pca955x.h b/include/dt-bindings/leds/leds-pca955x.h
new file mode 100644 (file)
index 0000000..78cb7e9
--- /dev/null
@@ -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 */
index 64c56d4..bf6db4f 100644 (file)
@@ -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;
 };