* [RFC PATCH 0/2] leds: flash: LM3643 dual LED flash driver
@ 2026-08-21 8:36 Rillian Grant
2026-08-21 8:36 ` [RFC PATCH 1/2] dt-bindings: leds: Document TI " Rillian Grant
2026-08-21 8:36 ` [RFC PATCH 2/2] leds: flash: Add support for the " Rillian Grant
0 siblings, 2 replies; 5+ messages in thread
From: Rillian Grant @ 2026-08-21 8:36 UTC (permalink / raw)
To: lee, pavel, linux-leds; +Cc: Rillian Grant
This series adds a flash LED driver for the Texas Instruments
LM3643. I wrote it to support the Windows Hello NIR illuminator on my
ThinkPad X9-15 Gen 1, which appears to have a single LED connected to
both current sources. All non-error code paths have been tested on my
device.
This device is enumerated by ACPI (HID TXNW3643) as an I2C client with
no _DSD. The driver, following the leds/flash convention, sees no LED
child nodes and fails to probe. For testing I have been using an SSDT
overlay.
On Windows, Lenovo distributes an LM3643 driver authored by Intel. Its
inf file matches on the HID alone and contains a default intensity for
each LED, 100% for LED1 and 60% for LED2, which suggests the driver
applies a static configuration to any enumerated LM3643.
I am sending this RFC to ask how best to supply this default static
configuration. The options I can see are:
1. A DMI-matched table, similar to touchscreen_dmi.c, that attaches
LED child nodes on verified boards. I haven't found any existing
examples of this being done for LED child nodes.
2. A generic fallback in the driver exposing both current sources as
independent LEDs. However this would apply to all matches without
a firmware description.
I'd appreciate any guidance on the preferred approach.
Rillian Grant (2):
dt-bindings: leds: Document TI LM3643 dual LED flash driver
leds: flash: Add support for the TI LM3643 dual LED flash driver
.../devicetree/bindings/leds/ti,lm3643.yaml | 126 +++
MAINTAINERS | 7 +
drivers/leds/flash/Kconfig | 12 +
drivers/leds/flash/Makefile | 1 +
drivers/leds/flash/leds-lm3643.c | 775 ++++++++++++++++++
5 files changed, 921 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3643.yaml
create mode 100644 drivers/leds/flash/leds-lm3643.c
base-commit: a4ff2be345d0abc943da8dd8da98151843b750dc
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH 1/2] dt-bindings: leds: Document TI LM3643 dual LED flash driver
2026-08-21 8:36 [RFC PATCH 0/2] leds: flash: LM3643 dual LED flash driver Rillian Grant
@ 2026-08-21 8:36 ` Rillian Grant
2026-08-21 8:44 ` sashiko-bot
2026-08-21 8:36 ` [RFC PATCH 2/2] leds: flash: Add support for the " Rillian Grant
1 sibling, 1 reply; 5+ messages in thread
From: Rillian Grant @ 2026-08-21 8:36 UTC (permalink / raw)
To: lee, pavel, linux-leds
Cc: Rillian Grant, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
Document Texas Instruments LM3643 dual LED flash driver devicetree
bindings.
Signed-off-by: Rillian Grant <rillian.grant@gmail.com>
---
.../devicetree/bindings/leds/ti,lm3643.yaml | 126 ++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3643.yaml
diff --git a/Documentation/devicetree/bindings/leds/ti,lm3643.yaml b/Documentation/devicetree/bindings/leds/ti,lm3643.yaml
new file mode 100644
index 000000000000..18b08a79512f
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/ti,lm3643.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/ti,lm3643.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments LM3643 1500mA dual LED Flash Driver
+
+maintainers:
+ - Rillian Grant <rillian.grant@gmail.com>
+
+description: |
+ The LM3643 is a dual LED flash driver utilizing a synchronous boost converter
+ to provide power to two current sources.
+
+ The dual high-side current sources can be tied together to provide flash
+ currents up to 1.5A through a single LED.
+
+ Datasheet: https://www.ti.com/lit/ds/symlink/lm3643.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,lm3643
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^led@[0-1]$":
+ type: object
+ $ref: common.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description: Index of the LED.
+ minimum: 0
+ maximum: 1
+
+ led-sources:
+ minItems: 1
+ maxItems: 2
+ items:
+ minimum: 0
+ maximum: 1
+
+ flash-max-microamp:
+ description: |
+ Maximum flash current through this LED. Each high-side current source
+ can provide up to 1.5A. The maximum current the boost can deliver is
+ 1.5A across both sources.
+
+ Valid values for an LED connected to one source:
+ 10900 - 1500000, step by 11725 (rounded down)
+ Valid values for an LED connected to both sources:
+ 21800 - 1500000, step by 23450 (rounded down)
+
+ minimum: 10900
+ maximum: 1500000
+
+ led-max-microamp:
+ description: |
+ Maximum torch current through this LED. In torch mode each high-side
+ current source can provide up to 179mA.
+
+ Valid values for an LED connected to one source:
+ 977 - 179000, step by 1400 (rounded down)
+ Valid values for an LED connected to both sources:
+ 1954 - 358000, step by 2800 (rounded down)
+
+ minimum: 977
+ maximum: 358000
+
+ flash-max-timeout-us:
+ description: |
+ The chip supports timeouts from 10ms to 100ms in increments of 10ms
+ and from 100ms to 400ms in increments of 50ms. The provided value
+ will be rounded down to a supported value.
+
+ minimum: 10000
+ maximum: 400000
+
+ required:
+ - reg
+ - led-sources
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flash-led-controller@63 {
+ compatible = "ti,lm3643";
+ reg = <0x63>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+ led-sources = <0>, <1>;
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_IR>;
+ flash-max-microamp = <750000>;
+ led-max-microamp = <180000>;
+ flash-max-timeout-us = <150000>;
+ };
+ };
+ };
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [RFC PATCH 2/2] leds: flash: Add support for the TI LM3643 dual LED flash driver
2026-08-21 8:36 [RFC PATCH 0/2] leds: flash: LM3643 dual LED flash driver Rillian Grant
2026-08-21 8:36 ` [RFC PATCH 1/2] dt-bindings: leds: Document TI " Rillian Grant
@ 2026-08-21 8:36 ` Rillian Grant
2026-08-21 8:46 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Rillian Grant @ 2026-08-21 8:36 UTC (permalink / raw)
To: lee, pavel, linux-leds; +Cc: Rillian Grant, linux-kernel
The LM3643 is an I2C dual LED flash driver. Its two high-side current
sources share a synchronous boost converter and each supports torch and
flash modes with programmable current and flash timeout alongside fault
reporting.
It also supports connecting both sources together to supply one LED with
current up to the boost converter's 1.5A maximum. In that case the current
will be split between each source symmetrically and the sources will be
enabled and disabled at the same time.
As strobing is toggled chip-wide the driver returns -EBUSY when a sibling
LED is active.
Flash timeouts do not have a constant step. The driver advertises the most
granular step and rounds down to the nearest supported value.
Register behavior was taken from the datasheet (SNVS967A).
Signed-off-by: Rillian Grant <rillian.grant@gmail.com>
---
MAINTAINERS | 7 +
drivers/leds/flash/Kconfig | 12 +
drivers/leds/flash/Makefile | 1 +
drivers/leds/flash/leds-lm3643.c | 775 +++++++++++++++++++++++++++++++
4 files changed, 795 insertions(+)
create mode 100644 drivers/leds/flash/leds-lm3643.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 040df7bfb24f..5aa66c3f3237 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26791,6 +26791,13 @@ F: include/linux/dma/k3-udma-glue.h
F: include/linux/dma/ti-cppi5.h
X: drivers/dma/ti/cppi41.c
+TEXAS INSTRUMENTS LM3643 LED FLASH DRIVER
+M: Rillian Grant <rillian.grant@gmail.com>
+L: linux-leds@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/leds/ti,lm3643.yaml
+F: drivers/leds/flash/leds-lm3643.c
+
TEXAS INSTRUMENTS TPS25990 HARDWARE MONITOR DRIVER
M: Jerome Brunet <jbrunet@baylibre.com>
L: linux-hwmon@vger.kernel.org
diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
index 9b6dd3f1ffe8..0f0a60a902fc 100644
--- a/drivers/leds/flash/Kconfig
+++ b/drivers/leds/flash/Kconfig
@@ -39,6 +39,18 @@ config LEDS_LM3601X
This option enables support for the TI LM3601x family
of flash, torch and indicator classes.
+config LEDS_LM3643
+ tristate "LED support for LM3643 dual LED flash driver"
+ depends on I2C
+ depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
+ select REGMAP_I2C
+ help
+ This option enables support for the Texas Instruments LM3643 dual
+ LED flash driver.
+
+ This driver can also be built as a module. If so, the module
+ will be called "leds-lm3643".
+
config LEDS_MAX77693
tristate "LED support for MAX77693 Flash"
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile
index 44e6c1b4beb3..e2e885e02123 100644
--- a/drivers/leds/flash/Makefile
+++ b/drivers/leds/flash/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
+obj-$(CONFIG_LEDS_LM3643) += leds-lm3643.o
obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o
obj-$(CONFIG_LEDS_QCOM_FLASH) += leds-qcom-flash.o
obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o
diff --git a/drivers/leds/flash/leds-lm3643.c b/drivers/leds/flash/leds-lm3643.c
new file mode 100644
index 000000000000..48e164261c98
--- /dev/null
+++ b/drivers/leds/flash/leds-lm3643.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments LM3643(A) Synchronous Boost Dual LED Flash Driver
+ *
+ * Copyright 2026 Rillian Grant <rillian.grant@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/i2c.h>
+#include <linux/led-class-flash.h>
+#include <linux/leds.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <media/v4l2-flash-led-class.h>
+
+#define LM3643_NUM_CHANNELS 2
+#define LM3643_CHAN_JOINT LM3643_NUM_CHANNELS
+
+#define LM3643_REG_ENABLE 0x01
+#define LM3643_REG_FLASH_BR_LED1 0x03
+#define LM3643_REG_FLASH_BR_LED2 0x04
+#define LM3643_REG_TORCH_BR_LED1 0x05
+#define LM3643_REG_TORCH_BR_LED2 0x06
+#define LM3643_REG_CONFIG 0x08
+#define LM3643_REG_FLAGS1 0x0A
+#define LM3643_REG_FLAGS2 0x0B
+#define LM3643_REG_DEV_ID 0x0C
+
+#define LM3643_ENABLE_LED_MASK GENMASK(1, 0)
+#define LM3643_ENABLE_LED1 BIT(0)
+#define LM3643_ENABLE_LED2 BIT(1)
+
+#define LM3643_MODE_MASK GENMASK(3, 2)
+#define LM3643_MODE_STANDBY 0x00
+#define LM3643_MODE_TORCH FIELD_PREP(LM3643_MODE_MASK, 0x2)
+#define LM3643_MODE_FLASH FIELD_PREP(LM3643_MODE_MASK, 0x3)
+
+#define LM3643_TORCH_BR_MASK GENMASK(6, 0)
+#define LM3643_TORCH_BR_LED2_OVERRIDE BIT(7)
+#define LM3643_TORCH_BR_CODE_RESET 0x3F
+#define LM3643_TORCH_BR_CODE_MAX 0x7F
+
+/* Torch: microamps = (code x 1.4) + 0.977 */
+#define LM3643_TORCH_BR_UA_OFFSET 977
+#define LM3643_TORCH_BR_UA_STEP 1400
+#define LM3643_TORCH_BR_UA_TO_CODE(ua) \
+ (((ua) - LM3643_TORCH_BR_UA_OFFSET) / LM3643_TORCH_BR_UA_STEP)
+#define LM3643_TORCH_BR_CODE_TO_UA(code) \
+ (((code) * LM3643_TORCH_BR_UA_STEP) + LM3643_TORCH_BR_UA_OFFSET)
+#define LM3643_TORCH_BR_UA_MIN LM3643_TORCH_BR_CODE_TO_UA(0)
+
+#define LM3643_FLASH_BR_MASK GENMASK(6, 0)
+#define LM3643_FLASH_BR_LED2_OVERRIDE BIT(7)
+#define LM3643_FLASH_BR_CODE_RESET 0x3F
+#define LM3643_FLASH_BR_CODE_MAX 0x7F
+
+/* Flash: microamps = (code x 11.725) + 10.9 */
+#define LM3643_FLASH_BR_UA_OFFSET 10900
+#define LM3643_FLASH_BR_UA_STEP 11725
+#define LM3643_FLASH_BR_UA_TO_CODE(ua) \
+ (((ua) - LM3643_FLASH_BR_UA_OFFSET) / LM3643_FLASH_BR_UA_STEP)
+#define LM3643_FLASH_BR_CODE_TO_UA(code) \
+ (((code) * LM3643_FLASH_BR_UA_STEP) + LM3643_FLASH_BR_UA_OFFSET)
+#define LM3643_FLASH_BR_UA_MIN LM3643_FLASH_BR_CODE_TO_UA(0)
+/* Maximum current the chip can produce across both sources */
+#define LM3643_FLASH_BR_UA_TOTAL_MAX 1500000
+
+/* CDEV brightness values are 1-indexed for use by led_classdev */
+#define LM3643_TORCH_BR_CODE_TO_CDEV(code) ((code) + 1)
+#define LM3643_TORCH_BR_CDEV_TO_CODE(brightness) ((brightness) - 1)
+#define LM3643_TORCH_BR_CDEV_MAX LM3643_TORCH_BR_CODE_TO_CDEV(LM3643_TORCH_BR_CODE_MAX)
+#define LM3643_TORCH_BR_UA_TO_CDEV(ua) \
+ LM3643_TORCH_BR_CODE_TO_CDEV(LM3643_TORCH_BR_UA_TO_CODE(ua))
+#define LM3643_TORCH_BR_CDEV_TO_UA(brightness) \
+ LM3643_TORCH_BR_CODE_TO_UA(LM3643_TORCH_BR_CDEV_TO_CODE(brightness))
+
+#define LM3643_CONFIG_FLASH_TIMEOUT_MASK GENMASK(3, 0)
+#define LM3643_CONFIG_FLASH_TIMEOUT_RESET 0xA
+
+#define LM3643_TIMEOUT_US_MIN 10000
+#define LM3643_TIMEOUT_US_STEP 10000
+#define LM3643_TIMEOUT_US_MAX 400000
+
+#define LM3643_FLAGS1_MASK GENMASK(6, 0)
+#define LM3643_FLAGS1_FAULT_TIMEOUT BIT(0)
+#define LM3643_FLAGS1_FAULT_UVLO BIT(1)
+#define LM3643_FLAGS1_FAULT_TSD BIT(2)
+#define LM3643_FLAGS1_FAULT_CURRENT_LIMIT BIT(3)
+#define LM3643_FLAGS1_FAULT_VLED2_SHORT BIT(4)
+#define LM3643_FLAGS1_FAULT_VLED1_SHORT BIT(5)
+#define LM3643_FLAGS1_FAULT_VOUT_SHORT BIT(6)
+#define LM3643_FLAGS1_LED1_FAULTS (LM3643_FLAGS1_MASK & ~LM3643_FLAGS1_FAULT_VLED2_SHORT)
+#define LM3643_FLAGS1_LED2_FAULTS (LM3643_FLAGS1_MASK & ~LM3643_FLAGS1_FAULT_VLED1_SHORT)
+
+#define LM3643_FLAGS2_MASK GENMASK(2, 0)
+#define LM3643_FLAGS2_FAULT_TEMP BIT(0)
+#define LM3643_FLAGS2_FAULT_OVP BIT(1)
+#define LM3643_FLAGS2_FAULT_IVFM_TRIP BIT(2)
+
+#define LM3643_FAULTS_ALL ( \
+ LED_FAULT_TIMEOUT \
+ | LED_FAULT_UNDER_VOLTAGE \
+ | LED_FAULT_OVER_TEMPERATURE \
+ | LED_FAULT_OVER_CURRENT \
+ | LED_FAULT_SHORT_CIRCUIT \
+ | LED_FAULT_LED_OVER_TEMPERATURE \
+ | LED_FAULT_OVER_VOLTAGE \
+ | LED_FAULT_INPUT_VOLTAGE \
+)
+
+#define LM3643_DEV_ID_MASK GENMASK(5, 3)
+#define LM3643_DEV_ID 0x00
+
+struct lm3643_chan {
+ u8 enable_bit;
+ u8 torch_br_reg;
+ u8 flash_br_reg;
+ u8 flags1_faults;
+ u8 num_sources;
+};
+
+static const struct lm3643_chan lm3643_chans[LM3643_NUM_CHANNELS + 1] = {
+ {
+ .enable_bit = LM3643_ENABLE_LED1,
+ .torch_br_reg = LM3643_REG_TORCH_BR_LED1,
+ .flash_br_reg = LM3643_REG_FLASH_BR_LED1,
+ .flags1_faults = LM3643_FLAGS1_LED1_FAULTS,
+ .num_sources = 1,
+ },
+ {
+ .enable_bit = LM3643_ENABLE_LED2,
+ .torch_br_reg = LM3643_REG_TORCH_BR_LED2,
+ .flash_br_reg = LM3643_REG_FLASH_BR_LED2,
+ .flags1_faults = LM3643_FLAGS1_LED2_FAULTS,
+ .num_sources = 1,
+ },
+ [LM3643_CHAN_JOINT] = {
+ .enable_bit = LM3643_ENABLE_LED_MASK,
+ .torch_br_reg = LM3643_REG_TORCH_BR_LED1,
+ .flash_br_reg = LM3643_REG_FLASH_BR_LED1,
+ .flags1_faults = LM3643_FLAGS1_MASK,
+ .num_sources = LM3643_NUM_CHANNELS,
+ },
+};
+
+static const struct led_flash_setting lm3643_flash_br_setting = {
+ .max = LM3643_FLASH_BR_CODE_TO_UA(LM3643_FLASH_BR_CODE_MAX),
+ .min = LM3643_FLASH_BR_UA_MIN,
+ .step = LM3643_FLASH_BR_UA_STEP,
+ .val = LM3643_FLASH_BR_CODE_TO_UA(LM3643_FLASH_BR_CODE_RESET),
+};
+
+static const u32 lm3643_timeout_us[] = {
+ LM3643_TIMEOUT_US_MIN,
+ 20000,
+ 30000,
+ 40000,
+ 50000,
+ 60000,
+ 70000,
+ 80000,
+ 90000,
+ 100000,
+ 150000,
+ 200000,
+ 250000,
+ 300000,
+ 350000,
+ LM3643_TIMEOUT_US_MAX,
+};
+
+static_assert(ARRAY_SIZE(lm3643_timeout_us) == LM3643_CONFIG_FLASH_TIMEOUT_MASK + 1);
+
+static unsigned int lm3643_timeout_to_code(u32 timeout)
+{
+ unsigned int i;
+
+ for (i = ARRAY_SIZE(lm3643_timeout_us) - 1; i > 0; i--)
+ if (timeout >= lm3643_timeout_us[i])
+ break;
+
+ return i;
+}
+
+/* The chip's timeouts step by 10 ms up to 100 ms and then by 50 ms. */
+static const struct led_flash_setting lm3643_flash_time_setting = {
+ .max = LM3643_TIMEOUT_US_MAX,
+ .min = LM3643_TIMEOUT_US_MIN,
+ .step = LM3643_TIMEOUT_US_STEP,
+ .val = lm3643_timeout_us[LM3643_CONFIG_FLASH_TIMEOUT_RESET],
+};
+
+struct lm3643_led {
+ struct lm3643 *chip;
+ struct regmap *regmap;
+ struct led_classdev_flash flash_cdev;
+ struct v4l2_flash *v4l2_flash;
+
+ const struct lm3643_chan *chan;
+
+ u8 flags1, flags2;
+};
+
+struct lm3643 {
+ struct regmap *regmap;
+ /* Synchronizes access to enable and flag registers */
+ struct mutex lock;
+ struct lm3643_led leds[LM3643_NUM_CHANNELS];
+ unsigned int leds_active;
+};
+
+static enum led_brightness lm3643_torch_get_brightness(struct led_classdev *led_cdev)
+{
+ struct lm3643_led *led = container_of(lcdev_to_flcdev(led_cdev),
+ struct lm3643_led,
+ flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+ unsigned int brightness;
+ unsigned int enable;
+ int ret;
+
+ guard(mutex)(&led->chip->lock);
+
+ ret = regmap_read(led->regmap, LM3643_REG_ENABLE, &enable);
+ if (ret) {
+ dev_err(led_cdev->dev, "failed to get enable register\n");
+ return LED_OFF;
+ }
+ enable &= LM3643_MODE_MASK | chan->enable_bit;
+ if (enable != (LM3643_MODE_TORCH | chan->enable_bit))
+ return LED_OFF;
+
+ ret = regmap_read(led->regmap, chan->torch_br_reg, &brightness);
+ if (ret) {
+ dev_err(led_cdev->dev,
+ "failed to get LED brightness register 0x%02x\n",
+ chan->torch_br_reg);
+ return LED_OFF;
+ }
+ brightness &= LM3643_TORCH_BR_MASK;
+
+ return LM3643_TORCH_BR_CODE_TO_CDEV(brightness);
+}
+
+static int lm3643_torch_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct lm3643_led *led = container_of(lcdev_to_flcdev(led_cdev),
+ struct lm3643_led, flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+ struct lm3643 *chip = led->chip;
+ unsigned int sibling_bit, keep, enable, mode;
+ int ret;
+
+ guard(mutex)(&chip->lock);
+
+ ret = regmap_read(led->regmap, LM3643_REG_ENABLE, &enable);
+ if (ret)
+ return ret;
+ sibling_bit = LM3643_ENABLE_LED_MASK & ~chan->enable_bit;
+
+ mode = enable & LM3643_MODE_MASK;
+ if ((enable & sibling_bit) &&
+ mode != LM3643_MODE_STANDBY &&
+ mode != LM3643_MODE_TORCH)
+ return -EBUSY;
+
+ if (brightness == 0)
+ return regmap_update_bits(led->regmap, LM3643_REG_ENABLE,
+ chan->enable_bit, 0);
+
+ ret = regmap_update_bits(led->regmap, chan->torch_br_reg, LM3643_TORCH_BR_MASK,
+ LM3643_TORCH_BR_CDEV_TO_CODE(brightness));
+ if (ret)
+ return ret;
+
+ keep = (enable & LM3643_MODE_MASK) == LM3643_MODE_TORCH
+ ? enable & sibling_bit
+ : 0;
+
+ return regmap_update_bits(led->regmap,
+ LM3643_REG_ENABLE,
+ LM3643_MODE_MASK | LM3643_ENABLE_LED_MASK,
+ LM3643_MODE_TORCH | chan->enable_bit | keep);
+}
+
+static int lm3643_flash_brightness_get(struct led_classdev_flash *fled_cdev, u32 *brightness)
+{
+ struct lm3643_led *led = container_of(fled_cdev,
+ struct lm3643_led,
+ flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+ int ret;
+
+ ret = regmap_read(led->regmap, chan->flash_br_reg, brightness);
+ if (ret)
+ return ret;
+
+ *brightness &= LM3643_FLASH_BR_MASK;
+ *brightness = LM3643_FLASH_BR_CODE_TO_UA(*brightness);
+ *brightness *= chan->num_sources;
+
+ return 0;
+}
+
+static int lm3643_flash_brightness_set(struct led_classdev_flash *fled_cdev, u32 brightness)
+{
+ struct lm3643_led *led = container_of(fled_cdev, struct lm3643_led, flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+
+ brightness /= chan->num_sources;
+ brightness = LM3643_FLASH_BR_UA_TO_CODE(brightness);
+
+ return regmap_update_bits(led->regmap,
+ chan->flash_br_reg,
+ LM3643_FLASH_BR_MASK,
+ brightness);
+}
+
+static int lm3643_flash_timeout_set(struct led_classdev_flash *fled_cdev, u32 timeout)
+{
+ unsigned int code = lm3643_timeout_to_code(timeout);
+
+ fled_cdev->timeout.val = lm3643_timeout_us[code];
+
+ return 0;
+}
+
+static int lm3643_flash_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
+{
+ struct lm3643_led *led = container_of(fled_cdev, struct lm3643_led, flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+ unsigned int mode;
+ int ret;
+
+ ret = regmap_read(led->regmap, LM3643_REG_ENABLE, &mode);
+ if (ret)
+ return ret;
+
+ *state = (mode & (LM3643_MODE_MASK | chan->enable_bit)) ==
+ (LM3643_MODE_FLASH | chan->enable_bit);
+
+ return 0;
+}
+
+static int lm3643_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
+{
+ struct lm3643_led *led = container_of(fled_cdev, struct lm3643_led, flash_cdev);
+ const struct lm3643_chan *chan = led->chan;
+ unsigned int enable_reg, sibling_bit, mode;
+ int ret;
+
+ guard(mutex)(&led->chip->lock);
+
+ ret = regmap_read(led->regmap, LM3643_REG_ENABLE, &enable_reg);
+ if (ret)
+ return ret;
+ sibling_bit = LM3643_ENABLE_LED_MASK & ~led->chan->enable_bit;
+ mode = enable_reg & LM3643_MODE_MASK;
+
+ if (!state) {
+ if (mode != LM3643_MODE_FLASH || !(enable_reg & chan->enable_bit))
+ return 0;
+
+ return regmap_update_bits(led->regmap, LM3643_REG_ENABLE,
+ LM3643_MODE_MASK, LM3643_MODE_STANDBY);
+ }
+ if ((enable_reg & sibling_bit) && mode != LM3643_MODE_STANDBY)
+ return -EBUSY;
+
+ ret = regmap_update_bits(led->regmap,
+ LM3643_REG_CONFIG,
+ LM3643_CONFIG_FLASH_TIMEOUT_MASK,
+ lm3643_timeout_to_code(fled_cdev->timeout.val));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(led->regmap,
+ LM3643_REG_ENABLE,
+ LM3643_MODE_MASK | LM3643_ENABLE_LED_MASK,
+ LM3643_MODE_FLASH | chan->enable_bit);
+}
+
+static unsigned int lm3643_decode_faults(u8 flags1, u8 flags2)
+{
+ unsigned int faults = 0;
+
+ if (flags1 & LM3643_FLAGS1_FAULT_TIMEOUT)
+ faults |= LED_FAULT_TIMEOUT;
+ if (flags1 & LM3643_FLAGS1_FAULT_UVLO)
+ faults |= LED_FAULT_UNDER_VOLTAGE;
+ if (flags1 & LM3643_FLAGS1_FAULT_TSD)
+ faults |= LED_FAULT_OVER_TEMPERATURE;
+ if (flags1 & LM3643_FLAGS1_FAULT_CURRENT_LIMIT)
+ faults |= LED_FAULT_OVER_CURRENT;
+
+ /*
+ * The caller has already masked off the sibling channel's short flag,
+ * so all three short conditions collapse to the one generic fault.
+ */
+ if (flags1 & (LM3643_FLAGS1_FAULT_VLED1_SHORT |
+ LM3643_FLAGS1_FAULT_VLED2_SHORT |
+ LM3643_FLAGS1_FAULT_VOUT_SHORT))
+ faults |= LED_FAULT_SHORT_CIRCUIT;
+
+ /* TSD is the die tripping at 150C; TEMP is the external NTC at the LED. */
+ if (flags2 & LM3643_FLAGS2_FAULT_TEMP)
+ faults |= LED_FAULT_LED_OVER_TEMPERATURE;
+ if (flags2 & LM3643_FLAGS2_FAULT_OVP)
+ faults |= LED_FAULT_OVER_VOLTAGE;
+ if (flags2 & LM3643_FLAGS2_FAULT_IVFM_TRIP)
+ faults |= LED_FAULT_INPUT_VOLTAGE;
+
+ return faults;
+}
+
+static int lm3643_fault_get(struct led_classdev_flash *fled_cdev, u32 *fault)
+{
+ struct lm3643_led *led = container_of(fled_cdev, struct lm3643_led, flash_cdev);
+ struct lm3643 *chip = led->chip;
+ u32 flags1, flags2;
+ int ret;
+
+ guard(mutex)(&chip->lock);
+
+ ret = regmap_read(chip->regmap, LM3643_REG_FLAGS1, &flags1);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(chip->regmap, LM3643_REG_FLAGS2, &flags2);
+ if (ret)
+ return ret;
+
+ flags1 &= LM3643_FLAGS1_MASK;
+ flags2 &= LM3643_FLAGS2_MASK;
+
+ for (int i = 0; i < LM3643_NUM_CHANNELS; i++) {
+ struct lm3643_led *sibling = &chip->leds[i];
+
+ if (!sibling->chan)
+ continue;
+
+ sibling->flags1 |= flags1 & sibling->chan->flags1_faults;
+ sibling->flags2 |= flags2;
+ }
+
+ *fault = lm3643_decode_faults(led->flags1, led->flags2);
+ led->flags1 = 0;
+ led->flags2 = 0;
+
+ return 0;
+}
+
+static const struct led_flash_ops lm3643_flash_ops = {
+ .strobe_get = lm3643_flash_strobe_get,
+ .strobe_set = lm3643_flash_strobe_set,
+ .flash_brightness_get = lm3643_flash_brightness_get,
+ .flash_brightness_set = lm3643_flash_brightness_set,
+ .timeout_set = lm3643_flash_timeout_set,
+ .fault_get = lm3643_fault_get,
+};
+
+static const struct regmap_config lm3643_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0D,
+ .cache_type = REGCACHE_NONE,
+};
+
+static void lm3643_v4l2_release(void *v4l2_flash)
+{
+ v4l2_flash_release(v4l2_flash);
+}
+
+static void lm3643_standby(void *regmap)
+{
+ regmap_write(regmap, LM3643_REG_ENABLE, LM3643_MODE_STANDBY);
+}
+
+static void lm3643_scale_flash_setting_microamps(struct led_flash_setting *setting, u8 num_sources)
+{
+ setting->max *= num_sources;
+ setting->min *= num_sources;
+ setting->step *= num_sources;
+ setting->val *= num_sources;
+}
+
+static struct lm3643_led *lm3643_claim_channels(struct device *dev, struct lm3643 *chip,
+ struct fwnode_handle *fwnode)
+{
+ u32 sources[LM3643_NUM_CHANNELS];
+ struct lm3643_led *led;
+ int num_sources;
+ int ret;
+
+ num_sources = fwnode_property_count_u32(fwnode, "led-sources");
+ if (num_sources < 0)
+ return ERR_PTR(dev_err_probe(dev, num_sources,
+ "failed to read led-sources property\n"));
+ if (num_sources < 1 || num_sources > LM3643_NUM_CHANNELS)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "led-sources has %d entries, expected 1 to %d\n",
+ num_sources, LM3643_NUM_CHANNELS));
+
+ ret = fwnode_property_read_u32_array(fwnode, "led-sources", sources, num_sources);
+ if (ret)
+ return ERR_PTR(dev_err_probe(dev, ret,
+ "failed to read led-sources property\n"));
+
+ for (int i = 0; i < num_sources; i++) {
+ if (sources[i] >= LM3643_NUM_CHANNELS)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "led-sources entry %u exceeds the %d current outputs\n",
+ sources[i], LM3643_NUM_CHANNELS));
+ if (chip->leds_active & BIT(sources[i]))
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "current output %u claimed more than once\n", sources[i]));
+
+ chip->leds_active |= BIT(sources[i]);
+ }
+
+ if (num_sources == LM3643_NUM_CHANNELS) {
+ led = &chip->leds[0];
+ led->chan = &lm3643_chans[LM3643_CHAN_JOINT];
+ } else {
+ led = &chip->leds[sources[0]];
+ led->chan = &lm3643_chans[sources[0]];
+ }
+
+ led->chip = chip;
+ led->regmap = chip->regmap;
+
+ return led;
+}
+
+static int lm3643_apply_fw_limits(struct device *dev, struct lm3643_led *led,
+ struct fwnode_handle *fwnode)
+{
+ struct led_classdev_flash *flash_cdev = &led->flash_cdev;
+ struct led_classdev *led_cdev = &flash_cdev->led_cdev;
+ u8 num_sources = led->chan->num_sources;
+ u32 max_us, max_torch_ua, max_flash_ua;
+
+ if (!fwnode_property_read_u32(fwnode, "flash-max-microamp", &max_flash_ua)) {
+ if (max_flash_ua < LM3643_FLASH_BR_UA_MIN * num_sources)
+ return dev_err_probe(dev, -EINVAL,
+ "flash-max-microamp %u is below the %u uA minimum\n",
+ max_flash_ua, LM3643_FLASH_BR_UA_MIN * num_sources);
+
+ /* Rounded down to a supported value. */
+ flash_cdev->brightness.max = min(flash_cdev->brightness.max, max_flash_ua);
+ }
+
+ if (!fwnode_property_read_u32(fwnode, "led-max-microamp", &max_torch_ua)) {
+ if (max_torch_ua < LM3643_TORCH_BR_UA_MIN * num_sources)
+ return dev_err_probe(dev, -EINVAL,
+ "led-max-microamp %u is below the %u uA minimum\n",
+ max_torch_ua, LM3643_TORCH_BR_UA_MIN * num_sources);
+
+ led_cdev->max_brightness = min(led_cdev->max_brightness,
+ LM3643_TORCH_BR_UA_TO_CDEV(max_torch_ua /
+ num_sources));
+ }
+
+ if (!fwnode_property_read_u32(fwnode, "flash-max-timeout-us", &max_us)) {
+ if (max_us < LM3643_TIMEOUT_US_MIN)
+ return dev_err_probe(dev, -EINVAL,
+ "flash-max-timeout-us %u below the %u us minimum\n",
+ max_us, LM3643_TIMEOUT_US_MIN);
+
+ flash_cdev->timeout.max = lm3643_timeout_us[lm3643_timeout_to_code(max_us)];
+ }
+
+ return 0;
+}
+
+static int lm3643_register_v4l2(struct device *dev, struct lm3643_led *led,
+ struct fwnode_handle *fwnode)
+{
+ struct led_classdev *led_cdev = &led->flash_cdev.led_cdev;
+ struct v4l2_flash_config v4l2_flash_config = {};
+
+ strscpy(v4l2_flash_config.dev_name, dev_name(led_cdev->dev),
+ sizeof(v4l2_flash_config.dev_name));
+ v4l2_flash_config.flash_faults = LM3643_FAULTS_ALL;
+ v4l2_flash_config.has_external_strobe = false;
+ v4l2_flash_config.intensity.min = LM3643_TORCH_BR_UA_MIN;
+ v4l2_flash_config.intensity.step = LM3643_TORCH_BR_UA_STEP;
+ v4l2_flash_config.intensity.max = LM3643_TORCH_BR_CDEV_TO_UA(led_cdev->max_brightness);
+ v4l2_flash_config.intensity.val =
+ min_t(u32, LM3643_TORCH_BR_CODE_TO_UA(LM3643_TORCH_BR_CODE_RESET),
+ v4l2_flash_config.intensity.max);
+
+ lm3643_scale_flash_setting_microamps(&v4l2_flash_config.intensity, led->chan->num_sources);
+
+ led->v4l2_flash = v4l2_flash_init(dev, fwnode, &led->flash_cdev, NULL,
+ &v4l2_flash_config);
+ if (IS_ERR(led->v4l2_flash))
+ return dev_err_probe(dev,
+ PTR_ERR(led->v4l2_flash),
+ "failed to register v4l2 flash\n");
+
+ return devm_add_action_or_reset(dev, lm3643_v4l2_release, led->v4l2_flash);
+}
+
+static int lm3643_register_led(struct device *dev, struct lm3643 *chip,
+ struct fwnode_handle *fwnode)
+{
+ struct led_init_data init_data = { .fwnode = fwnode };
+ struct led_classdev_flash *flash_cdev;
+ struct led_classdev *led_cdev;
+ struct lm3643_led *led;
+ u32 torch_max_brightness;
+ int ret;
+
+ led = lm3643_claim_channels(dev, chip, fwnode);
+ if (IS_ERR(led))
+ return PTR_ERR(led);
+
+ flash_cdev = &led->flash_cdev;
+ led_cdev = &flash_cdev->led_cdev;
+
+ if (led->chan->num_sources == LM3643_NUM_CHANNELS) {
+ /* Bit 7 of the LED1 brightness registers override LED2 with the same values. */
+ ret = regmap_update_bits(chip->regmap, LM3643_REG_TORCH_BR_LED1,
+ LM3643_TORCH_BR_LED2_OVERRIDE,
+ LM3643_TORCH_BR_LED2_OVERRIDE);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(chip->regmap, LM3643_REG_FLASH_BR_LED1,
+ LM3643_FLASH_BR_LED2_OVERRIDE,
+ LM3643_FLASH_BR_LED2_OVERRIDE);
+ if (ret)
+ return ret;
+ }
+
+ flash_cdev->brightness = lm3643_flash_br_setting;
+ lm3643_scale_flash_setting_microamps(&flash_cdev->brightness, led->chan->num_sources);
+ flash_cdev->timeout = lm3643_flash_time_setting;
+ flash_cdev->ops = &lm3643_flash_ops;
+
+ led_cdev->flags |= LED_DEV_CAP_FLASH;
+ led_cdev->max_brightness = LM3643_TORCH_BR_CDEV_MAX;
+ led_cdev->brightness_set_blocking = lm3643_torch_set_brightness;
+ led_cdev->brightness_get = lm3643_torch_get_brightness;
+
+ ret = lm3643_apply_fw_limits(dev, led, fwnode);
+ if (ret)
+ return ret;
+
+ torch_max_brightness = led_cdev->max_brightness;
+
+ flash_cdev->brightness.max = min(flash_cdev->brightness.max, LM3643_FLASH_BR_UA_TOTAL_MAX);
+ flash_cdev->brightness.val = min(flash_cdev->brightness.val, flash_cdev->brightness.max);
+ flash_cdev->timeout.val = min(flash_cdev->timeout.val, flash_cdev->timeout.max);
+
+ ret = lm3643_flash_brightness_set(flash_cdev, flash_cdev->brightness.val);
+ if (ret)
+ return ret;
+
+ ret = devm_led_classdev_flash_register_ext(dev, flash_cdev, &init_data);
+ if (ret)
+ return ret;
+
+ if (led_cdev->max_brightness > torch_max_brightness) {
+ dev_warn(dev, "max-brightness %u not supported (using %u)\n",
+ led_cdev->max_brightness,
+ torch_max_brightness);
+ led_cdev->max_brightness = torch_max_brightness;
+ }
+
+ return lm3643_register_v4l2(dev, led, fwnode);
+}
+
+static int lm3643_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lm3643 *chip;
+ unsigned int dev_id;
+ unsigned int count;
+ int ret;
+
+ count = device_get_child_node_count(dev);
+ if (!count || count > LM3643_NUM_CHANNELS)
+ return dev_err_probe(dev, -EINVAL, "%u LED nodes found, expected 1 to %d\n",
+ count, LM3643_NUM_CHANNELS);
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init_i2c(client, &lm3643_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return dev_err_probe(dev,
+ PTR_ERR(chip->regmap),
+ "failed to allocate register map\n");
+
+ ret = regmap_read(chip->regmap, LM3643_REG_DEV_ID, &dev_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to read device ID\n");
+
+ dev_id &= LM3643_DEV_ID_MASK;
+ if (dev_id != LM3643_DEV_ID)
+ return dev_err_probe(dev, -ENODEV, "wrong chip id 0x%02x (expected 0x%02x)\n",
+ dev_id, LM3643_DEV_ID);
+
+ ret = devm_mutex_init(dev, &chip->lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to create mutex\n");
+
+ ret = regmap_write(chip->regmap, LM3643_REG_ENABLE, LM3643_MODE_STANDBY);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to put device in standby mode\n");
+ ret = devm_add_action_or_reset(dev, lm3643_standby, chip->regmap);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(chip->regmap, LM3643_REG_TORCH_BR_LED1,
+ LM3643_TORCH_BR_LED2_OVERRIDE,
+ 0);
+ if (ret)
+ return dev_err_probe(dev,
+ ret,
+ "failed to clear LED2 torch current override register\n");
+
+ ret = regmap_update_bits(chip->regmap, LM3643_REG_FLASH_BR_LED1,
+ LM3643_FLASH_BR_LED2_OVERRIDE,
+ 0);
+ if (ret)
+ return dev_err_probe(dev,
+ ret,
+ "failed to clear LED2 flash current override register\n");
+
+ device_for_each_child_node_scoped(dev, child) {
+ ret = lm3643_register_led(dev, chip, child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct acpi_device_id lm3643_acpi_leds_match[] = {
+ { "TXNW3643" },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, lm3643_acpi_leds_match);
+
+static const struct of_device_id lm3643_of_leds_match[] = {
+ { .compatible = "ti,lm3643" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm3643_of_leds_match);
+
+static struct i2c_driver lm3643_i2c_driver = {
+ .driver = {
+ .name = "lm3643",
+ .acpi_match_table = lm3643_acpi_leds_match,
+ .of_match_table = lm3643_of_leds_match,
+ },
+ .probe = lm3643_probe,
+};
+module_i2c_driver(lm3643_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3643 LED Flash Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rillian Grant <rillian.grant@gmail.com>");
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 1/2] dt-bindings: leds: Document TI LM3643 dual LED flash driver
2026-08-21 8:36 ` [RFC PATCH 1/2] dt-bindings: leds: Document TI " Rillian Grant
@ 2026-08-21 8:44 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-21 8:44 UTC (permalink / raw)
To: Rillian Grant; +Cc: devicetree, robh, lee, linux-leds, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The schema does not enforce `flash-max-microamp`, `flash-max-timeout-us`, and `led-max-microamp` as required properties for the LED child node.
--
--- Patch [1]: [RFC PATCH 1/2] dt-bindings: leds: Document TI LM3643 dual LED flash driver ---
commit 0c56365dce9a84b7387d67d8621b8beeacea7aec
Author: Rillian Grant <rillian.grant@gmail.com>
dt-bindings: leds: Document TI LM3643 dual LED flash driver
This commit documents the devicetree bindings for the Texas Instruments LM3643
dual LED flash driver, which uses a synchronous boost converter to power up to
two current sources up to 1.5A.
> diff --git a/Documentation/devicetree/bindings/leds/ti,lm3643.yaml b/Documentation/devicetree/bindings/leds/ti,lm3643.yaml
> new file mode 100644
> index 0000000000000..18b08a79512f1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/ti,lm3643.yaml
[ ... ]
> +patternProperties:
> + "^led@[0-1]$":
> + type: object
> + $ref: common.yaml#
> + unevaluatedProperties: false
[ ... ]
> + flash-max-timeout-us:
> + description: |
> + The chip supports timeouts from 10ms to 100ms in increments of 10ms
> + and from 100ms to 400ms in increments of 50ms. The provided value
> + will be rounded down to a supported value.
> +
> + minimum: 10000
> + maximum: 400000
> +
> + required:
> + - reg
> + - led-sources
[Severity: High]
Should flash-max-microamp, flash-max-timeout-us, and led-max-microamp be
included here as required properties for the LED child node?
Without these properties enforced by the schema, a device tree could omit them
and still pass validation. If omitted, will driver fallback to the controller's
absolute maximums (1.5A and 400ms) risk physically damaging connected LEDs that
have lower electrical limits?
> +
> +required:
> + - compatible
> + - reg
> + - "#address-cells"
> + - "#size-cells"
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260821083620.68324-1-rillian.grant@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 2/2] leds: flash: Add support for the TI LM3643 dual LED flash driver
2026-08-21 8:36 ` [RFC PATCH 2/2] leds: flash: Add support for the " Rillian Grant
@ 2026-08-21 8:46 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-21 8:46 UTC (permalink / raw)
To: Rillian Grant; +Cc: linux-leds, lee
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Turning off an LED fails to clear the chip mode bits, causing power drain and breaking flash strobe teardown.
--
--- Patch [2]: [RFC PATCH 2/2] leds: flash: Add support for the TI LM3643 dual LED flash driver ---
commit bdf1945ab184ca005c2f401ecd5408571ece1de3
Author: Rillian Grant <rillian.grant@gmail.com>
leds: flash: Add support for the TI LM3643 dual LED flash driver
The LM3643 is an I2C dual LED flash driver. Its two high-side current
sources share a synchronous boost converter and each supports torch and
flash modes with programmable current and flash timeout alongside fault
reporting.
> diff --git a/drivers/leds/flash/leds-lm3643.c b/drivers/leds/flash/leds-lm3643.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/leds/flash/leds-lm3643.c
[ ... ]
> +static int lm3643_torch_set_brightness(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
[ ... ]
> + if (brightness == 0)
> + return regmap_update_bits(led->regmap, LM3643_REG_ENABLE,
> + chan->enable_bit, 0);
[Severity: High]
When brightness is 0, this only clears the channel's enable bit but leaves
the LM3643_MODE_MASK bits intact. If no other LEDs are active, does this
leave the chip in an active power state (Torch or Flash mode) instead of
transitioning to LM3643_MODE_STANDBY, potentially causing continuous battery
drain?
[ ... ]
> +static int lm3643_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
> +{
[ ... ]
> + if (!state) {
> + if (mode != LM3643_MODE_FLASH || !(enable_reg & chan->enable_bit))
> + return 0;
> +
> + return regmap_update_bits(led->regmap, LM3643_REG_ENABLE,
> + LM3643_MODE_MASK, LM3643_MODE_STANDBY);
> + }
[Severity: High]
If the torch brightness was set to 0 during a flash, the channel's enable
bit will have been cleared. During the subsequent teardown call to
lm3643_flash_strobe_set() with state = false, does this early return trigger
because the enable bit is no longer set?
This would fail to restore the mode to LM3643_MODE_STANDBY, permanently
stranding the driver's state machine in Flash mode.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260821083620.68324-1-rillian.grant@gmail.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-21 8:46 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 8:36 [RFC PATCH 0/2] leds: flash: LM3643 dual LED flash driver Rillian Grant
2026-08-21 8:36 ` [RFC PATCH 1/2] dt-bindings: leds: Document TI " Rillian Grant
2026-08-21 8:44 ` sashiko-bot
2026-08-21 8:36 ` [RFC PATCH 2/2] leds: flash: Add support for the " Rillian Grant
2026-08-21 8:46 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox