leds: pca955x: Drop platform data and allocate LEDs with the device

struct pca955x_platform_data has been private to this driver since
commit ed1f4b9676 ("leds: pca955x: add device tree support"), so
nothing can pass it in through dev_get_platdata() and it only serves
as an intermediate copy of the per-LED type, default state and fwnode.
That copy is a full array of struct pca955x_led, the same runtime
structure that struct pca955x already holds, so the data is stored
twice.

Remove the platform data and parse the child firmware nodes directly
into pca955x->leds[]. Make leds[] a flexible array member so it is
allocated together with struct pca955x. This removes three
allocations and the num_leds sanity check, which could no longer
fail.

struct pca955x_led moves above struct pca955x, as a flexible array
member needs the complete element type.

Assisted-by: LLM
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Link: https://patch.msgid.link/20261005200906.188636-1-rosenp@gmail.com
Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
Rosen Penev 2026-10-05 13:09:06 -07:00 committed by Lee Jones
parent dc8aec7428
commit 7260db6364

View File

@ -79,6 +79,17 @@ struct pca955x_chipdef {
int blink_div; /* PSC divider */
};
struct pca955x_led {
struct pca955x *pca955x;
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
u32 type;
enum led_default_state default_state;
struct fwnode_handle *fwnode;
};
#define led_to_pca955x(l) container_of(l, struct pca955x_led, led_cdev)
static const struct pca955x_chipdef pca955x_chipdefs[] = {
[pca9550] = {
.bits = 2,
@ -114,7 +125,6 @@ static const struct pca955x_chipdef pca955x_chipdefs[] = {
struct pca955x {
struct mutex lock;
struct pca955x_led *leds;
const struct pca955x_chipdef *chipdef;
struct i2c_client *client;
unsigned long active_blink;
@ -123,22 +133,7 @@ struct pca955x {
#ifdef CONFIG_LEDS_PCA955X_GPIO
struct gpio_chip gpio;
#endif
};
struct pca955x_led {
struct pca955x *pca955x;
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
u32 type;
enum led_default_state default_state;
struct fwnode_handle *fwnode;
};
#define led_to_pca955x(l) container_of(l, struct pca955x_led, led_cdev)
struct pca955x_platform_data {
struct pca955x_led *leds;
int num_leds;
struct pca955x_led leds[];
};
/* 8 bits per input register */
@ -530,37 +525,27 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc,
}
#endif /* CONFIG_LEDS_PCA955X_GPIO */
static struct pca955x_platform_data *
pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
static int pca955x_parse_fwnode(struct pca955x *pca955x)
{
struct pca955x_platform_data *pdata;
struct device *dev = &pca955x->client->dev;
u8 bits = pca955x->chipdef->bits;
struct pca955x_led *led;
struct fwnode_handle *child;
int count;
count = device_get_child_node_count(&client->dev);
if (count > chip->bits)
return ERR_PTR(-ENODEV);
count = device_get_child_node_count(dev);
if (count > bits)
return -ENODEV;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->leds = devm_kcalloc(&client->dev,
chip->bits, sizeof(struct pca955x_led),
GFP_KERNEL);
if (!pdata->leds)
return ERR_PTR(-ENOMEM);
device_for_each_child_node(&client->dev, child) {
device_for_each_child_node(dev, child) {
u32 reg;
int res;
res = fwnode_property_read_u32(child, "reg", &reg);
if ((res != 0) || (reg >= chip->bits))
if ((res != 0) || (reg >= bits))
continue;
led = &pdata->leds[reg];
led = &pca955x->leds[reg];
led->type = PCA955X_TYPE_LED;
led->fwnode = child;
led->default_state = led_init_default_state_get(child);
@ -568,9 +553,7 @@ pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
fwnode_property_read_u32(child, "type", &led->type);
}
pdata->num_leds = chip->bits;
return pdata;
return 0;
}
static int pca955x_probe(struct i2c_client *client)
@ -584,7 +567,6 @@ static int pca955x_probe(struct i2c_client *client)
u8 i, nls, psc0;
u8 ls1[4];
u8 ls2[4];
struct pca955x_platform_data *pdata;
bool keep_psc0 = false;
bool set_default_label = false;
char default_label[4];
@ -595,12 +577,6 @@ static int pca955x_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n");
adapter = client->adapter;
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
pdata = pca955x_get_pdata(client, chip);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
/* Make sure the slave address / chip type combo given is possible */
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
@ -616,22 +592,11 @@ static int pca955x_probe(struct i2c_client *client)
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
if (pdata->num_leds != chip->bits) {
dev_err(&client->dev,
"board info claims %d LEDs on a %u-bit chip\n",
pdata->num_leds, chip->bits);
return -ENODEV;
}
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
pca955x = devm_kzalloc(&client->dev,
struct_size(pca955x, leds, chip->bits), GFP_KERNEL);
if (!pca955x)
return -ENOMEM;
pca955x->leds = devm_kcalloc(&client->dev, chip->bits,
sizeof(*pca955x_led), GFP_KERNEL);
if (!pca955x->leds)
return -ENOMEM;
i2c_set_clientdata(client, pca955x);
mutex_init(&pca955x->lock);
@ -639,6 +604,10 @@ static int pca955x_probe(struct i2c_client *client)
pca955x->chipdef = chip;
pca955x->blink_period = PCA955X_BLINK_DEFAULT_MS;
err = pca955x_parse_fwnode(pca955x);
if (err)
return err;
init_data.devname_mandatory = false;
init_data.devicename = "pca955x";
@ -657,7 +626,6 @@ static int pca955x_probe(struct i2c_client *client)
pca955x_led = &pca955x->leds[i];
pca955x_led->led_num = i;
pca955x_led->pca955x = pca955x;
pca955x_led->type = pdata->leds[i].type;
switch (pca955x_led->type) {
case PCA955X_TYPE_NONE:
@ -671,16 +639,16 @@ static int pca955x_probe(struct i2c_client *client)
led->brightness_get = pca955x_led_get;
led->blink_set = pca955x_led_blink;
if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF)
if (pca955x_led->default_state == LEDS_DEFSTATE_OFF)
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_OFF);
else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON)
else if (pca955x_led->default_state == LEDS_DEFSTATE_ON)
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_ON);
else if (pca955x_ledstate(ls2[reg], bit) == PCA955X_LS_BLINK0) {
keep_psc0 = true;
set_bit(i, &pca955x->active_blink);
}
init_data.fwnode = pdata->leds[i].fwnode;
init_data.fwnode = pca955x_led->fwnode;
if (is_of_node(init_data.fwnode)) {
if (to_of_node(init_data.fwnode)->name[0] ==