]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/leds/leds-lm3692x.c
Merge tag 'tag-chrome-platform-for-v5.6' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / leds / leds-lm3692x.c
index 8b408102e138715f82e079eebaed36e3e8d94ea1..28a51aeb28dec598f4b621501e2fdf81f7409986 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/leds.h>
+#include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
@@ -114,6 +115,9 @@ struct lm3692x_led {
        struct regulator *regulator;
        int led_enable;
        int model_id;
+
+       u8 boost_ctrl, brightness_ctrl;
+       bool enabled;
 };
 
 static const struct reg_default lm3692x_reg_defs[] = {
@@ -162,44 +166,14 @@ static int lm3692x_fault_check(struct lm3692x_led *led)
        return read_buf;
 }
 
-static int lm3692x_brightness_set(struct led_classdev *led_cdev,
-                               enum led_brightness brt_val)
-{
-       struct lm3692x_led *led =
-                       container_of(led_cdev, struct lm3692x_led, led_dev);
-       int ret;
-       int led_brightness_lsb = (brt_val >> 5);
-
-       mutex_lock(&led->lock);
-
-       ret = lm3692x_fault_check(led);
-       if (ret) {
-               dev_err(&led->client->dev, "Cannot read/clear faults: %d\n",
-                       ret);
-               goto out;
-       }
-
-       ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val);
-       if (ret) {
-               dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret);
-               goto out;
-       }
-
-       ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb);
-       if (ret) {
-               dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret);
-               goto out;
-       }
-out:
-       mutex_unlock(&led->lock);
-       return ret;
-}
-
-static int lm3692x_init(struct lm3692x_led *led)
+static int lm3692x_leds_enable(struct lm3692x_led *led)
 {
        int enable_state;
        int ret, reg_ret;
 
+       if (led->enabled)
+               return 0;
+
        if (led->regulator) {
                ret = regulator_enable(led->regulator);
                if (ret) {
@@ -249,10 +223,7 @@ static int lm3692x_init(struct lm3692x_led *led)
        if (ret)
                goto out;
 
-       ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL,
-                       LM3692X_BOOST_SW_1MHZ |
-                       LM3692X_BOOST_SW_NO_SHIFT |
-                       LM3692X_OCP_PROT_1_5A);
+       ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, led->boost_ctrl);
        if (ret)
                goto out;
 
@@ -305,6 +276,7 @@ static int lm3692x_init(struct lm3692x_led *led)
        ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK,
                                 enable_state | LM3692X_DEVICE_EN);
 
+       led->enabled = true;
        return ret;
 out:
        dev_err(&led->client->dev, "Fail writing initialization values\n");
@@ -322,10 +294,92 @@ static int lm3692x_init(struct lm3692x_led *led)
        return ret;
 }
 
+static int lm3692x_leds_disable(struct lm3692x_led *led)
+{
+       int ret;
+
+       if (!led->enabled)
+               return 0;
+
+       ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0);
+       if (ret) {
+               dev_err(&led->client->dev, "Failed to disable regulator: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (led->enable_gpio)
+               gpiod_direction_output(led->enable_gpio, 0);
+
+       if (led->regulator) {
+               ret = regulator_disable(led->regulator);
+               if (ret)
+                       dev_err(&led->client->dev,
+                               "Failed to disable regulator: %d\n", ret);
+       }
+
+       led->enabled = false;
+       return ret;
+}
+
+static int lm3692x_brightness_set(struct led_classdev *led_cdev,
+                               enum led_brightness brt_val)
+{
+       struct lm3692x_led *led =
+                       container_of(led_cdev, struct lm3692x_led, led_dev);
+       int ret;
+       int led_brightness_lsb = (brt_val >> 5);
+
+       mutex_lock(&led->lock);
+
+       if (brt_val == 0) {
+               ret = lm3692x_leds_disable(led);
+               goto out;
+       } else {
+               lm3692x_leds_enable(led);
+       }
+
+       ret = lm3692x_fault_check(led);
+       if (ret) {
+               dev_err(&led->client->dev, "Cannot read/clear faults: %d\n",
+                       ret);
+               goto out;
+       }
+
+       ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val);
+       if (ret) {
+               dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret);
+               goto out;
+       }
+
+       ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb);
+       if (ret) {
+               dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&led->lock);
+       return ret;
+}
+
+static enum led_brightness lm3692x_max_brightness(struct lm3692x_led *led,
+                                                 u32 max_cur)
+{
+       u32 max_code;
+
+       /* see p.12 of LM36922 data sheet for brightness formula */
+       max_code = ((max_cur * 1000) - 37806) / 12195;
+       if (max_code > 0x7FF)
+               max_code = 0x7FF;
+
+       return max_code >> 3;
+}
+
 static int lm3692x_probe_dt(struct lm3692x_led *led)
 {
        struct fwnode_handle *child = NULL;
        struct led_init_data init_data = {};
+       u32 ovp, max_cur;
        int ret;
 
        led->enable_gpio = devm_gpiod_get_optional(&led->client->dev,
@@ -350,6 +404,32 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
                led->regulator = NULL;
        }
 
+       led->boost_ctrl = LM3692X_BOOST_SW_1MHZ |
+               LM3692X_BOOST_SW_NO_SHIFT |
+               LM3692X_OCP_PROT_1_5A;
+       ret = device_property_read_u32(&led->client->dev,
+                                      "ti,ovp-microvolt", &ovp);
+       if (ret) {
+               led->boost_ctrl |= LM3692X_OVP_29V;
+       } else {
+               switch (ovp) {
+               case 17000000:
+                       break;
+               case 21000000:
+                       led->boost_ctrl |= LM3692X_OVP_21V;
+                       break;
+               case 25000000:
+                       led->boost_ctrl |= LM3692X_OVP_25V;
+                       break;
+               case 29000000:
+                       led->boost_ctrl |= LM3692X_OVP_29V;
+                       break;
+               default:
+                       dev_err(&led->client->dev, "Invalid OVP %d\n", ovp);
+                       return -EINVAL;
+               }
+       }
+
        child = device_get_next_child_node(&led->client->dev, child);
        if (!child) {
                dev_err(&led->client->dev, "No LED Child node\n");
@@ -365,6 +445,10 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
                return ret;
        }
 
+       ret = fwnode_property_read_u32(child, "led-max-microamp", &max_cur);
+       led->led_dev.max_brightness = ret ? LED_FULL :
+               lm3692x_max_brightness(led, max_cur);
+
        init_data.fwnode = child;
        init_data.devicename = led->client->name;
        init_data.default_label = ":";
@@ -407,7 +491,7 @@ static int lm3692x_probe(struct i2c_client *client,
        if (ret)
                return ret;
 
-       ret = lm3692x_init(led);
+       ret = lm3692x_leds_enable(led);
        if (ret)
                return ret;
 
@@ -419,23 +503,9 @@ static int lm3692x_remove(struct i2c_client *client)
        struct lm3692x_led *led = i2c_get_clientdata(client);
        int ret;
 
-       ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0);
-       if (ret) {
-               dev_err(&led->client->dev, "Failed to disable regulator: %d\n",
-                       ret);
+       ret = lm3692x_leds_disable(led);
+       if (ret)
                return ret;
-       }
-
-       if (led->enable_gpio)
-               gpiod_direction_output(led->enable_gpio, 0);
-
-       if (led->regulator) {
-               ret = regulator_disable(led->regulator);
-               if (ret)
-                       dev_err(&led->client->dev,
-                               "Failed to disable regulator: %d\n", ret);
-       }
-
        mutex_destroy(&led->lock);
 
        return 0;