The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling
@ 2026-06-05 13:59 Manuel Fombuena
  2026-06-05 14:03 ` [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming Manuel Fombuena
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 13:59 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

This series fixes several bugs in the LED1202 driver related to hardware
pattern programming and brightness control. The issues were found during
testing on a Linksys MX4200v2 router running OpenWrt.

--- Pattern sequence not stopped before reprogramming ---

The LED1202 datasheet (section 4.8) states that writes to PAT_REP and
pattern duration registers are only applied after the sequence completes
or is stopped. When running in infinite loop mode the sequence never
completes on its own, so these writes are silently ignored by the
hardware.

  Patch 1: Stop the running sequence by clearing PATS in the
  configuration register at the start of both pattern_clear() and
  pattern_set(), ensuring the hardware accepts new values immediately.

  Patch 2: Validate pattern input before stopping the sequence. An
  out-of-range duration value should be rejected without disrupting a
  running pattern, so input validation is moved ahead of the sequence
  stop.

--- pattern_clear() leaving hardware in inconsistent state ---

Several independent bugs in pattern_clear() left the hardware in a state
that affected subsequent pattern or brightness operations.

  Patch 3: Fix the duration prescaler formula and the skip marker written
  by pattern_clear(). The original formula (value / ST1202_MILLIS_PATTERN_DUR_MIN
  - 1) was off by one, producing durations ~22ms too short. Additionally,
  pattern_clear() relied on the broken formula to produce register value 0
  (the pattern skip marker) by passing the minimum duration. With the formula
  corrected, pattern_clear() now writes 0 directly to unused duration
  registers instead of going through the conversion function.

  Patch 4: Restore Pattern0 PWM to full brightness (0x0FFF) after clearing.
  pattern_clear() zeroes all PWM slots as part of the clear, but leaves
  Pattern0 at zero, so a subsequent direct brightness write has no visible
  effect until Pattern0 PWM is restored.

--- Spurious pattern sequence start during setup ---

  Patch 5: Remove the write of PATS|PATSR to the configuration register
  in st1202_setup(). This accidentally started a pattern sequence before
  any pattern data was programmed, producing undefined output on startup.

--- Brightness control broken while pattern mode is active ---

  Patch 6: Exit pattern mode in brightness_set() before writing the ILED
  register. With PATS set, the LED output is determined by the pattern
  engine regardless of the ILED value, making direct brightness writes
  have no visible effect while a pattern is active.

  Patch 7: Disable the hardware channel in brightness_set() when value
  is zero. Previously only the ILED DAC was zeroed while the channel
  remained enabled, causing residual current through the enabled channel
  and a visible dim glow on the LED.

--- Input validation ---

  Patch 8: Validate the reg property read from the device tree before
  using it as an array index into chip->leds[]. A value >= ST1202_MAX_LEDS
  would cause an out-of-bounds write during probe.

--- Documentation ---

  Patch 9: Correct and extend the hw_pattern documentation. The maximum
  pattern duration was stated as 5660ms but the correct value derived
  from the prescaler formula is 5610ms. The repeat field documentation
  is also corrected and the brightness range is made explicit.

--- Testing ---

Tested on LED1202 hardware via I2C. Register state verified with i2cget
at each step. Correct LED behaviour confirmed across pattern cycling,
infinite repeat, pattern_clear, and direct brightness control with
trigger=none.

--- Changes in v3 ---

  In response to automated review feedback (Sashiko):

  Patch 1: Extend commit message to clarify that the LED1202 has a single
  global pattern sequencer shared across all channels, and that stopping
  it in pattern_clear() is therefore an inherent hardware constraint rather
  than a deliberate design choice.

  Patches 3+4: Squashed into a single patch. The prescaler fix and the
  skip marker fix are tightly coupled — the skip marker bug was a direct
  consequence of the broken formula — and are clearer as one change.

  Patch 5 (v2): Dropped. Resetting PAT_REP in pattern_clear() is
  unnecessary because pattern_set() always stops the sequencer and writes
  its own PAT_REP value before restarting. The reset introduced a spurious
  failure point without fixing a real bug.

  Patch 4 (was 6): Start the clearing loop at Pattern1 to avoid writing
  Pattern0 twice (the loop previously zeroed it before the explicit full
  restore).

  Patch 5 (was 7): Simplify commit message — remove inaccurate claim that
  the SHFT bit is never re-enabled after probe; pattern_clear() restores
  it during probe.

  New patch 8: Validate the reg device tree property against ST1202_MAX_LEDS
  before using it as an array index.

  Other pre-existing issues identified by the automated review (global
  sequencer coordination, brightness_set sleeping in atomic context,
  brightness_set_blocking ignoring the brightness value) are outside the
  scope of this fix series and will be addressed in a follow-up submission.

--- Changes in v2 ---

  Patch 3: Fix commit message wording — programmed durations were ~22ms
  too short, not too long.
  Patch 7: Fix Signed-off-by typo.
  Patch 10: Add missing quotes around commit subject in Fixes: tag.

v1: https://lore.kernel.org/all/WA0P291MB03850F4E9B4EC7389FE89779C5052@WA0P291MB0385.POLP291.PROD.OUTLOOK.COM/
v2: https://lore.kernel.org/all/WA0P291MB03855101311F0506B6448A8EC50D2@WA0P291MB0385.POLP291.PROD.OUTLOOK.COM/

Manuel Fombuena (9):
  leds: st1202: stop pattern sequence before reprogramming
  leds: st1202: validate pattern input before stopping the sequence
  leds: st1202: fix pattern duration prescaler and pattern_clear skip
    marker
  leds: st1202: restore Pattern0 PWM to full on after clearing pattern
  leds: st1202: fix spurious pattern sequence start in setup
  leds: st1202: fix brightness having no effect while pattern mode is
    active
  leds: st1202: disable channel when brightness is set to zero
  leds: st1202: validate LED reg property against channel count
  leds: st1202: correct and extend hw_pattern documentation

 Documentation/leds/leds-st1202.rst |   9 ++-
 drivers/leds/leds-st1202.c         | 102 ++++++++++++++++++-----------
 2 files changed, 68 insertions(+), 43 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
@ 2026-06-05 14:03 ` Manuel Fombuena
  2026-06-17 15:04   ` Lee Jones
  2026-06-05 14:04 ` [PATCH v3 2/9] leds: st1202: validate pattern input before stopping the sequence Manuel Fombuena
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:03 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

