Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Edelweise Escala" <edelweise.escala@analog.com>
Cc: robh@kernel.org, lee@kernel.org, linux-leds@vger.kernel.org,
	devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v12 2/2] leds: ltc3220: Add Support for LTC3220 18 channel LED Driver
Date: Fri, 03 Jul 2026 04:31:46 +0000	[thread overview]
Message-ID: <20260703043146.BE9F11F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260703-ltc3220-driver-v12-2-d4f0da2985e2@analog.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] In aggregated LED mode, regcache_sync() during resume incorrectly turns off all LEDs due to the hardware QUICK_WRITE broadcast feature.
- [High] Missing bounds checks in the second pass of child node iteration leads to an out-of-bounds memory write if the device tree is modified concurrently.
- [Medium] The `pattern_set` callback silently ignores the `repeat` argument, breaking repeating LED patterns.
- [Medium] Setting LED_CORE_SUSPENDRESUME destructively interferes with hardware blink and gradation state restoration on resume.
--

commit f1453b8dca779cc861037537a42cea5f29931bc3
Author: Edelweise Escala <edelweise.escala@analog.com>

leds: ltc3220: Add Support for LTC3220 18 channel LED Driver

Add driver for the LTC3220 18-channel LED driver
with I2C interface, individual brightness control, and hardware-assisted
blink/gradation features.

> diff --git a/drivers/leds/leds-ltc3220.c b/drivers/leds/leds-ltc3220.c
> new file mode 100644
> index 0000000000000..f8e1f4bcd5529
> --- /dev/null
> +++ b/drivers/leds/leds-ltc3220.c

[ ... ]

> +static int ltc3220_pattern_set(struct led_classdev *led_cdev,
> +			       struct led_pattern *pattern,
> +			       u32 len, int repeat)
> +{
> +	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
> +							 led_cdev);

[Severity: Medium]
Does this code safely handle repeating patterns? 

If a user requests a repeating pattern (e.g. repeat = -1 for infinite 
looping) via sysfs, this function does not check the repeat parameter 
and unconditionally returns 0. 

By returning 0 instead of -EINVAL or -EOPNOTSUPP, the driver falsely 
claims it natively handles the requested repeating pattern. The LED core 
consequently skips setting up a software fallback timer, causing the LED 
to perform a single ramp and permanently stop instead of looping.

> +	struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
> +					       uled_cfg[0]);

[ ... ]

> +static int ltc3220_resume(struct device *dev)
> +{
> +	struct ltc3220 *ltc3220 = i2c_get_clientdata(to_i2c_client(dev));
> +	int ret;
> +
> +	ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
> +				 LTC3220_SHUTDOWN_MASK, 0);
> +	if (ret)
> +		return ret;
> +
> +	usleep_range(10000, 12000);
> +
> +	return regcache_sync(ltc3220->regmap);
> +}

[Severity: High]
Will this regcache_sync() turn off all LEDs in aggregated LED mode?

During ltc3220_reset(), all 18 ULED registers are written to 0, populating 
the regmap cache with 0s for unused channels.

On resume, regcache_sync() writes all dirty registers in address order. 
It first restores COMMAND_REG, enabling the QUICK_WRITE_MASK. It then 
restores ULED_REG(0) to the active brightness, which broadcasts to all LEDs. 

However, it subsequently syncs ULED_REG(1) through ULED_REG(17) using their 
cached 0s. Because QUICK_WRITE is active, each of these writes broadcasts 
0 to all 18 LEDs, turning them off and overwriting the restored state.

[ ... ]

> +static int ltc3220_probe(struct i2c_client *client)
> +{

[ ... ]

> +		led->led_cdev.blink_set = ltc3220_blink_set;
> +		led->led_cdev.pattern_set = ltc3220_pattern_set;
> +		led->led_cdev.pattern_clear = ltc3220_pattern_clear;
> +		led->led_cdev.flags = LED_CORE_SUSPENDRESUME;
> +	}

[Severity: Medium]
Does setting this flag break hardware blink and gradation state 
restoration on resume?

Because the driver sets the LED_CORE_SUSPENDRESUME flag, the LED core forces 
the brightness to 0 on suspend. ltc3220_set_led_data() executes this by 
clearing the mode bits (LTC3220_NORMAL_MODE) in the regmap cache.

On resume, the driver correctly syncs the regmap cache, but it now contains 
the wiped mode state. Finally, the LED core restores the previous brightness, 
but does so via the basic setter, leaving the LED permanently in normal 
(solid) mode instead of restoring the hardware blink/pattern. 

Since the driver natively manages power management via chip shutdown, is 
opting into the LED core's power management overrides necessary?

[ ... ]

> +	/* Second pass: register LEDs after validation */
> +	device_for_each_child_node_scoped(&client->dev, child) {
> +		struct led_init_data init_data = {};
> +		struct ltc3220_uled_cfg *led;
> +		u32 source;
> +
> +		ret = fwnode_property_read_u32(child, "reg", &source);
> +		if (ret)
> +			return ret;
> +
> +		init_data.fwnode = child;
> +		init_data.devicename = "ltc3220";
> +
> +		led_index = source - 1;
> +		led = &ltc3220->uled_cfg[led_index];

[Severity: High]
Can this sequence cause an out-of-bounds write to the uled_cfg array?

The first pass validates that the reg property is within 1 to 
LTC3220_NUM_LEDS. However, the second pass re-iterates the device's 
children via device_for_each_child_node_scoped(), re-reads the reg 
property into source, but skips validation. 

If a device tree overlay adds a new child node to the device concurrently 
with the ltc3220_probe() execution, source could be out of bounds or 0. 
The driver computes led_index = source - 1 and uses it to index 
&ltc3220->uled_cfg[led_index]. 

Passing this invalid pointer to devm_led_classdev_register_ext() would 
corrupt memory.

> +
> +		ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
> +		if (ret)
> +			return dev_err_probe(&client->dev, ret, "Failed to register LED class\n");
> +	}
> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-ltc3220-driver-v12-0-d4f0da2985e2@analog.com?part=2

      reply	other threads:[~2026-07-03  4:31 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03  4:10 [PATCH v12 0/2] Add Support for LTC3220 18 Channel LED Driver Edelweise Escala
2026-07-03  4:10 ` [PATCH v12 1/2] dt-bindings: leds: Add LTC3220 18 channel " Edelweise Escala
2026-07-03  4:35   ` sashiko-bot
2026-07-03  4:10 ` [PATCH v12 2/2] leds: ltc3220: Add Support for " Edelweise Escala
2026-07-03  4:31   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260703043146.BE9F11F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=edelweise.escala@analog.com \
    --cc=lee@kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox