On Wed, Feb 12, 2025 at 09:58:42AM +0200, Svyatoslav Ryhel wrote: > Add ability to fill pdata from device tree. Common stuff is > filled from core driver and then pdata is filled per-device > since all cells are optional. ... > #include <linux/module.h> > #include <linux/mutex.h> > #include <linux/mfd/core.h> > +#include <linux/of.h> Is it used? In any case, please no OF-centric APIs in a new (feature) code. > #include <linux/platform_device.h> > +#include <linux/property.h> > #include <linux/slab.h> > #include <linux/uaccess.h> ... > +static int lm3533_pass_of_node(struct platform_device *pdev, pass_of_node sounds a bit awkward. Perhaps you thought about parse_fwnode ? > + struct lm3533_als_platform_data *pdata) > +{ > + struct device *parent_dev = pdev->dev.parent; > + struct device *dev = &pdev->dev; > + struct fwnode_handle *node; > + const char *label; > + int val, ret; > + > + device_for_each_child_node(parent_dev, node) { > + fwnode_property_read_string(node, "compatible", &label); > + > + if (!strcmp(label, pdev->name)) { This is a bit strange. Why one need to compare platform device instance (!) name with compatible which is part of ABI. This looks really wrong approach. Needs a very good explanation on what's going on here. Besides that the label is usually filled by LEDS core, why do we need to handle it in a special way? > + ret = fwnode_property_read_u32(node, "reg", &val); > + if (ret) { > + dev_err(dev, "reg property is missing: ret %d\n", ret); > + return ret; > + } > + > + if (val == pdev->id) { > + dev->fwnode = node; > + dev->of_node = to_of_node(node); No direct access to fwnode in struct device, please use device_set_node(). > + } > + } > + } > + > + return 0; > +} ... > pdata = dev_get_platdata(&pdev->dev); > if (!pdata) { > - dev_err(&pdev->dev, "no platform data\n"); > - return -EINVAL; > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + ret = lm3533_pass_of_node(pdev, pdata); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, > + "failed to get als device node\n"); With struct device *dev = &pdev->dev; at the top of the function will help a lot in making the code neater and easier to read. > + lm3533_parse_als(pdev, pdata); > } ... > #include <linux/leds.h> > #include <linux/mfd/core.h> > #include <linux/mutex.h> > +#include <linux/of.h> Cargo cult? "Proxy" header? Please follow IWYU principle. > #include <linux/platform_device.h> > +#include <linux/property.h> > #include <linux/slab.h> ... > +static void lm3533_parse_led(struct platform_device *pdev, > + struct lm3533_led_platform_data *pdata) > +{ > + struct device *dev = &pdev->dev; > + int val, ret; > + > + ret = device_property_read_string(dev, "default-trigger", > + &pdata->default_trigger); > + if (ret) > + pdata->default_trigger = "none"; Isn't this done already in LEDS core? > + /* 5000 - 29800 uA (800 uA step) */ > + ret = device_property_read_u32(dev, "max-current-microamp", &val); > + if (ret) > + val = 5000; > + pdata->max_current = val; > + > + /* 0 - 0x3f */ > + ret = device_property_read_u32(dev, "pwm", &val); > + if (ret) > + val = 0; > + pdata->pwm = val; > +} ... > +static int lm3533_pass_of_node(struct platform_device *pdev, > + struct lm3533_led_platform_data *pdata) I think I already saw exactly the same piece of code. Please make sure you have no duplications. > +} ... > pdata = dev_get_platdata(&pdev->dev); > if (!pdata) { > - dev_err(&pdev->dev, "no platform data\n"); > - return -EINVAL; > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + ret = lm3533_pass_of_node(pdev, pdata); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, > + "failed to get led device node\n"); > + > + lm3533_parse_led(pdev, pdata); Ditto. > } ... > - led->cdev.name = pdata->name; > + led->cdev.name = dev_name(&pdev->dev); Are you sure it's a good idea? ... > - if (!pdata->als) > + if (!pdata->num_als) > return 0; > > - lm3533_als_devs[0].platform_data = pdata->als; > - lm3533_als_devs[0].pdata_size = sizeof(*pdata->als); > + if (pdata->num_als > ARRAY_SIZE(lm3533_als_devs)) > + pdata->num_als = ARRAY_SIZE(lm3533_als_devs); Looks like you want pdata->num_als = clamp(pdata->num_als, 0, ARRAY_SIZE(lm3533_als_devs)); if (!pdata->num_als) return 0; instead of the above. You would need minmax.h for that. ... > + if (pdata->leds) { This is strange. I would expect num_leds == 0 in this case > + for (i = 0; i < pdata->num_leds; ++i) { > + lm3533_led_devs[i].platform_data = &pdata->leds[i]; > + lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]); > + } > } ... > +static void lm3533_parse_nodes(struct lm3533 *lm3533, > + struct lm3533_platform_data *pdata) > +{ > + struct fwnode_handle *node; > + const char *label; > + > + device_for_each_child_node(lm3533->dev, node) { > + fwnode_property_read_string(node, "compatible", &label); > + > + if (!strcmp(label, lm3533_bl_devs[pdata->num_backlights].name)) > + pdata->num_backlights++; > + > + if (!strcmp(label, lm3533_led_devs[pdata->num_leds].name)) > + pdata->num_leds++; > + > + if (!strcmp(label, lm3533_als_devs[pdata->num_als].name)) > + pdata->num_als++; > + } > +} Oh, I don't like this approach. If you have compatible, you have driver_data available, all this is not needed as it may be hard coded. ... > if (!pdata) { I would expect actually that legacy platform data support will be simply killed by this patch(es). Do we have in-kernel users? If so, they can be easily converted to use software nodes, otherwise we even don't need to care. > - dev_err(lm3533->dev, "no platform data\n"); > - return -EINVAL; > + pdata = devm_kzalloc(lm3533->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + ret = device_property_read_u32(lm3533->dev, > + "ti,boost-ovp", > + &pdata->boost_ovp); > + if (ret) > + pdata->boost_ovp = LM3533_BOOST_OVP_16V; > + > + ret = device_property_read_u32(lm3533->dev, > + "ti,boost-freq", > + &pdata->boost_freq); > + if (ret) > + pdata->boost_freq = LM3533_BOOST_FREQ_500KHZ; > + > + lm3533_parse_nodes(lm3533, pdata); > + > + lm3533->dev->platform_data = pdata; > } ... > +static const struct of_device_id lm3533_match_table[] = { > + { .compatible = "ti,lm3533" }, > + { }, No comma in the terminator entry. > +}; ... > +static void lm3533_parse_backlight(struct platform_device *pdev, pdev is not actually used, just pass struct device *dev directly. Same comment to other functions in this change. It will make code more bus independent and reusable. > + struct lm3533_bl_platform_data *pdata) > +{ > + struct device *dev = &pdev->dev; > + int val, ret; > + > + /* 5000 - 29800 uA (800 uA step) */ > + ret = device_property_read_u32(dev, "max-current-microamp", &val); > + if (ret) > + val = 5000; > + pdata->max_current = val; > + /* 0 - 255 */ > + ret = device_property_read_u32(dev, "default-brightness", &val); > + if (ret) > + val = LM3533_BL_MAX_BRIGHTNESS; > + pdata->default_brightness = val; Isn't handled by LEDS core? > + /* 0 - 0x3f */ > + ret = device_property_read_u32(dev, "pwm", &val); > + if (ret) > + val = 0; > + pdata->pwm = val; > +} ... > +static int lm3533_pass_of_node(struct platform_device *pdev, > + struct lm3533_bl_platform_data *pdata) > +{ 3rd dup? > +} ... > pdata = dev_get_platdata(&pdev->dev); > if (!pdata) { > - dev_err(&pdev->dev, "no platform data\n"); > - return -EINVAL; > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + ret = lm3533_pass_of_node(pdev, pdata); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, > + "failed to get backlight device node\n"); > + > + lm3533_parse_backlight(pdev, pdata); > } Ditto. > - bd = devm_backlight_device_register(&pdev->dev, pdata->name, > - pdev->dev.parent, bl, &lm3533_bl_ops, > - &props); > + bd = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), I'm not sure the dev_name() is a good idea. We usually try to rely on the predictable outcome, something like what "%pfw" prints against a certain fwnode. > + pdev->dev.parent, bl, > + &lm3533_bl_ops, &props); ... Also I feel that this change may be split to a few separate logical changes. -- With Best Regards, Andy Shevchenko