Linux LED subsystem development
 help / color / mirror / Atom feed
* [PATCH v5 0/1] leds: st1202: Add hardware-accelerated blink support
@ 2026-08-04 10:39 Manuel Fombuena
  2026-08-04 10:40 ` [PATCH v5 1/1] " Manuel Fombuena
  0 siblings, 1 reply; 5+ messages in thread
From: Manuel Fombuena @ 2026-08-04 10:39 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

This patch adds blink_set() to the ST1202 LED driver, enabling
hardware-accelerated blinking via the timer trigger.

A series of nine fixes to the pattern engine and brightness handling
was recently applied to for-leds-next:

https://lore.kernel.org/all/GV1PR08MB8497C0B898789BB73ACE6EE3C5F52@GV1PR08MB8497.eurprd08.prod.outlook.com/

With those fixes in place, the pattern engine can be used reliably to
implement blink_set(): a two-step pattern (full brightness for delay_on,
off for delay_off) is programmed and started in infinite repeat mode.
Requested delays are clamped to the hardware range and rounded up to
the nearest 22ms step.

During review of the fix series, several pre-existing issues were
identified in the driver — including brightness_set() being assigned to
a non-blocking callback, the global sequencer affecting all channels on
pattern operations, and missing brightness scaling in pattern_set().
These do not affect blink_set(): the callback is not invoked from atomic
context, the function explicitly programs all other channels' PWM slots
to zero before starting the sequencer, and channel brightness is set
directly via the ILED register. The pre-existing issues will be
addressed in a follow-up submission.

Tested on LED1202 hardware via I2C on a Linksys MX4200v2 router running
OpenWrt. Hardware blinking confirmed functional with the timer trigger.

--- Changes in v5 ---

  Use st1202_duration_pattern_write() for pattern duration slots 0 and
  1 in blink_set(), consistent with how st1202_led_pattern_set() uses
  the same helper for the same registers.

  Fix commit message: ST1202_MILLIS_PATTERN_DUR_MAX was wrapped across
  two lines.

--- Changes in v4 ---

  Fix delay clamping and rounding order following a review of the
  changes introduced across v2 and v3:

  v2 moved roundup() before clamp_val() so that rounding could not push
  the result past the hardware maximum. However, this left roundup()
  exposed to integer overflow for extreme inputs near ULONG_MAX, since
  roundup(x, 22) internally computes x + 21 before dividing.

  v3 addressed the overflow by prepending a min_t() cap at MAX before
  roundup(), and retained a trailing clamp_val() for the full range.
  The trailing clamp_val() turned out to be unreachable:
  ST1202_MILLIS_PATTERN_DUR_MAX (5610) is an exact multiple of
  ST1202_MILLIS_PATTERN_DUR_MIN (22), so roundup() on a value already
  capped at 5610 returns 5610 unchanged, and the result is always
  within range before the trailing clamp_val() runs.

  v4 reverts to clamp_val() before roundup(). Clamping first prevents
  overflow for extreme inputs, and since MAX is an exact multiple of MIN
  the rounded result cannot exceed MAX, addressing the v1 concern
  without the redundant operations introduced in v3.

--- Changes in v3 ---

  In response to automated review feedback (Sashiko) on v2:

  Clamp delay inputs to the hardware maximum before calling roundup() to
  prevent integer overflow for extreme values near ULONG_MAX.

  Other pre-existing issues identified by the automated review are
  outside the scope of this patch and will be addressed in a follow-up
  submission.

--- Changes in v2 ---

  In response to maintainer (Lee Jones) review on v1:

  Use short-form ternary for default delay substitution.
  Perform roundup before clamp_val to ensure rounding cannot exceed
  the hardware maximum.
  Capitalise commit subject per LED subsystem convention.
  Drop Assisted-by tag per maintainer suggestion.

v1: https://lore.kernel.org/all/GV1PR08MB8497C5B8CEB2CE19743DFCFFC5FA2@GV1PR08MB8497.eurprd08.prod.outlook.com/
v2: https://lore.kernel.org/all/GV1PR08MB84979858573735294D41A189C5C02@GV1PR08MB8497.eurprd08.prod.outlook.com/
v3: https://lore.kernel.org/all/GV1PR08MB84979E014A407174EBAE9875C5CF2@GV1PR08MB8497.eurprd08.prod.outlook.com/
v4: https://lore.kernel.org/all/GV1PR08MB8497E3103AD32162BD05D907C5CB2@GV1PR08MB8497.eurprd08.prod.outlook.com/

Manuel Fombuena (1):
  leds: st1202: Add hardware-accelerated blink support

 drivers/leds/leds-st1202.c | 80 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

-- 
2.55.0

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

* [PATCH v5 1/1] leds: st1202: Add hardware-accelerated blink support
  2026-08-04 10:39 [PATCH v5 0/1] leds: st1202: Add hardware-accelerated blink support Manuel Fombuena
@ 2026-08-04 10:40 ` Manuel Fombuena
  2026-08-04 10:52   ` sashiko-bot
  2026-08-06 14:17   ` Lee Jones
  0 siblings, 2 replies; 5+ messages in thread
From: Manuel Fombuena @ 2026-08-04 10:40 UTC (permalink / raw)
  To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel

Implement blink_set() to enable hardware-accelerated blinking via the
timer trigger. The LED1202 pattern engine is used to produce a two-step
sequence: full brightness for delay_on, off for delay_off, repeating
indefinitely.

Requested delays are clamped to the hardware range [22ms, 5610ms] then
rounded up to the nearest 22ms step. Clamping before rounding prevents
integer overflow in roundup() for extreme input values; since
ST1202_MILLIS_PATTERN_DUR_MAX is an exact multiple of
ST1202_MILLIS_PATTERN_DUR_MIN, rounding a clamped value cannot exceed
the maximum. A zero delay is replaced with the default of 500ms
independently for each of delay_on and delay_off.

The LED1202 pattern sequencer is global and its timing registers are
shared across all channels, so only one blink configuration can be
active at a time. Other active channels have their PWM slots zeroed for
both pattern steps so they remain dark rather than outputting unintended
values when the sequencer runs. The target channel's ILED register is
set to full brightness and the channel is enabled, since the timer
trigger deactivates the current trigger before calling blink_set which
would otherwise leave the channel disabled.

Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
---
 drivers/leds/leds-st1202.c | 80 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 168df5ecf27b..334e4733387f 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 
+#define ST1202_BLINK_DEFAULT_DELAY         500
 #define ST1202_CHAN_DISABLE_ALL            0x00
 #define ST1202_CHAN_ENABLE_HIGH            0x03
 #define ST1202_CHAN_ENABLE_LOW             0x02
@@ -275,6 +276,84 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
 	return 0;
 }
 
+static int st1202_blink_set(struct led_classdev *led_cdev,
+			unsigned long *delay_on, unsigned long *delay_off)
+{
+	struct st1202_led *led = cdev_to_st1202_led(led_cdev);
+	struct st1202_chip *chip = led->chip;
+	unsigned long on, off;
+	int ret;
+
+	on = *delay_on ?: ST1202_BLINK_DEFAULT_DELAY;
+	off = *delay_off ?: ST1202_BLINK_DEFAULT_DELAY;
+
+	on = clamp_val(on, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX);
+	off = clamp_val(off, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX);
+	on = roundup(on, ST1202_MILLIS_PATTERN_DUR_MIN);
+	off = roundup(off, ST1202_MILLIS_PATTERN_DUR_MIN);
+
+	guard(mutex)(&chip->lock);
+
+	ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
+	if (ret)
+		return ret;
+
+	/* Zero out PWM for all other active channels to prevent them from blinking */
+	for (int i = 0; i < ST1202_MAX_LEDS; i++) {
+		if (!chip->leds[i].is_active || i == led->led_num)
+			continue;
+		ret = st1202_pwm_pattern_write(chip, i, 0, LED_OFF);
+		if (ret)
+			return ret;
+		ret = st1202_pwm_pattern_write(chip, i, 1, LED_OFF);
+		if (ret)
+			return ret;
+	}
+
+	ret = st1202_pwm_pattern_write(chip, led->led_num, 0, ST1202_PATTERN_PWM_FULL);
+	if (ret)
+		return ret;
+	ret = st1202_pwm_pattern_write(chip, led->led_num, 1, LED_OFF);
+	if (ret)
+		return ret;
+
+	ret = st1202_duration_pattern_write(chip, 0, on);
+	if (ret)
+		return ret;
+	ret = st1202_duration_pattern_write(chip, 1, off);
+	if (ret)
+		return ret;
+
+	for (int patt = 2; patt < ST1202_MAX_PATTERNS; patt++) {
+		ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + patt, 0);
+		if (ret)
+			return ret;
+	}
+
+	ret = st1202_write_reg(chip, ST1202_PATTERN_REP, U8_MAX);
+	if (ret)
+		return ret;
+
+	ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, U8_MAX);
+	if (ret)
+		return ret;
+
+	ret = __st1202_channel_set(chip, led->led_num, true);
+	if (ret)
+		return ret;
+
+	ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
+				ST1202_CONFIG_REG_PATSR | ST1202_CONFIG_REG_PATS |
+				ST1202_CONFIG_REG_SHFT);
+	if (ret)
+		return ret;
+
+	*delay_on = on;
+	*delay_off = off;
+
+	return 0;
+}
+
 static int st1202_dt_init(struct st1202_chip *chip)
 {
 	struct device *dev = &chip->client->dev;
@@ -301,6 +380,7 @@ static int st1202_dt_init(struct st1202_chip *chip)
 		led->led_cdev.pattern_set = st1202_led_pattern_set;
 		led->led_cdev.pattern_clear = st1202_led_pattern_clear;
 		led->led_cdev.default_trigger = "pattern";
+		led->led_cdev.blink_set = st1202_blink_set;
 		led->led_cdev.brightness_set = st1202_brightness_set;
 		led->led_cdev.brightness_get = st1202_brightness_get;
 	}
-- 
2.55.0


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

* Re: [PATCH v5 1/1] leds: st1202: Add hardware-accelerated blink support
  2026-08-04 10:40 ` [PATCH v5 1/1] " Manuel Fombuena
@ 2026-08-04 10:52   ` sashiko-bot
  2026-08-04 15:41     ` Manuel Fombuena
  2026-08-06 14:17   ` Lee Jones
  1 sibling, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-08-04 10:52 UTC (permalink / raw)
  To: Manuel Fombuena; +Cc: linux-leds, lee

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [High] Hardware-accelerated blink destructively alters the physical state of all other active LEDs by zeroing their PWM slots.
- [Medium] Hardware blink overrides user-configured brightness by hardcoding maximum intensity.
- [Medium] Updating LED brightness indiscriminately breaks an active hardware blink configuration.

Pre-existing issues:
- [High] The driver incorrectly assigns a sleeping function to the non-blocking `brightness_set` callback, causing 'scheduling while atomic' panics.
- [High] Dangling `fwnode` pointers due to un-incremented scoped device node reference.
--

--- Patch [1]: [PATCH v5 1/1] leds: st1202: Add hardware-accelerated blink support ---
commit c3a88eb697aa140ded0dcf2250d8f8ad71aa79ac
Author: Manuel Fombuena <fombuena@outlook.com>

leds: st1202: Add hardware-accelerated blink support

Implement blink_set() to enable hardware-accelerated blinking via the timer
trigger. Requested delays are clamped to the hardware range and the LED1202
pattern sequencer is configured globally.

> diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
> --- a/drivers/leds/leds-st1202.c
> +++ b/drivers/leds/leds-st1202.c
[ ... ]
> +static int st1202_blink_set(struct led_classdev *led_cdev,
> +			unsigned long *delay_on, unsigned long *delay_off)
> +{
[ ... ]
> +	/* Zero out PWM for all other active channels to prevent them from blinking */
> +	for (int i = 0; i < ST1202_MAX_LEDS; i++) {
> +		if (!chip->leds[i].is_active || i == led->led_num)
> +			continue;
> +		ret = st1202_pwm_pattern_write(chip, i, 0, LED_OFF);
> +		if (ret)
> +			return ret;
> +		ret = st1202_pwm_pattern_write(chip, i, 1, LED_OFF);
> +		if (ret)
> +			return ret;
> +	}

[Severity: High]
Does this loop destructively alter the physical state of all other active
LEDs?

Because the LED1202 hardware sequencer is a global resource, starting it
forces all channels to execute the sequence. Any LED that was previously set
to steady ON will output zero during these steps, causing it to go dark
while the kernel subsystem still believes it to be on.

> +
> +	ret = st1202_pwm_pattern_write(chip, led->led_num, 0, ST1202_PATTERN_PWM_FULL);

[Severity: Medium]
Can this override the user-configured brightness by hardcoding maximum
intensity?

The PWM slot is being forced to ST1202_PATTERN_PWM_FULL, and later the ILED
register is forced to U8_MAX, completely disregarding the
led_cdev->brightness value.

[ ... ]
> @@ -301,6 +380,7 @@ static int st1202_dt_init(struct st1202_chip *chip)
[ ... ]
>  		led = &chip->leds[reg];
>  		led->is_active = true;
>  		led->fwnode = of_fwnode_handle(child);

[Severity: High]
This is a pre-existing issue, but could this leave led->fwnode with a
dangling pointer?

The driver stores a pointer to this node from the scoped
for_each_available_child_of_node_scoped() loop, but fails to increment
its reference count via fwnode_handle_get(). When the scoped loop
drops the reference, the node may be freed, causing a use-after-free
when accessing the fwnode during class device registration or subsystem
operations.

>  
>  		led->led_cdev.max_brightness = U8_MAX;
>  		led->led_cdev.brightness_set_blocking = st1202_led_set;
>  		led->led_cdev.pattern_set = st1202_led_pattern_set;
>  		led->led_cdev.pattern_clear = st1202_led_pattern_clear;
>  		led->led_cdev.default_trigger = "pattern";
> +		led->led_cdev.blink_set = st1202_blink_set;

[Severity: Medium]
Would updating the LED brightness indiscriminately break an active hardware
blink configuration?

If a user changes the brightness while the LED is hardware-blinking,
st1202_brightness_set() unconditionally forces all 8 PWM pattern slots to
ST1202_PATTERN_PWM_FULL:

st1202_brightness_set() {
        ...
        for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++)
                st1202_pwm_pattern_write(chip, led->led_num, patt, 
                                         ST1202_PATTERN_PWM_FULL);
        ...
}

This overwrites the OFF step established by st1202_blink_set(), turning the
LED steadily ON while the subsystem still considers it to be blinking.

>  		led->led_cdev.brightness_set = st1202_brightness_set;

[Severity: High]
This is a pre-existing issue, but is it safe to assign a sleeping function
to the non-blocking brightness_set callback? 

st1202_brightness_set() acquires a mutex and performs sleepable I2C
operations:

st1202_brightness_set() {
        ...
        guard(mutex)(&chip->lock);
        ...
        st1202_write_reg(...)
}

The LED core assumes led_cdev.brightness_set does not sleep, calling it
directly from atomic contexts. This can cause scheduling while atomic
panics.

>  		led->led_cdev.brightness_get = st1202_brightness_get;
>  	}
>  
>  	return 0;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/GV1PR08MB849709A3BE3D1CBB9E28ABD2C5D42@GV1PR08MB8497.eurprd08.prod.outlook.com?part=1

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

* Re: [PATCH v5 1/1] leds: st1202: Add hardware-accelerated blink support
  2026-08-04 10:52   ` sashiko-bot
@ 2026-08-04 15:41     ` Manuel Fombuena
  0 siblings, 0 replies; 5+ messages in thread
From: Manuel Fombuena @ 2026-08-04 15:41 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-leds, lee

On Tue, 2026-08-04 at 10:52 +0000, sashiko-bot@kernel.org wrote:
> 
> New issues:
> - [High] Hardware-accelerated blink destructively alters the physical
> state of all other active LEDs by zeroing their PWM slots.
> - [Medium] Hardware blink overrides user-configured brightness by
> hardcoding maximum intensity.
> - [Medium] Updating LED brightness indiscriminately breaks an active
> hardware blink configuration.
> 
> Pre-existing issues:
> - [High] The driver incorrectly assigns a sleeping function to the
> non-blocking `brightness_set` callback, causing 'scheduling while
> atomic' panics.
> - [High] Dangling `fwnode` pointers due to un-incremented scoped
> device node reference.
> --

All five findings are either previously answered or already tracked for
a follow-up submission, as they are not directly related to the feature
this patch implements.

Unless there are any other reviewer comments, there will not be a v6.

--
Manuel Fombuena

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

* Re: [PATCH v5 1/1] leds: st1202: Add hardware-accelerated blink support
  2026-08-04 10:40 ` [PATCH v5 1/1] " Manuel Fombuena
  2026-08-04 10:52   ` sashiko-bot
@ 2026-08-06 14:17   ` Lee Jones
  1 sibling, 0 replies; 5+ messages in thread
From: Lee Jones @ 2026-08-06 14:17 UTC (permalink / raw)
  To: Manuel Fombuena; +Cc: pavel, vicentiu.galanopulo, linux-leds, linux-kernel

On Tue, 04 Aug 2026, Manuel Fombuena wrote:

> Implement blink_set() to enable hardware-accelerated blinking via the
> timer trigger. The LED1202 pattern engine is used to produce a two-step
> sequence: full brightness for delay_on, off for delay_off, repeating
> indefinitely.
> 
> Requested delays are clamped to the hardware range [22ms, 5610ms] then
> rounded up to the nearest 22ms step. Clamping before rounding prevents
> integer overflow in roundup() for extreme input values; since
> ST1202_MILLIS_PATTERN_DUR_MAX is an exact multiple of
> ST1202_MILLIS_PATTERN_DUR_MIN, rounding a clamped value cannot exceed
> the maximum. A zero delay is replaced with the default of 500ms
> independently for each of delay_on and delay_off.
> 
> The LED1202 pattern sequencer is global and its timing registers are
> shared across all channels, so only one blink configuration can be
> active at a time. Other active channels have their PWM slots zeroed for
> both pattern steps so they remain dark rather than outputting unintended
> values when the sequencer runs. The target channel's ILED register is
> set to full brightness and the channel is enabled, since the timer
> trigger deactivates the current trigger before calling blink_set which
> would otherwise leave the channel disabled.
> 
> Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
> ---
>  drivers/leds/leds-st1202.c | 80 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 80 insertions(+)

Sorry, I've just reviewed v3.

I think the points are still valid though.

-- 
Lee Jones

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

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

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 10:39 [PATCH v5 0/1] leds: st1202: Add hardware-accelerated blink support Manuel Fombuena
2026-08-04 10:40 ` [PATCH v5 1/1] " Manuel Fombuena
2026-08-04 10:52   ` sashiko-bot
2026-08-04 15:41     ` Manuel Fombuena
2026-08-06 14:17   ` Lee Jones

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