From 8e8d699b051f9ef86e31fb24569ccf3d869b54c4 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 5 Dec 2019 22:25:01 +0100 Subject: [PATCH 01/22] leds: gpio: Fix uninitialized gpio label for fwnode based probe When switching to using generic LED name composition mechanism via devm_led_classdev_register_ext() API the part of code initializing struct gpio_led's template name property was removed alongside. It was however overlooked that the property was also passed to devm_fwnode_get_gpiod_from_child() in place of "label" parameter, which when set to NULL, results in gpio label being initialized to '?'. It could be observed in debugfs and failed to properly identify gpio association with LED consumer. Fix this shortcoming by updating the GPIO label after the LED is registered and its final name is known. Fixes: d7235f5feaa0 ("leds: gpio: Use generic support for composing LED names") Cc: Russell King Reviewed-by: Linus Walleij Signed-off-by: Jacek Anaszewski [fixed comment] Signed-off-by: Pavel Machek --- drivers/leds/leds-gpio.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index a5c73f3d5f79..2bf74595610f 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -151,9 +151,14 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) struct gpio_led led = {}; const char *state = NULL; + /* + * Acquire gpiod from DT with uninitialized label, which + * will be updated after LED class device is registered, + * Only then the final LED name is known. + */ led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child, GPIOD_ASIS, - led.name); + NULL); if (IS_ERR(led.gpiod)) { fwnode_handle_put(child); return ERR_CAST(led.gpiod); @@ -186,6 +191,9 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) fwnode_handle_put(child); return ERR_PTR(ret); } + /* Set gpiod label to match the corresponding LED name. */ + gpiod_set_consumer_name(led_dat->gpiod, + led_dat->cdev.dev->kobj.name); priv->num_leds++; } From 9f4b952d0bfb7efffb7e85797efa5322e7e69dc5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 4 Dec 2019 09:56:42 +0200 Subject: [PATCH 02/22] leds-as3645a: Drop fwnode reference on ignored node If a node is ignored, do not get a reference to it. Fix the bug by moving fwnode_handle_get() where a reference to an fwnode is saved for clarity. Reported-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Pavel Machek --- drivers/leds/leds-as3645a.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-as3645a.c b/drivers/leds/leds-as3645a.c index b7e0ae1af8fa..e8922fa03379 100644 --- a/drivers/leds/leds-as3645a.c +++ b/drivers/leds/leds-as3645a.c @@ -493,16 +493,17 @@ static int as3645a_parse_node(struct as3645a *flash, switch (id) { case AS_LED_FLASH: flash->flash_node = child; + fwnode_handle_get(child); break; case AS_LED_INDICATOR: flash->indicator_node = child; + fwnode_handle_get(child); break; default: dev_warn(&flash->client->dev, "unknown LED %u encountered, ignoring\n", id); break; } - fwnode_handle_get(child); } if (!flash->flash_node) { From 697529091ac7a0a90ca349b914bb30641c13c753 Mon Sep 17 00:00:00 2001 From: Zahari Petkov Date: Mon, 18 Nov 2019 23:02:55 +0200 Subject: [PATCH 03/22] leds: pca963x: Fix open-drain initialization Before commit bb29b9cccd95 ("leds: pca963x: Add bindings to invert polarity") Mode register 2 was initialized directly with either 0x01 or 0x05 for open-drain or totem pole (push-pull) configuration. Afterwards, MODE2 initialization started using bitwise operations on top of the default MODE2 register value (0x05). Using bitwise OR for setting OUTDRV with 0x01 and 0x05 does not produce correct results. When open-drain is used, instead of setting OUTDRV to 0, the driver keeps it as 1: Open-drain: 0x05 | 0x01 -> 0x05 (0b101 - incorrect) Totem pole: 0x05 | 0x05 -> 0x05 (0b101 - correct but still wrong) Now OUTDRV setting uses correct bitwise operations for initialization: Open-drain: 0x05 & ~0x04 -> 0x01 (0b001 - correct) Totem pole: 0x05 | 0x04 -> 0x05 (0b101 - correct) Additional MODE2 register definitions are introduced now as well. Fixes: bb29b9cccd95 ("leds: pca963x: Add bindings to invert polarity") Signed-off-by: Zahari Petkov Signed-off-by: Pavel Machek --- drivers/leds/leds-pca963x.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 4afc317901a8..66cdc003b8f4 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -40,6 +40,8 @@ #define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ #define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ +#define PCA963X_MODE2_OUTDRV 0x04 /* Open-drain or totem pole */ +#define PCA963X_MODE2_INVRT 0x10 /* Normal or inverted direction */ #define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ #define PCA963X_MODE1 0x00 @@ -438,12 +440,12 @@ static int pca963x_probe(struct i2c_client *client, PCA963X_MODE2); /* Configure output: open-drain or totem pole (push-pull) */ if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= 0x01; + mode2 &= ~PCA963X_MODE2_OUTDRV; else - mode2 |= 0x05; + mode2 |= PCA963X_MODE2_OUTDRV; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) - mode2 |= 0x10; + mode2 |= PCA963X_MODE2_INVRT; i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2); } From 4c3718f9d6a6f6557b6e5564af8bd90130123fb8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Dec 2019 01:42:42 +0100 Subject: [PATCH 04/22] leds: bd2802: Convert to use GPIO descriptors The Rohm BD2802 have no in-kernel users so we can drop the GPIO number from the platform data and require users to provide the GPIO line using machine descriptors. As the descriptors come with inherent polarity inversion semantics, we invert the calls to set the GPIO line such that 0 means "unasserted" and 1 means "asserted". Put a note in the driver that machine descriptor tables will need to specify that the line is active low. Cc: Kim Kyuwon Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/leds-bd2802.c | 27 ++++++++++++++++++--------- include/linux/leds-bd2802.h | 1 - 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index e7ec6bff2b5f..bd61a823d0ca 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -67,6 +67,7 @@ struct led_state { struct bd2802_led { struct bd2802_led_platform_data *pdata; struct i2c_client *client; + struct gpio_desc *reset; struct rw_semaphore rwsem; struct led_state led[2]; @@ -200,7 +201,7 @@ static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, return; if (bd2802_is_all_off(led) && !led->adf_on) { - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); return; } @@ -226,7 +227,7 @@ static void bd2802_configure(struct bd2802_led *led) static void bd2802_reset_cancel(struct bd2802_led *led) { - gpio_set_value(led->pdata->reset_gpio, 1); + gpiod_set_value(led->reset, 0); udelay(100); bd2802_configure(led); } @@ -420,7 +421,7 @@ static void bd2802_disable_adv_conf(struct bd2802_led *led) bd2802_addr_attributes[i]); if (bd2802_is_all_off(led)) - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); led->adf_on = 0; } @@ -670,8 +671,16 @@ static int bd2802_probe(struct i2c_client *client, pdata = led->pdata = dev_get_platdata(&client->dev); i2c_set_clientdata(client, led); - /* Configure RESET GPIO (L: RESET, H: RESET cancel) */ - gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, "RGB_RESETB"); + /* + * Configure RESET GPIO (L: RESET, H: RESET cancel) + * + * We request the reset GPIO as OUT_LOW which means de-asserted, + * board files specifying this GPIO line in a machine descriptor + * table should take care to specify GPIO_ACTIVE_LOW for this line. + */ + led->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(led->reset)) + return PTR_ERR(led->reset); /* Tacss = min 0.1ms */ udelay(100); @@ -685,7 +694,7 @@ static int bd2802_probe(struct i2c_client *client, dev_info(&client->dev, "return 0x%02x\n", ret); /* To save the power, reset BD2802 after detecting */ - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); /* Default attributes */ led->wave_pattern = BD2802_PATTERN_HALF; @@ -720,7 +729,7 @@ static int bd2802_remove(struct i2c_client *client) struct bd2802_led *led = i2c_get_clientdata(client); int i; - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); bd2802_unregister_led_classdev(led); if (led->adf_on) bd2802_disable_adv_conf(led); @@ -750,7 +759,7 @@ static int bd2802_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct bd2802_led *led = i2c_get_clientdata(client); - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); return 0; } diff --git a/include/linux/leds-bd2802.h b/include/linux/leds-bd2802.h index dd93c8d787b4..ec577f5f8707 100644 --- a/include/linux/leds-bd2802.h +++ b/include/linux/leds-bd2802.h @@ -11,7 +11,6 @@ #define _LEDS_BD2802_H_ struct bd2802_led_platform_data{ - int reset_gpio; u8 rgb_time; }; From b0ff986084007ca91a9d0738825e593b41d3b979 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Dec 2019 11:08:32 +0100 Subject: [PATCH 05/22] led: max77650: add of_match table We need the of_match table if we want to use the compatible string in the pmic's child node and get the led driver loaded automatically. Signed-off-by: Bartosz Golaszewski Signed-off-by: Pavel Machek --- drivers/leds/leds-max77650.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/leds/leds-max77650.c b/drivers/leds/leds-max77650.c index 4c2d0b3c6dad..a0d4b725c917 100644 --- a/drivers/leds/leds-max77650.c +++ b/drivers/leds/leds-max77650.c @@ -135,9 +135,16 @@ err_node_put: return rv; } +static const struct of_device_id max77650_led_of_match[] = { + { .compatible = "maxim,max77650-led" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77650_led_of_match); + static struct platform_driver max77650_led_driver = { .driver = { .name = "max77650-led", + .of_match_table = max77650_led_of_match, }, .probe = max77650_led_probe, }; From b3b42b4a383c11daf34b6e4e997bd0d0bb83f02b Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Mon, 16 Dec 2019 09:32:58 -0500 Subject: [PATCH 06/22] leds: tps6105x: add driver for MFD chip LED mode This driver adds support for the LED operational mode of the tps6105x MFD device. Acked-by: Jacek Anaszewski Signed-off-by: Sven Van Asbroeck Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 10 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-tps6105x.c | 89 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/leds/leds-tps6105x.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 4b68520ac251..d82f1dea3711 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -836,6 +836,16 @@ config LEDS_LM36274 Say Y to enable the LM36274 LED driver for TI LMU devices. This supports the LED device LM36274. +config LEDS_TPS6105X + tristate "LED support for TI TPS6105X" + depends on LEDS_CLASS + depends on TPS6105X + default y if TPS6105X + help + This driver supports TPS61050/TPS61052 LED chips. + It is a single boost converter primarily for white LEDs and + audio amplifiers. + comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 2da39e896ce8..d7e1107753fb 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o +obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o # LED SPI Drivers obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o diff --git a/drivers/leds/leds-tps6105x.c b/drivers/leds/leds-tps6105x.c new file mode 100644 index 000000000000..09fd88a6c8f0 --- /dev/null +++ b/drivers/leds/leds-tps6105x.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 Sven Van Asbroeck + */ + +#include +#include +#include +#include +#include + +struct tps6105x_priv { + struct regmap *regmap; + struct led_classdev cdev; + struct fwnode_handle *fwnode; +}; + +static void tps6105x_handle_put(void *data) +{ + struct tps6105x_priv *priv = data; + + fwnode_handle_put(priv->fwnode); +} + +static int tps6105x_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct tps6105x_priv *priv = container_of(cdev, struct tps6105x_priv, + cdev); + + return regmap_update_bits(priv->regmap, TPS6105X_REG_0, + TPS6105X_REG0_TORCHC_MASK, + brightness << TPS6105X_REG0_TORCHC_SHIFT); +} + +static int tps6105x_led_probe(struct platform_device *pdev) +{ + struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); + struct tps6105x_platform_data *pdata = tps6105x->pdata; + struct led_init_data init_data = { }; + struct tps6105x_priv *priv; + int ret; + + /* This instance is not set for torch mode so bail out */ + if (pdata->mode != TPS6105X_MODE_TORCH) { + dev_info(&pdev->dev, + "chip not in torch mode, exit probe"); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + /* fwnode/devicetree is optional. NULL is allowed for priv->fwnode */ + priv->fwnode = device_get_next_child_node(pdev->dev.parent, NULL); + ret = devm_add_action_or_reset(&pdev->dev, tps6105x_handle_put, priv); + if (ret) + return ret; + priv->regmap = tps6105x->regmap; + priv->cdev.brightness_set_blocking = tps6105x_brightness_set; + priv->cdev.max_brightness = 7; + init_data.devicename = "tps6105x"; + init_data.default_label = ":torch"; + init_data.fwnode = priv->fwnode; + + ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK | + TPS6105X_REG0_TORCHC_MASK, + TPS6105X_REG0_MODE_TORCH << + TPS6105X_REG0_MODE_SHIFT); + if (ret) + return ret; + + return devm_led_classdev_register_ext(&pdev->dev, &priv->cdev, + &init_data); +} + +static struct platform_driver led_driver = { + .probe = tps6105x_led_probe, + .driver = { + .name = "tps6105x-leds", + }, +}; + +module_platform_driver(led_driver); + +MODULE_DESCRIPTION("TPS6105x LED driver"); +MODULE_AUTHOR("Sven Van Asbroeck "); +MODULE_LICENSE("GPL v2"); From 6bcf76316a146347ba4b72d0c34de631725d3b39 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Mon, 16 Dec 2019 09:32:59 -0500 Subject: [PATCH 07/22] dt-bindings: mfd: update TI tps6105x chip bindings The driver has been extended to optionally get its operational mode, regulator init data and LED naming from the devicetree. Acked-by: Jacek Anaszewski Reviewed-by: Rob Herring Signed-off-by: Sven Van Asbroeck Signed-off-by: Pavel Machek --- .../devicetree/bindings/mfd/tps6105x.txt | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/tps6105x.txt b/Documentation/devicetree/bindings/mfd/tps6105x.txt index 93602c7a19c8..dc448a9d5b4d 100644 --- a/Documentation/devicetree/bindings/mfd/tps6105x.txt +++ b/Documentation/devicetree/bindings/mfd/tps6105x.txt @@ -7,7 +7,22 @@ Required properties: - compatible: "ti,tps61050" or "ti,tps61052" - reg: Specifies the I2C slave address -Example: +Optional sub-node: + +This subnode selects the chip's operational mode. +There can be at most one single available subnode. + +- regulator: presence of this sub-node puts the chip in regulator mode. + see ../regulator/regulator.yaml + +- led: presence of this sub-node puts the chip in led mode. + Optional properties: + - function : see ../leds/common.txt + - color : see ../leds/common.txt + - label : see ../leds/common.txt + (deprecated) + +Example (GPIO operation only): i2c0 { tps61052@33 { @@ -15,3 +30,33 @@ i2c0 { reg = <0x33>; }; }; + +Example (GPIO + regulator operation): + +i2c0 { + tps61052@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + + regulator { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + }; +}; + +Example (GPIO + led operation): + +#include + +i2c0 { + tps61052@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + + led { + color = ; + }; + }; +}; From 2f8c665243d7a27cac34fd66156ff13656d20689 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sun, 10 Mar 2019 19:54:40 +0100 Subject: [PATCH 08/22] ledtrig-pattern: fix email address quoting in MODULE_AUTHOR() Apparently it is quite easy to forget ">" in quoting of email address. This fixes it. Signed-off-by: Pavel Machek --- drivers/leds/trigger/ledtrig-pattern.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index 718729c89440..3abcafe46278 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -455,7 +455,7 @@ static void __exit pattern_trig_exit(void) module_init(pattern_trig_init); module_exit(pattern_trig_exit); -MODULE_AUTHOR("Raphael Teysseyre "); +MODULE_AUTHOR("Baolin Wang "); MODULE_DESCRIPTION("LED Pattern trigger"); MODULE_LICENSE("GPL v2"); From 445da487f19cd4b2ee6035c043493c8adf4bf905 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 2 Jan 2020 22:29:35 +0100 Subject: [PATCH 09/22] leds: rb532: cleanup whitespace Trivial cleanup removing empty line at wrong place. Signed-off-by: Pavel Machek --- drivers/leds/leds-rb532.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c index db5af83f0cec..b6447c1721b4 100644 --- a/drivers/leds/leds-rb532.c +++ b/drivers/leds/leds-rb532.c @@ -21,7 +21,6 @@ static void rb532_led_set(struct led_classdev *cdev, { if (brightness) set_latch_u5(LO_ULED, 0); - else set_latch_u5(0, LO_ULED); } From 867b682459208dc3e18d3ab69f35da0064943889 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 2 Jan 2020 22:31:44 +0100 Subject: [PATCH 10/22] leds: lm3642: remove warnings for bad strtol, cleanup gotos Bad string from userspace is not worth printing warning on; gotos that just directly return are not good, plus a comment fix. Cc: jacek.anaszewski@gmail.com, linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org, dmurphy@ti.com Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3642.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 480575442ed8..4232906fcb75 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -106,7 +106,7 @@ static int lm3642_control(struct lm3642_chip_data *chip, ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); if (ret < 0) { dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); - goto out; + return ret; } if (chip->last_flag) @@ -146,11 +146,11 @@ static int lm3642_control(struct lm3642_chip_data *chip, break; default: - return ret; + return -EINVAL; } if (ret < 0) { dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); - goto out; + return ret; } if (chip->tx_pin) @@ -159,13 +159,12 @@ static int lm3642_control(struct lm3642_chip_data *chip, ret = regmap_update_bits(chip->regmap, REG_ENABLE, MODE_BITS_MASK << MODE_BITS_SHIFT, opmode << MODE_BITS_SHIFT); -out: return ret; } /* torch */ -/* torch pin config for lm3642*/ +/* torch pin config for lm3642 */ static ssize_t lm3642_torch_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -178,7 +177,7 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ret = kstrtouint(buf, 10, &state); if (ret) - goto out_strtoint; + return ret; if (state != 0) state = 0x01 << TORCH_PIN_EN_SHIFT; @@ -186,16 +185,12 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ret = regmap_update_bits(chip->regmap, REG_ENABLE, TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, state); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; + } return size; -out: - dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); - return ret; -out_strtoint: - dev_err(chip->dev, "%s: fail to change str to int\n", __func__); - return ret; } static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); @@ -229,7 +224,7 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ret = kstrtouint(buf, 10, &state); if (ret) - goto out_strtoint; + return ret; if (state != 0) state = 0x01 << STROBE_PIN_EN_SHIFT; @@ -237,16 +232,12 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ret = regmap_update_bits(chip->regmap, REG_ENABLE, STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, state); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; + } return size; -out: - dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); - return ret; -out_strtoint: - dev_err(chip->dev, "%s: fail to change str to int\n", __func__); - return ret; } static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); From cf6eb52fa3d0ff0414f3dfcfbd40bd57f3bbd679 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 7 Aug 2019 12:43:52 +0200 Subject: [PATCH 11/22] leds: lm3532: use extended registration so that LED can be used for backlight Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3532.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 0507c6575c08..23f49b6d1925 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -577,6 +577,12 @@ static int lm3532_parse_node(struct lm3532_data *priv) priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); device_for_each_child_node(priv->dev, child) { + struct led_init_data idata = { + .fwnode = child, + .default_label = ":", + .devicename = priv->client->name, + }; + led = &priv->leds[i]; ret = fwnode_property_read_u32(child, "reg", &control_bank); @@ -651,7 +657,7 @@ static int lm3532_parse_node(struct lm3532_data *priv) led->led_dev.name = led->label; led->led_dev.brightness_set_blocking = lm3532_brightness_set; - ret = devm_led_classdev_register(priv->dev, &led->led_dev); + ret = devm_led_classdev_register_ext(priv->dev, &led->led_dev, &idata); if (ret) { dev_err(&priv->client->dev, "led register err: %d\n", ret); From 31e065c4e8bac71be29a9d4d65ea62f5c9ec17b3 Mon Sep 17 00:00:00 2001 From: Pavel Date: Sun, 5 Jan 2020 23:36:58 +0100 Subject: [PATCH 12/22] leds: lm3532: add pointer to documentation and fix typo Add pointer to datasheet and fix typo in printk message. Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3532.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 23f49b6d1925..188a57da981a 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // TI LM3532 LED driver // Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// http://www.ti.com/lit/ds/symlink/lm3532.pdf #include #include @@ -629,7 +630,7 @@ static int lm3532_parse_node(struct lm3532_data *priv) led->num_leds = fwnode_property_count_u32(child, "led-sources"); if (led->num_leds > LM3532_MAX_LED_STRINGS) { - dev_err(&priv->client->dev, "To many LED string defined\n"); + dev_err(&priv->client->dev, "Too many LED string defined\n"); continue; } From 699a8c7c4bd376aee4808e6272188319e900c8af Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 3 Oct 2019 10:28:09 +0200 Subject: [PATCH 13/22] leds: Add of_led_get() and led_put() This patch adds basic support for a kernel driver to get a LED device. This will be used by the led-backlight driver. Only OF version is implemented for now, and the behavior is similar to PWM's of_pwm_get() and pwm_put(). Signed-off-by: Tomi Valkeinen Signed-off-by: Jean-Jacques Hiblot Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 44 ++++++++++++++++++++++++++++++++++++++++ include/linux/leds.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 438774315e6c..03ce74a2e6a9 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "leds.h" static struct class *leds_class; @@ -214,6 +215,49 @@ static int led_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume); +/** + * of_led_get() - request a LED device via the LED framework + * @np: device node to get the LED device from + * @index: the index of the LED + * + * Returns the LED device parsed from the phandle specified in the "leds" + * property of a device tree node or a negative error-code on failure. + */ +struct led_classdev *of_led_get(struct device_node *np, int index) +{ + struct device *led_dev; + struct led_classdev *led_cdev; + struct device_node *led_node; + + led_node = of_parse_phandle(np, "leds", index); + if (!led_node) + return ERR_PTR(-ENOENT); + + led_dev = class_find_device_by_of_node(leds_class, led_node); + of_node_put(led_node); + + if (!led_dev) + return ERR_PTR(-EPROBE_DEFER); + + led_cdev = dev_get_drvdata(led_dev); + + if (!try_module_get(led_cdev->dev->parent->driver->owner)) + return ERR_PTR(-ENODEV); + + return led_cdev; +} +EXPORT_SYMBOL_GPL(of_led_get); + +/** + * led_put() - release a LED device + * @led_cdev: LED device + */ +void led_put(struct led_classdev *led_cdev) +{ + module_put(led_cdev->dev->parent->driver->owner); +} +EXPORT_SYMBOL_GPL(led_put); + static int led_classdev_next_name(const char *init_name, char *name, size_t len) { diff --git a/include/linux/leds.h b/include/linux/leds.h index 242258f7d837..dcfb6a325866 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -20,6 +20,7 @@ struct device; struct led_pattern; +struct device_node; /* * LED Core */ @@ -196,6 +197,9 @@ void devm_led_classdev_unregister(struct device *parent, void led_classdev_suspend(struct led_classdev *led_cdev); void led_classdev_resume(struct led_classdev *led_cdev); +extern struct led_classdev *of_led_get(struct device_node *np, int index); +extern void led_put(struct led_classdev *led_cdev); + /** * led_blink_set - set blinking with software fallback * @led_cdev: the LED to start blinking From e389240ad99292d52a4f8ff30f16830dc9c63779 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 3 Oct 2019 10:28:10 +0200 Subject: [PATCH 14/22] leds: Add managed API to get a LED from a device driver If the LED is acquired by a consumer device with devm_led_get(), it is automatically released when the device is detached. Signed-off-by: Jean-Jacques Hiblot Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 49 ++++++++++++++++++++++++++++++++++++++++ include/linux/leds.h | 2 ++ 2 files changed, 51 insertions(+) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 03ce74a2e6a9..0868f841d656 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -258,6 +258,55 @@ void led_put(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_put); +static void devm_led_release(struct device *dev, void *res) +{ + struct led_classdev **p = res; + + led_put(*p); +} + +/** + * devm_of_led_get - Resource-managed request of a LED device + * @dev: LED consumer + * @index: index of the LED to obtain in the consumer + * + * The device node of the device is parse to find the request LED device. + * The LED device returned from this function is automatically released + * on driver detach. + * + * @return a pointer to a LED device or ERR_PTR(errno) on failure. + */ +struct led_classdev *__must_check devm_of_led_get(struct device *dev, + int index) +{ + struct led_classdev *led; + struct led_classdev **dr; + + if (!dev) + return ERR_PTR(-EINVAL); + + /* Not using device tree? */ + if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) + return ERR_PTR(-ENOTSUPP); + + led = of_led_get(dev->of_node, index); + if (IS_ERR(led)) + return led; + + dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), + GFP_KERNEL); + if (!dr) { + led_put(led); + return ERR_PTR(-ENOMEM); + } + + *dr = led; + devres_add(dev, dr); + + return led; +} +EXPORT_SYMBOL_GPL(devm_of_led_get); + static int led_classdev_next_name(const char *init_name, char *name, size_t len) { diff --git a/include/linux/leds.h b/include/linux/leds.h index dcfb6a325866..75353e5f9d13 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -199,6 +199,8 @@ void led_classdev_resume(struct led_classdev *led_cdev); extern struct led_classdev *of_led_get(struct device_node *np, int index); extern void led_put(struct led_classdev *led_cdev); +struct led_classdev *__must_check devm_of_led_get(struct device *dev, + int index); /** * led_blink_set - set blinking with software fallback From 7a349e8c535d7327bf80710323c725df47149b8d Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Sun, 5 Jan 2020 23:31:14 +0100 Subject: [PATCH 15/22] leds: populate the device's of_node If initialization data is available and its fwnode is actually a of_node, store this information in the led device's structure. This will allow the device to use or provide OF-based API such (devm_xxx). Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 0868f841d656..1fc40e8af75e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -369,8 +369,10 @@ int led_classdev_register_ext(struct device *parent, mutex_unlock(&led_cdev->led_access); return PTR_ERR(led_cdev->dev); } - if (init_data && init_data->fwnode) + if (init_data && init_data->fwnode) { led_cdev->dev->fwnode = init_data->fwnode; + led_cdev->dev->of_node = to_of_node(init_data->fwnode); + } if (ret) dev_warn(parent, "Led %s renamed to %s due to name collision", From ebf59a4558c2c1629dff276da6fc9b0e77800fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 4 Jan 2020 11:54:17 +0100 Subject: [PATCH 16/22] dt: bindings: lm3692x: Add ti,ovp-microvolt property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to set the overvoltage protection to 17V, 21V, 25V or 29V. Signed-off-by: Guido Günther Acked-by: Pavel Machek Signed-off-by: Pavel Machek Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/leds/leds-lm3692x.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt index 4c2d923f8758..9b334695c410 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt @@ -18,6 +18,10 @@ Required properties: Optional properties: - enable-gpios : gpio pin to enable/disable the device. - vled-supply : LED supply + - ti,ovp-microvolt: Overvoltage protection in + micro-volt, can be 17000000, 21000000, 25000000 or + 29000000. If ti,ovp-microvolt is not specified it + defaults to 29000000. Required child properties: - reg : 0 - Will enable all LED sync paths @@ -44,6 +48,7 @@ led-controller@36 { enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; vled-supply = <&vbatt>; + ti,ovp-microvolt = <29000000>; led@0 { reg = <0>; From cffd61a5c7755546154539dcd7f36590e91e002f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 4 Jan 2020 11:54:18 +0100 Subject: [PATCH 17/22] leds: lm3692x: Allow to configure over voltage protection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overvoltage protection is currently using the default of 29V. Make it configurable via DT. Signed-off-by: Guido Günther Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 8b408102e138..28973cc5a6cc 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -114,6 +114,8 @@ struct lm3692x_led { struct regulator *regulator; int led_enable; int model_id; + + u8 boost_ctrl; }; static const struct reg_default lm3692x_reg_defs[] = { @@ -249,10 +251,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; @@ -326,6 +325,7 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) { struct fwnode_handle *child = NULL; struct led_init_data init_data = {}; + u32 ovp; int ret; led->enable_gpio = devm_gpiod_get_optional(&led->client->dev, @@ -350,6 +350,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"); From 3e0801b62a30f5c83df0b7c75afce50061401e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 4 Jan 2020 11:54:21 +0100 Subject: [PATCH 18/22] dt: bindings: lm3692x: Add led-max-microamp property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can be used to limit the current per LED strip. Signed-off-by: Guido Günther Acked-by: Pavel Machek Reviewed-by: Rob Herring Signed-off-by: Pavel Machek --- Documentation/devicetree/bindings/leds/leds-lm3692x.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt index 9b334695c410..501468aa4d38 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt @@ -35,6 +35,8 @@ Optional child properties: - label : see Documentation/devicetree/bindings/leds/common.txt (deprecated) - linux,default-trigger : see Documentation/devicetree/bindings/leds/common.txt + - led-max-microamp : + see Documentation/devicetree/bindings/leds/common.txt Example: @@ -55,6 +57,7 @@ led-controller@36 { function = LED_FUNCTION_BACKLIGHT; color = ; linux,default-trigger = "backlight"; + led-max-microamp = <20000>; }; } From df7083b57331da0222380392d8d0d8b39972bef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 6 Jan 2020 16:48:50 +0100 Subject: [PATCH 19/22] leds: lm3692x: Make sure we don't exceed the maximum LED current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current is given by the formular from page 12 of https://www.ti.com/lit/ds/symlink/lm36922.pdf. We use this to limit the led's max_brightness using the led-max-microamp DT property. The formula for the lm36923 is identical according to the data sheet. Signed-off-by: Guido Günther Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 28973cc5a6cc..1b056af60bd6 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -321,11 +322,24 @@ out: 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; + u32 ovp, max_cur; int ret; led->enable_gpio = devm_gpiod_get_optional(&led->client->dev, @@ -391,6 +405,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 = ":"; From 5e9d7b6f433628b3e178bbf1ca62fe1965afe361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 6 Jan 2020 16:48:51 +0100 Subject: [PATCH 20/22] leds: lm3692x: Move lm3692x_init and rename to lm3692x_leds_enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves lm3692x_init so it can be used from lm3692x_brightness_set. Rename to lm3692_leds_enable to pair up with lm3692x_leds_disable. No functional change. Signed-off-by: Guido Günther Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 1b056af60bd6..f7fdfaee5ac5 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -165,40 +165,7 @@ 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; @@ -322,6 +289,39 @@ out: 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); + + 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) { @@ -451,7 +451,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; From 4a8d2bee2080d32511b70d8558036894f42617fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 6 Jan 2020 16:48:52 +0100 Subject: [PATCH 21/22] leds: lm3692x: Split out lm3692x_leds_disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the relevant parts out of lm3692x_remove() and call it from there. No functional change. Signed-off-by: Guido Günther Acked-by: Pavel Machek Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 42 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index f7fdfaee5ac5..1254695d7e94 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -289,6 +289,30 @@ out: return ret; } +static int lm3692x_leds_disable(struct lm3692x_led *led) +{ + 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); + 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); + } + + return ret; +} + static int lm3692x_brightness_set(struct led_classdev *led_cdev, enum led_brightness brt_val) { @@ -463,23 +487,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; From 260718b3a35d23fe89d27cc7b5e8bd30f4bba1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 6 Jan 2020 16:48:53 +0100 Subject: [PATCH 22/22] leds: lm3692x: Disable chip on brightness 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise there's a noticeable glow even with brightness 0. Also turning off the regulator can save additional power. Signed-off-by: Guido Günther Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 1254695d7e94..28a51aeb28de 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -116,7 +116,8 @@ struct lm3692x_led { int led_enable; int model_id; - u8 boost_ctrl; + u8 boost_ctrl, brightness_ctrl; + bool enabled; }; static const struct reg_default lm3692x_reg_defs[] = { @@ -170,6 +171,9 @@ 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) { @@ -272,6 +276,7 @@ static int lm3692x_leds_enable(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"); @@ -293,6 +298,9 @@ 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", @@ -310,6 +318,7 @@ static int lm3692x_leds_disable(struct lm3692x_led *led) "Failed to disable regulator: %d\n", ret); } + led->enabled = false; return ret; } @@ -323,6 +332,13 @@ static int lm3692x_brightness_set(struct led_classdev *led_cdev, 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",