The LED1202 datasheet (section 4.8) states that modifications to the
Pattern Sequence Repetition register (PAT_REP) and pattern duration
registers are only applied after the sequence has completed or been
stopped. When the device is running in infinite loop mode (PAT_REP =
0xFF) the sequence never completes on its own, so these writes are
silently ignored by the hardware.

Neither pattern_clear() nor pattern_set() stop the running sequence
before modifying pattern registers, causing any subsequent pattern
reprogramming to have no effect when the previous pattern was set to
infinite repeat.

Fix this by clearing PATS in the Configuration register before touching
any pattern registers in both functions, ensuring the hardware accepts
the new values immediately.

Note that the LED1202 has a single global pattern sequencer shared by
all channels: PATS, PATSR, the duration registers, and PAT_REP are
chip-wide. Stopping the sequencer in pattern_clear() therefore halts
any pattern running on other channels. This is an inherent hardware
constraint; pattern_set() restarts the sequencer when a new pattern is
programmed.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 7f68d956f694..316ed8eb054f 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -200,6 +200,10 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
 
 	guard(mutex)(&chip->lock);
 
+	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
+	if (ret != 0)
+		return ret;
+
 	for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
 		ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
 		if (ret != 0)
@@ -226,6 +230,10 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
 
 	guard(mutex)(&chip->lock);
 
