]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
leds: leds-lp50xx: Enable chip before any communication
authorChristian Hitz <christian.hitz@bbv.ch>
Mon, 5 Jan 2026 16:48:08 +0000 (11:48 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Jan 2026 12:12:07 +0000 (13:12 +0100)
[ Upstream commit 434959618c47efe9e5f2e20f4a850caac4f6b823 ]

If a GPIO is used to control the chip's enable pin, it needs to be pulled
high before any i2c communication is attempted.

Currently, the enable GPIO handling is not correct.

Assume the enable GPIO is low when the probe function is entered. In this
case the device is in SHUTDOWN mode and does not react to i2c commands.

During probe the following sequence happens:
 1. The call to lp50xx_reset() on line 548 has no effect as i2c is not
    possible yet.
 2. Then - on line 552 - lp50xx_enable_disable() is called. As
    "priv->enable_gpio“ has not yet been initialized, setting the GPIO has
    no effect. Also the i2c enable command is not executed as the device
    is still in SHUTDOWN.
 3. On line 556 the call to lp50xx_probe_dt() finally parses the rest of
    the DT and the configured priv->enable_gpio is set up.

As a result the device is still in SHUTDOWN mode and not ready for
operation.

Split lp50xx_enable_disable() into distinct enable and disable functions
to enforce correct ordering between enable_gpio manipulations and i2c
commands.
Read enable_gpio configuration from DT before attempting to manipulate
enable_gpio.
Add delays to observe correct wait timing after manipulating enable_gpio
and before any i2c communication.

Cc: stable@vger.kernel.org
Fixes: 242b81170fb8 ("leds: lp50xx: Add the LP50XX family of the RGB LED driver")
Signed-off-by: Christian Hitz <christian.hitz@bbv.ch>
Link: https://patch.msgid.link/20251028155141.1603193-1-christian@klarinett.li
Signed-off-by: Lee Jones <lee@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/leds/leds-lp50xx.c

index 1eb21d603b8eff813b9e7ac6c897ec6634aaf46e..db154a03c6752260449e1f1303f2e7c309cbb0b6 100644 (file)
 
 #define LP50XX_SW_RESET                0xff
 #define LP50XX_CHIP_EN         BIT(6)
+#define LP50XX_CHIP_DISABLE    0x00
+#define LP50XX_START_TIME_US   500
+#define LP50XX_RESET_TIME_US   3
+
+#define LP50XX_EN_GPIO_LOW     0
+#define LP50XX_EN_GPIO_HIGH    1
 
 /* There are 3 LED outputs per bank */
 #define LP50XX_LEDS_PER_MODULE 3
@@ -376,19 +382,42 @@ static int lp50xx_reset(struct lp50xx *priv)
        return regmap_write(priv->regmap, priv->chip_info->reset_reg, LP50XX_SW_RESET);
 }
 
-static int lp50xx_enable_disable(struct lp50xx *priv, int enable_disable)
+static int lp50xx_enable(struct lp50xx *priv)
 {
        int ret;
 
-       ret = gpiod_direction_output(priv->enable_gpio, enable_disable);
+       if (priv->enable_gpio) {
+               ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_HIGH);
+               if (ret)
+                       return ret;
+
+               udelay(LP50XX_START_TIME_US);
+       }
+
+       ret = lp50xx_reset(priv);
        if (ret)
                return ret;
 
-       if (enable_disable)
-               return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
-       else
-               return regmap_write(priv->regmap, LP50XX_DEV_CFG0, 0);
+       return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
+}
 
+static int lp50xx_disable(struct lp50xx *priv)
+{
+       int ret;
+
+       ret = regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_DISABLE);
+       if (ret)
+               return ret;
+
+       if (priv->enable_gpio) {
+               ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_LOW);
+               if (ret)
+                       return ret;
+
+               udelay(LP50XX_RESET_TIME_US);
+       }
+
+       return 0;
 }
 
 static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
@@ -458,6 +487,10 @@ static int lp50xx_probe_dt(struct lp50xx *priv)
                return ret;
        }
 
+       ret = lp50xx_enable(priv);
+       if (ret)
+               return ret;
+
        priv->regulator = devm_regulator_get(priv->dev, "vled");
        if (IS_ERR(priv->regulator))
                priv->regulator = NULL;
@@ -565,14 +598,6 @@ static int lp50xx_probe(struct i2c_client *client,
                return ret;
        }
 
-       ret = lp50xx_reset(led);
-       if (ret)
-               return ret;
-
-       ret = lp50xx_enable_disable(led, 1);
-       if (ret)
-               return ret;
-
        return lp50xx_probe_dt(led);
 }
 
@@ -581,7 +606,7 @@ static int lp50xx_remove(struct i2c_client *client)
        struct lp50xx *led = i2c_get_clientdata(client);
        int ret;
 
-       ret = lp50xx_enable_disable(led, 0);
+       ret = lp50xx_disable(led);
        if (ret)
                dev_err(led->dev, "Failed to disable chip\n");