This patch extends the device tree support for the pca9532 allowing LEDs to 
blink, dim or even being unchanged, i.e. not being turned off during driver 
initialization.

Signed-off-by: Felix Brack <f...@ltec.ch>
---
 .../devicetree/bindings/leds/leds-pca9532.txt      | 22 ++++++++++++
 drivers/leds/leds-pca9532.c                        | 41 +++++++++++++++++++++-
 include/linux/leds-pca9532.h                       |  4 +--
 3 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/leds/leds-pca9532.txt 
b/Documentation/devicetree/bindings/leds/leds-pca9532.txt
index 198f3ba..81b6563 100644
--- a/Documentation/devicetree/bindings/leds/leds-pca9532.txt
+++ b/Documentation/devicetree/bindings/leds/leds-pca9532.txt
@@ -11,12 +11,24 @@ Required properties:
                "nxp,pca9533"
        - reg -  I2C slave address
 
+Optional properties:
+       - psc0: 8 bit prescaler value according to NXP data sheet
+       - pwm0: 8 bit PWM value according to NXP data sheet
+       - psc1: 8 bit prescaler value according to NXP data sheet
+       - pwm1: 8 bit PWM value according to NXP data sheet
+
 Each led is represented as a sub-node of the nxp,pca9530.
 
 Optional sub-node properties:
        - label: see Documentation/devicetree/bindings/leds/common.txt
        - type: Output configuration, see dt-bindings/leds/leds-pca9532.h 
(default NONE)
        - linux,default-trigger: see 
Documentation/devicetree/bindings/leds/common.txt
+       - default-state: see Documentation/devicetree/bindings/leds/common.txt
+         This property is only valid for sub-nodes of type <PCA9532_TYPE_LED>.
+         In addition to the values mentioned in the document above the 
additional
+         values "pwm0" and "pwm1" are valid. The corresponding LED will blink
+         or will be dimmed depending on the configuration of prescaler and pwm
+         values (see optional node properties above).
 
 Example:
   #include <dt-bindings/leds/leds-pca9532.h>
@@ -24,6 +36,8 @@ Example:
   leds: pca9530@60 {
     compatible = "nxp,pca9530";
     reg = <0x60>;
+    psc0 = <0x97>; // blink frequency 1Hz
+    pwm0 = <0x80>; // 50% duty cycle (500ms On / 500ms Off)
 
     red-power {
       label = "pca:red:power";
@@ -33,6 +47,14 @@ Example:
       label = "pca:green:power";
       type = <PCA9532_TYPE_LED>;
     };
+    kernel-booting {
+       type = <PCA9532_TYPE_LED>;
+       default-state = "pwm0";
+    };
+    sys-stat {
+       type = <PCA9532_TYPE_LED>;
+       default-state = "keep"; // don't touch, was set by U-Boot
+    };
   };
 
 For more product information please see the link below:
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 06e6310..3353739 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -254,6 +254,21 @@ static void pca9532_input_work(struct work_struct *work)
        mutex_unlock(&data->update_lock);
 }
 
+static enum pca9532_state pca9532_getled(struct pca9532_led *led)
+{
+       struct i2c_client *client = led->client;
+       struct pca9532_data *data = i2c_get_clientdata(client);
+       u8 maxleds = data->chip_info->num_leds;
+       char reg;
+       enum pca9532_state ret;
+
+       mutex_lock(&data->update_lock);
+       reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
+       ret = reg >> LED_NUM(led->id)/2;
+       mutex_unlock(&data->update_lock);
+       return ret;
+}
+
 #ifdef CONFIG_LEDS_PCA9532_GPIO
 static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset)
 {
@@ -366,7 +381,10 @@ static int pca9532_configure(struct i2c_client *client,
                        gpios++;
                        break;
                case PCA9532_TYPE_LED:
-                       led->state = pled->state;
+                       if (pled->state == PCA9532_KEEP)
+                               led->state = pca9532_getled(led);
+                       else
+                               led->state = pled->state;
                        led->name = pled->name;
                        led->ldev.name = led->name;
                        led->ldev.default_trigger = pled->default_trigger;
@@ -456,6 +474,8 @@ pca9532_of_populate_pdata(struct device *dev, struct 
device_node *np)
        const struct of_device_id *match;
        int devid, maxleds;
        int i = 0;
+       const char *state;
+       u32 val;
 
        match = of_match_device(of_pca9532_leds_match, dev);
        if (!match)
@@ -468,6 +488,15 @@ pca9532_of_populate_pdata(struct device *dev, struct 
device_node *np)
        if (!pdata)
                return ERR_PTR(-ENOMEM);
 
+       if (!of_property_read_u32(np, "psc0", &val))
+               pdata->psc[0] = val & 0xff;
+       if (!of_property_read_u32(np, "pwm0", &val))
+               pdata->pwm[0] = val & 0xff;
+       if (!of_property_read_u32(np, "psc1", &val))
+               pdata->psc[1] = val & 0xff;
+       if (!of_property_read_u32(np, "pwm1", &val))
+               pdata->pwm[1] = val & 0xff;
+
        for_each_child_of_node(np, child) {
                if (of_property_read_string(child, "label",
                                            &pdata->leds[i].name))
@@ -475,6 +504,16 @@ pca9532_of_populate_pdata(struct device *dev, struct 
device_node *np)
                of_property_read_u32(child, "type", &pdata->leds[i].type);
                of_property_read_string(child, "linux,default-trigger",
                                        &pdata->leds[i].default_trigger);
+               if (!of_property_read_string(child, "default-state", &state)) {
+                       if (!strcmp(state, "on"))
+                               pdata->leds[i].state = PCA9532_ON;
+                       else if (!strcmp(state, "keep"))
+                               pdata->leds[i].state = PCA9532_KEEP;
+                       else if (!strcmp(state, "pwm0"))
+                               pdata->leds[i].state = PCA9532_PWM0;
+                       else if (!strcmp(state, "pwm1"))
+                               pdata->leds[i].state = PCA9532_PWM1;
+               }
                if (++i >= maxleds) {
                        of_node_put(child);
                        break;
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h
index d215b45..a327b1aa 100644
--- a/include/linux/leds-pca9532.h
+++ b/include/linux/leds-pca9532.h
@@ -22,7 +22,8 @@ enum pca9532_state {
        PCA9532_OFF  = 0x0,
        PCA9532_ON   = 0x1,
        PCA9532_PWM0 = 0x2,
-       PCA9532_PWM1 = 0x3
+       PCA9532_PWM1 = 0x3,
+       PCA9532_KEEP = 0xff
 };
 
 struct pca9532_led {
@@ -44,4 +45,3 @@ struct pca9532_platform_data {
 };
 
 #endif /* __LINUX_PCA9532_H */
-
-- 
2.7.4

Reply via email to