+	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
+	if (ret != 0)
+		return ret;
+
 	for (int patt = 0; patt < len; patt++) {
 		if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
 				pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 2/9] leds: st1202: validate pattern input before stopping the sequence
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
  2026-06-05 14:03 ` [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming Manuel Fombuena
@ 2026-06-05 14:04 ` Manuel Fombuena
  2026-06-05 14:06 ` [PATCH v3 3/9] leds: st1202: fix pattern duration prescaler and pattern_clear skip marker Manuel Fombuena
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:04 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

Input validation for pattern duration is performed inside the write
loop, after the pattern sequence has already been stopped. If
validation fails mid-loop the chip is left with the sequence stopped
and partially written pattern data, with no recovery.

Move all input validation before the mutex and before any hardware
interaction, so an invalid input leaves the chip state unchanged.

Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 316ed8eb054f..a750c95ef3f8 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -228,6 +228,12 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
 	if (len > ST1202_MAX_PATTERNS)
 		return -EINVAL;
 
+	for (int patt = 0; patt < len; patt++) {
+		if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
+				pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
+			return -EINVAL;
+	}
+
 	guard(mutex)(&chip->lock);
 
 	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
@@ -235,10 +241,6 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
 		return ret;
 
 	for (int patt = 0; patt < len; patt++) {
-		if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
-				pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
-			return -EINVAL;
-
 		ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
 		if (ret != 0)
 			return ret;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 3/9] leds: st1202: fix pattern duration prescaler and pattern_clear skip marker
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
  2026-06-05 14:03 ` [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming Manuel Fombuena
  2026-06-05 14:04 ` [PATCH v3 2/9] leds: st1202: validate pattern input before stopping the sequence Manuel Fombuena
@ 2026-06-05 14:06 ` Manuel Fombuena
  2026-06-05 14:07 ` [PATCH v3 4/9] leds: st1202: restore Pattern0 PWM to full on after clearing pattern Manuel Fombuena
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:06 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

The PATy_DUR register encodes duration as N × 22.2 ms, with register
value 0 reserved as a pattern skip indicator (§7.10). The driver
incorrectly subtracted 1 from the register value:

  value / ST1202_MILLIS_PATTERN_DUR_MIN - 1

This caused two problems:

  - All programmed durations were off by one step (~22 ms too short).
  - Writing the minimum duration (22 ms) produced register value 0,
    silently skipping the pattern step instead of setting a 22 ms
    duration.

The maximum duration constant was also wrong at 5660 ms. The 8-bit
register saturates at 255, giving a maximum of 5610 ms (22 ms × 255).
Values above 5653 ms were already producing a uint8_t overflow and
writing 0 to the hardware.

Fix the formula by removing the erroneous subtraction, and derive the
maximum from the register width so the relationship is explicit. Update
the documentation to reflect the correct maximum.

This exposes a secondary issue: pattern_clear() was calling
st1202_duration_pattern_write() with ST1202_MILLIS_PATTERN_DUR_MIN to
reset unused slots, accidentally relying on the broken formula to
produce register value 0. With the corrected formula, the same call
writes 0x01 (22 ms), leaving unused slots as valid 22 ms zero-PWM
steps and making the LED appear off for 7 × 22 ms out of every cycle.

Write 0 directly to the duration registers in pattern_clear() so unused
slots are always explicitly marked as skip, independently of the
conversion formula.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 Documentation/leds/leds-st1202.rst | 2 +-
 drivers/leds/leds-st1202.c         | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/leds/leds-st1202.rst b/Documentation/leds/leds-st1202.rst
index 1a09fbfcedcf..a2353549469e 100644
--- a/Documentation/leds/leds-st1202.rst
+++ b/Documentation/leds/leds-st1202.rst
@@ -17,7 +17,7 @@ To be compatible with the hardware pattern format, maximum 8 tuples of
 brightness (PWM) and duration must be written to hw_pattern.
 
 - Min pattern duration: 22 ms
-- Max pattern duration: 5660 ms
+- Max pattern duration: 5610 ms
 
 The format of the hardware pattern values should be:
 "brightness duration brightness duration ..."
diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index a750c95ef3f8..b4adade49898 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -31,7 +31,7 @@
 #define ST1202_ILED_REG0                   0x09
 #define ST1202_MAX_LEDS                    12
 #define ST1202_MAX_PATTERNS                8
-#define ST1202_MILLIS_PATTERN_DUR_MAX      5660
+#define ST1202_MILLIS_PATTERN_DUR_MAX      (ST1202_MILLIS_PATTERN_DUR_MIN * U8_MAX)
 #define ST1202_MILLIS_PATTERN_DUR_MIN      22
 #define ST1202_PATTERN_DUR                 0x16
 #define ST1202_PATTERN_PWM                 0x1E
@@ -85,7 +85,7 @@ static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
 
 static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
 {
-	return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1;
+	return value / ST1202_MILLIS_PATTERN_DUR_MIN;
 }
 
 static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
@@ -209,7 +209,7 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
 		if (ret != 0)
 			return ret;
 
-		ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN);
+		ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + patt, 0);
 		if (ret != 0)
 			return ret;
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 4/9] leds: st1202: restore Pattern0 PWM to full on after clearing pattern
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (2 preceding siblings ...)
  2026-06-05 14:06 ` [PATCH v3 3/9] leds: st1202: fix pattern duration prescaler and pattern_clear skip marker Manuel Fombuena
@ 2026-06-05 14:07 ` Manuel Fombuena
  2026-06-05 14:08 ` [PATCH v3 5/9] leds: st1202: fix spurious pattern sequence start in setup Manuel Fombuena
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:07 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

When PATS is cleared the LED output is the product of the ILED analog
current register and the Pattern0 PWM duty cycle. brightness_set only
writes the ILED register, so if Pattern0 PWM is zero the LED remains
dark regardless of the brightness value set.

pattern_clear zeros all pattern PWM registers including Pattern0, which
means any subsequent brightness_set call has no visible effect until a
new pattern is programmed.

Restore Pattern0 PWM to full duty cycle after clearing so that ILED
alone controls brightness in static mode.

Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index b4adade49898..b32d8e716372 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -35,6 +35,7 @@
 #define ST1202_MILLIS_PATTERN_DUR_MIN      22
 #define ST1202_PATTERN_DUR                 0x16
 #define ST1202_PATTERN_PWM                 0x1E
+#define ST1202_PATTERN_PWM_FULL            0x0FFF
 #define ST1202_PATTERN_REP                 0x15
 
 struct st1202_led {
@@ -204,7 +205,15 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
 	if (ret != 0)
 		return ret;
 
-	for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
+	ret = st1202_pwm_pattern_write(chip, led->led_num, 0, ST1202_PATTERN_PWM_FULL);
+	if (ret != 0)
+		return ret;
+
+	ret = st1202_write_reg(chip, ST1202_PATTERN_DUR, 0);
+	if (ret != 0)
+		return ret;
+
+	for (int patt = 1; patt < ST1202_MAX_PATTERNS; patt++) {
 		ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
 		if (ret != 0)
 			return ret;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 5/9] leds: st1202: fix spurious pattern sequence start in setup
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (3 preceding siblings ...)
  2026-06-05 14:07 ` [PATCH v3 4/9] leds: st1202: restore Pattern0 PWM to full on after clearing pattern Manuel Fombuena
@ 2026-06-05 14:08 ` Manuel Fombuena
  2026-06-05 14:09 ` [PATCH v3 6/9] leds: st1202: fix brightness having no effect while pattern mode is active Manuel Fombuena
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:08 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

st1202_setup() writes PATS and PATSR to the Configuration register as
its final step, which starts the hardware pattern sequencer during
device probe before any patterns have been programmed. This causes the
device to run a sequence with whatever values happen to be in the
pattern registers at the time.

Remove the write. The device reset at the start of setup restores all
registers to their power-on defaults, leaving PATS and PATSR cleared.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index b32d8e716372..168d14566c1a 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -341,11 +341,6 @@ static int st1202_setup(struct st1202_chip *chip)
 	if (ret < 0)
 		return ret;
 
-	ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
-				ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR);
-	if (ret < 0)
-		return ret;
-
 	return 0;
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 6/9] leds: st1202: fix brightness having no effect while pattern mode is active
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (4 preceding siblings ...)
  2026-06-05 14:08 ` [PATCH v3 5/9] leds: st1202: fix spurious pattern sequence start in setup Manuel Fombuena
@ 2026-06-05 14:09 ` Manuel Fombuena
  2026-06-05 14:10 ` [PATCH v3 7/9] leds: st1202: disable channel when brightness is set to zero Manuel Fombuena
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:09 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

Once a hardware pattern is running (PATS=1), writing to the brightness
sysfs attribute only updates the ILED register. The visible output is
ILED x Pattern_PWM/4095, so the change has little effect and the LED
never returns to steady static operation as the user expects.

Set CONFIG_REG to SHFT (static mode) before writing ILED so that a
direct brightness write always produces a steady output at the
requested level.

This also enables basic LED operation without the pattern trigger: with
the trigger set to none, the brightness sysfs attribute fully controls
the LED as a simple on/off device. Previously there was no working path
for this, as brightness writes had no visible effect unless a hardware
pattern had first been programmed.

This is safe because software patterns already run with PATS=0 (making
the write a no-op in that path), and the hardware pattern engine does
not call brightness_set during autonomous pattern execution.

Note that CONFIG_REG is chip-wide, so this clears the pattern mode for
all channels on the device.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 168d14566c1a..4fa41cc74412 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -136,6 +136,7 @@ static void st1202_brightness_set(struct led_classdev *led_cdev,
 
 	guard(mutex)(&chip->lock);
 
+	st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
 	st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 7/9] leds: st1202: disable channel when brightness is set to zero
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (5 preceding siblings ...)
  2026-06-05 14:09 ` [PATCH v3 6/9] leds: st1202: fix brightness having no effect while pattern mode is active Manuel Fombuena
@ 2026-06-05 14:10 ` Manuel Fombuena
  2026-06-05 14:11 ` [PATCH v3 8/9] leds: st1202: validate LED reg property against channel count Manuel Fombuena
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:10 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

When brightness_set() is called with LED_OFF, only the ILED register is
zeroed; the channel enable bit is left set from probe time. A hardware
channel enabled with ILED=0 still draws a small residual current, causing
a dim glow even when the LED is supposed to be off.

Fix this by splitting st1202_channel_set() into a lockless inner function
__st1202_channel_set() and a locking wrapper, then calling the inner
function from brightness_set() while it already holds the mutex. The
channel is now disabled when value is zero and re-enabled when non-zero,
in the same lock region as the ILED write.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 62 +++++++++++++++++++++-----------------
 1 file changed, 34 insertions(+), 28 deletions(-)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 4fa41cc74412..7413d316d857 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -128,38 +128,11 @@ static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
 				st1202_prescalar_to_miliseconds(value));
 }
 
-static void st1202_brightness_set(struct led_classdev *led_cdev,
-				enum led_brightness value)
-{
-	struct st1202_led *led = cdev_to_st1202_led(led_cdev);
-	struct st1202_chip *chip = led->chip;
-
-	guard(mutex)(&chip->lock);
-
-	st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
-	st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
-}
-
-static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
-{
-	struct st1202_led *led = cdev_to_st1202_led(led_cdev);
-	struct st1202_chip *chip = led->chip;
-	u8 value = 0;
-
-	guard(mutex)(&chip->lock);
-
-	st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
-
-	return value;
-}
-
-static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
+static int __st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
 {
 	u8 chan_low, chan_high;
 	int ret;
 
-	guard(mutex)(&chip->lock);
-
 	if (led_num <= 7) {
 		ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low);
 		if (ret < 0)
@@ -187,6 +160,39 @@ static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active
 	return 0;
 }
 
+static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
+{
+	guard(mutex)(&chip->lock);
+
+	return __st1202_channel_set(chip, led_num, active);
+}
+
+static void st1202_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct st1202_led *led = cdev_to_st1202_led(led_cdev);
+	struct st1202_chip *chip = led->chip;
+
+	guard(mutex)(&chip->lock);
+
+	st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
+	st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
+	__st1202_channel_set(chip, led->led_num, !!value);
+}
+
+static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
+{
+	struct st1202_led *led = cdev_to_st1202_led(led_cdev);
+	struct st1202_chip *chip = led->chip;
+	u8 value = 0;
+
+	guard(mutex)(&chip->lock);
+
+	st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
+
+	return value;
+}
+
 static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
 {
 	struct st1202_led *led = cdev_to_st1202_led(ldev);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 8/9] leds: st1202: validate LED reg property against channel count
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (6 preceding siblings ...)
  2026-06-05 14:10 ` [PATCH v3 7/9] leds: st1202: disable channel when brightness is set to zero Manuel Fombuena
@ 2026-06-05 14:11 ` Manuel Fombuena
  2026-06-05 14:12 ` [PATCH v3 9/9] leds: st1202: correct and extend hw_pattern documentation Manuel Fombuena
  2026-06-17 15:04 ` [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Lee Jones
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:11 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

The reg property from the device tree is used directly as an array index
into chip->leds[] without bounds checking. A value >= ST1202_MAX_LEDS
would cause an out-of-bounds write during probe.

Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 drivers/leds/leds-st1202.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 7413d316d857..d1fadbaac127 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -289,6 +289,11 @@ static int st1202_dt_init(struct st1202_chip *chip)
 		if (err)
 			return dev_err_probe(dev, err, "Invalid register\n");
 
+		if (reg >= ST1202_MAX_LEDS)
+			return dev_err_probe(dev, -EINVAL,
+					"LED reg %u out of range [0, %d]\n",
+					reg, ST1202_MAX_LEDS - 1);
+
 		led = &chip->leds[reg];
 		led->is_active = true;
 		led->fwnode = of_fwnode_handle(child);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 9/9] leds: st1202: correct and extend hw_pattern documentation
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (7 preceding siblings ...)
  2026-06-05 14:11 ` [PATCH v3 8/9] leds: st1202: validate LED reg property against channel count Manuel Fombuena
@ 2026-06-05 14:12 ` Manuel Fombuena
  2026-06-17 15:04 ` [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Lee Jones
  9 siblings, 0 replies; 12+ messages in thread
From: Manuel Fombuena @ 2026-06-05 14:12 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

Fix the repeat section: -1 is a valid value meaning infinite repeat,
as accepted by the ledtrig-pattern sysfs interface; only 0 and values
below -1 are rejected. The previous text incorrectly stated all negative
numbers were invalid. Also remove the redundant trailing sentence since
the behaviour is now covered inline.

Add the brightness range (0-255) to the hw_pattern section, which was
previously undocumented.

Fixes: b1816b22381b ("Documentation:leds: Add leds-st1202.rst")
Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
Assisted-by: Claude:claude-sonnet-4-6
---
 Documentation/leds/leds-st1202.rst | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/Documentation/leds/leds-st1202.rst b/Documentation/leds/leds-st1202.rst
index a2353549469e..ed32eb3a27d4 100644
--- a/Documentation/leds/leds-st1202.rst
+++ b/Documentation/leds/leds-st1202.rst
@@ -16,6 +16,7 @@ in terms of PWM duty-cycle and duration (ms).
 To be compatible with the hardware pattern format, maximum 8 tuples of
 brightness (PWM) and duration must be written to hw_pattern.
 
+- Brightness range: 0-255
 - Min pattern duration: 22 ms
 - Max pattern duration: 5610 ms
 
@@ -26,9 +27,7 @@ The format of the hardware pattern values should be:
 ----------------------------
 
 Specify a pattern repeat number, which is common for all channels.
-Default is 1; negative numbers and 0 are invalid.
+Default is 1. Writing 0 is invalid. Writing -1 or 255 repeats the
+pattern indefinitely.
 
 This file will always return the originally written repeat number.
-
-When the 255 value is written to it, all patterns will repeat
-indefinitely.
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming
  2026-06-05 14:03 ` [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming Manuel Fombuena
@ 2026-06-17 15:04   ` Lee Jones
  0 siblings, 0 replies; 12+ messages in thread
From: Lee Jones @ 2026-06-17 15:04 UTC (permalink / raw)
  To: Manuel Fombuena; +Cc: pavel, vicentiu.galanopulo, linux-leds, linux-kernel

/* Sashiko Automation: Issues Found (3 Findings) */

Where the non-pre-existing issues considered?

On Fri, 05 Jun 2026, Manuel Fombuena wrote:

> The LED1202 datasheet (section 4.8) states that modifications to the
> Pattern Sequence Repetition register (PAT_REP) and pattern duration
> registers are only applied after the sequence has completed or been
> stopped. When the device is running in infinite loop mode (PAT_REP =
> 0xFF) the sequence never completes on its own, so these writes are
> silently ignored by the hardware.
> 
> Neither pattern_clear() nor pattern_set() stop the running sequence
> before modifying pattern registers, causing any subsequent pattern
> reprogramming to have no effect when the previous pattern was set to
> infinite repeat.
> 
> Fix this by clearing PATS in the Configuration register before touching
> any pattern registers in both functions, ensuring the hardware accepts
> the new values immediately.
> 
> Note that the LED1202 has a single global pattern sequencer shared by
> all channels: PATS, PATSR, the duration registers, and PAT_REP are
> chip-wide. Stopping the sequencer in pattern_clear() therefore halts
> any pattern running on other channels. This is an inherent hardware
> constraint; pattern_set() restarts the sequencer when a new pattern is
> programmed.
> 
> Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
> Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
> Assisted-by: Claude:claude-sonnet-4-6
> ---
>  drivers/leds/leds-st1202.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
> index 7f68d956f694..316ed8eb054f 100644
> --- a/drivers/leds/leds-st1202.c
> +++ b/drivers/leds/leds-st1202.c
> @@ -200,6 +200,10 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
>  
>  	guard(mutex)(&chip->lock);
>  
> +	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
> +	if (ret != 0)
> +		return ret;
> +

[Severity: High]
Does clearing the PATS bit here permanently freeze actively running sequences
on all other channels?

Because the ST1202 uses a single global sequencer, stopping it here and never
restarting it seems to break multi-channel independence. The commit message
notes this is an inherent hardware constraint required to modify duration
registers. 

However, does clearing a channel's pattern actually require stopping
the sequencer, or can it be done simply by setting the channel's PWM
registers to 0 (which the datasheet doesn't restrict while running)? 

Is it necessary to stop the sequencer and zero the global duration registers,
or does this fundamentally break the independence of multi-channel
led_classdev instances?



>  	for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
>  		ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
>  		if (ret != 0)
> @@ -226,6 +230,10 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
>  
>  	guard(mutex)(&chip->lock);
>  
> +	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
> +	if (ret != 0)
> +		return ret;
> +

[Severity: High]
Does clearing the PATS bit here permanently freeze actively running sequences
on all other channels?

Because the ST1202 uses a single global sequencer, stopping it here and never
restarting it seems to break multi-channel independence. The commit message
notes this is an inherent hardware constraint required to modify duration
registers. 

However, does clearing a channel's pattern actually require stopping
the sequencer, or can it be done simply by setting the channel's PWM
registers to 0 (which the datasheet doesn't restrict while running)? 

Is it necessary to stop the sequencer and zero the global duration registers,
or does this fundamentally break the independence of multi-channel
led_classdev instances?



>  	for (int patt = 0; patt < len; patt++) {

[Severity: High]
This is a pre-existing issue, but does setting a pattern on a single channel
destructively overwrite the globally shared pattern duration registers?

Looking at the subsequent loop in st1202_led_pattern_set():

	for (int patt = 0; patt < len; patt++) {
		ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
		if (ret != 0)
			return ret;

		ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t);
		if (ret != 0)
			return ret;
	}

The ST1202 hardware utilizes a single set of shared pattern duration
registers (ST1202_PATTERN_DUR). Because st1202_led_pattern_set() executes
as a per-channel led_classdev operation, directly overwriting these global
duration registers with the newly requested channel's delta_t timings will
silently corrupt the sequence timings of all other active channels.



[Severity: High]
This is a pre-existing issue, but looking at how the led_classdev callbacks
are initialized in st1202_dt_init():

	led->led_cdev.brightness_set = st1202_brightness_set;

Is it safe to assign a sleepable function to the non-blocking brightness_set
operation?

The LED subsystem strictly requires brightness_set to be non-blocking because
it is routinely invoked from atomic contexts (e.g., inside softirq timer
callbacks by ledtrig-timer or ledtrig-heartbeat). 

Since st1202_brightness_set() acquires a mutex and performs I2C bus
transactions:

static int st1202_brightness_set(struct led_classdev *ldev, enum led_brightness value)
{
	...
	guard(mutex)(&chip->lock);
	...
}

Calling it from these triggers will cause a scheduling while atomic kernel
panic.

Should this driver map its blocking I2C functions to the
brightness_set_blocking hook instead, which safely delegates execution to a
workqueue?

>  		if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
>  				pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
> -- 
> 2.54.0
> 
> 

-- 
Lee Jones

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling
  2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
                   ` (8 preceding siblings ...)
  2026-06-05 14:12 ` [PATCH v3 9/9] leds: st1202: correct and extend hw_pattern documentation Manuel Fombuena
@ 2026-06-17 15:04 ` Lee Jones
  9 siblings, 0 replies; 12+ messages in thread
From: Lee Jones @ 2026-06-17 15:04 UTC (permalink / raw)
  To: Manuel Fombuena; +Cc: pavel, vicentiu.galanopulo, linux-leds, linux-kernel

On Fri, 05 Jun 2026, Manuel Fombuena wrote:

> This series fixes several bugs in the LED1202 driver related to hardware
> pattern programming and brightness control. The issues were found during
> testing on a Linksys MX4200v2 router running OpenWrt.

I was going to comment on each patch, but here, take the whole review:

https://sashiko.dev/#/patchset/GV1PR08MB84976729EED25ECB484835A4C5112@GV1PR08MB8497.eurprd08.prod.outlook.com

=:-)

-- 
Lee Jones

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-06-17 15:04 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 13:59 [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Manuel Fombuena
2026-06-05 14:03 ` [PATCH v3 1/9] leds: st1202: stop pattern sequence before reprogramming Manuel Fombuena
2026-06-17 15:04   ` Lee Jones
2026-06-05 14:04 ` [PATCH v3 2/9] leds: st1202: validate pattern input before stopping the sequence Manuel Fombuena
2026-06-05 14:06 ` [PATCH v3 3/9] leds: st1202: fix pattern duration prescaler and pattern_clear skip marker Manuel Fombuena
2026-06-05 14:07 ` [PATCH v3 4/9] leds: st1202: restore Pattern0 PWM to full on after clearing pattern Manuel Fombuena
2026-06-05 14:08 ` [PATCH v3 5/9] leds: st1202: fix spurious pattern sequence start in setup Manuel Fombuena
2026-06-05 14:09 ` [PATCH v3 6/9] leds: st1202: fix brightness having no effect while pattern mode is active Manuel Fombuena
2026-06-05 14:10 ` [PATCH v3 7/9] leds: st1202: disable channel when brightness is set to zero Manuel Fombuena
2026-06-05 14:11 ` [PATCH v3 8/9] leds: st1202: validate LED reg property against channel count Manuel Fombuena
2026-06-05 14:12 ` [PATCH v3 9/9] leds: st1202: correct and extend hw_pattern documentation Manuel Fombuena
2026-06-17 15:04 ` [PATCH v3 0/9] leds: st1202: fix multiple bugs in pattern engine and brightness handling Lee Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox