On 9/28/20 5:50 PM, Marco Felsch wrote: > The barebox 'deep probe' or 'probe on demand' mechanism is the answer of > unwanted -EPROBE_DEFER failures. The EPROBE_DEFER error code was > introduced by commit ab3da15bc14c ("base: Introduce deferred probing") > and since then it causes a few problems. > > The error is returned if either the device is not yet present or the > driver is not yet registered. This makes sense on linux systems where > modules and hot-plug devices are used very often but not for barebox. > The module support is rarely used and devices aren't hot pluggable. > > The current barebox behaviour populates all devices before the drivers > are registered so all devices are present during the driver > registration. So the driver probe() function gets called immediately > after the driver registration and causes the -EPROBE_DEFER error if this > driver depends on an other not yet registered driver. > > To get rid of the EPROBE_DEFER error code we need to reorder the device > population and the driver registration. All drivers must be registered > first. In an ideal world all driver can be registered by the same > initcall level. Then devices are getting populated which causes calling > the driver probe() function but this time resources/devices are created > on demand if not yet available. > > Dependencies between devices are normally expressed as references to > other device nodes. With deep probe barebox provides helper functions > which take a device node and probe the device behind that node if > necessary. This means instead of returning -EPROBE_DEFER, we can now > make the desired resources available once we need them. > > If the resource can't be created we are returning -ENODEV since we are > not supporting hot-plugging. Dropping EPROBE_DEFER is the long-term > goal, avoid initcall shifting is the short-term goal. > > Call it deep-probe since the on-demand device creation can greate very > deep stacks. This commit adds the initial support for: spi, i2c, reset, > regulator and clk resource on-demand creation. The deep-probe mechanism > must be enabled for each board to avoid breaking changes using > deep_probe_enable(). This can be changed later after all boards are > converted to the new mechanism. > > Signed-off-by: Marco Felsch <m.felsch@xxxxxxxxxxxxxx> > --- > common/Makefile | 1 + > common/deep-probe.c | 39 +++++++++++++++ > drivers/base/driver.c | 11 ++++- > drivers/clk/clk.c | 5 ++ > drivers/i2c/i2c.c | 6 +++ > drivers/of/base.c | 13 ++++- > drivers/of/platform.c | 101 ++++++++++++++++++++++++++++++++++++++- > drivers/regulator/core.c | 6 +++ > drivers/reset/core.c | 5 ++ > drivers/spi/spi.c | 2 + > include/deep-probe.h | 17 +++++++ > include/of.h | 37 +++++++++++++- > 12 files changed, 238 insertions(+), 5 deletions(-) > create mode 100644 common/deep-probe.c > create mode 100644 include/deep-probe.h > > diff --git a/common/Makefile b/common/Makefile > index c3ae3ca1b9..8525240422 100644 > --- a/common/Makefile > +++ b/common/Makefile > @@ -3,6 +3,7 @@ obj-y += memory_display.o > pbl-$(CONFIG_PBL_CONSOLE) += memory_display.o > obj-y += clock.o > obj-y += console_common.o > +obj-y += deep-probe.o > obj-y += startup.o > obj-y += misc.o > obj-pbl-y += memsize.o > diff --git a/common/deep-probe.c b/common/deep-probe.c > new file mode 100644 > index 0000000000..05ce4352b1 > --- /dev/null > +++ b/common/deep-probe.c > @@ -0,0 +1,39 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#include <common.h> > +#include <deep-probe.h> Shouldn't this rather be under drivers/of/ ? maybe also name it something starting with of_ ? > +#include <of.h> > + > +struct deep_probe_entry { > + struct list_head entry; > + const char *compatible; > +}; > + > +static LIST_HEAD(boards); > + > +int deep_probe_add_board(const char *machine) > +{ > + struct deep_probe_entry *new_machine; > + > + new_machine = xzalloc(sizeof(*new_machine)); > + if (!new_machine) > + return -ENOMEM; xzalloc can't fail. > + > + new_machine->compatible = machine; > + list_add(&new_machine->entry, &boards); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(deep_probe_add_board); > + > +bool deep_probe_is_supported(void) > +{ > + struct deep_probe_entry *machine; > + > + list_for_each_entry(machine, &boards, entry) { > + if (of_machine_is_compatible(machine->compatible)) > + return true; > + } > + return false; > +} > +EXPORT_SYMBOL_GPL(deep_probe_is_supported); > diff --git a/drivers/base/driver.c b/drivers/base/driver.c > index 412db6c406..b797655c15 100644 > --- a/drivers/base/driver.c > +++ b/drivers/base/driver.c > @@ -21,6 +21,7 @@ > > #include <common.h> > #include <command.h> > +#include <deep-probe.h> > #include <driver.h> > #include <malloc.h> > #include <console.h> > @@ -95,7 +96,15 @@ int device_probe(struct device_d *dev) > if (ret == -EPROBE_DEFER) { > list_del(&dev->active); > list_add(&dev->active, &deferred); > - dev_dbg(dev, "probe deferred\n"); > + > + /* > + * -EPROBE_DEFER should never appear on a deep-probe machine so > + * inform the user immediately. > + */ > + if (deep_probe_is_supported()) > + dev_warn(dev, "probe deferred\n"); > + else > + dev_dbg(dev, "probe deferred\n"); > return ret; > } > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c > index b04d44593b..218317d00d 100644 > --- a/drivers/clk/clk.c > +++ b/drivers/clk/clk.c > @@ -437,9 +437,14 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); > > struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) > { > + struct device_d *dev; > struct of_clk_provider *provider; > struct clk *clk = ERR_PTR(-EPROBE_DEFER); > > + dev = of_device_create_on_demand(clkspec->np); > + if (IS_ERR(dev)) > + return ERR_CAST(dev); > + > /* Check if we have such a provider in our array */ > list_for_each_entry(provider, &of_clk_providers, link) { > if (provider->node == clkspec->np) > diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c > index 57d8c7017f..dbc0bd4f0b 100644 > --- a/drivers/i2c/i2c.c > +++ b/drivers/i2c/i2c.c > @@ -406,6 +406,7 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, > return NULL; > } > client->dev.info = i2c_info; > + chip->of_node->dev = &client->dev; Are you sure, you can always assume of_node != NULL here? > > return client; > } > @@ -547,8 +548,13 @@ struct i2c_adapter *i2c_get_adapter(int busnum) > > struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) > { > + struct device_d *dev; > struct i2c_adapter *adap; > > + dev = of_device_create_on_demand(node); > + if (IS_ERR(dev)) > + return ERR_CAST(dev); > + > for_each_i2c_adapter(adap) > if (adap->dev.device_node == node) > return adap; > diff --git a/drivers/of/base.c b/drivers/of/base.c > index 861871b221..cea6d5b1aa 100644 > --- a/drivers/of/base.c > +++ b/drivers/of/base.c > @@ -15,6 +15,7 @@ > * GNU General Public License for more details. > */ > #include <common.h> > +#include <deep-probe.h> > #include <of.h> > #include <of_address.h> > #include <errno.h> > @@ -1567,6 +1568,15 @@ int of_set_root_node(struct device_node *node) > return 0; > } > > +static int barebox_of_populate(void) > +{ > + if (IS_ENABLED(CONFIG_OFDEVICE) && deep_probe_is_supported()) > + of_probe(); > + > + return 0; > +} > +of_populate_initcall(barebox_of_populate); > + > void barebox_register_of(struct device_node *root) > { > if (root_node) > @@ -1577,7 +1587,8 @@ void barebox_register_of(struct device_node *root) > > if (IS_ENABLED(CONFIG_OFDEVICE)) { > of_clk_init(root, NULL); > - of_probe(); > + if (!deep_probe_is_supported()) > + of_probe(); > } > } > > diff --git a/drivers/of/platform.c b/drivers/of/platform.c > index 21c7cce1a5..c837adf8e8 100644 > --- a/drivers/of/platform.c > +++ b/drivers/of/platform.c > @@ -15,6 +15,7 @@ > * GNU General Public License for more details. > */ > #include <common.h> > +#include <deep-probe.h> > #include <malloc.h> > #include <of.h> > #include <of_address.h> > @@ -29,6 +30,11 @@ > struct device_d *of_find_device_by_node(struct device_node *np) > { > struct device_d *dev; > + > + dev = of_device_create_on_demand(np); > + if (IS_ERR(dev)) > + return NULL; > + > for_each_device(dev) > if (dev->device_node == np) > return dev; > @@ -106,6 +112,9 @@ struct device_d *of_platform_device_create(struct device_node *np, > if (!of_device_is_available(np)) > return NULL; > > + if (np->dev) > + return np->dev; > + Unsure if this is correct. Seek down to the "check if all address resources match" comment. It's possible that devices are registered multiple times and are coalesced later on. I can't say for sure though, where we are doing this at the moment. > /* count the io resources */ > if (of_can_translate_address(np)) > while (of_address_to_resource(np, num_reg, &temp_res) == 0) > @@ -170,8 +179,10 @@ struct device_d *of_platform_device_create(struct device_node *np, > (num_reg) ? &dev->resource[0].start : &resinval); > > ret = platform_device_register(dev); > - if (!ret) > + if (!ret) { > + np->dev = dev; > return dev; > + } > > free(dev); > if (num_reg) > @@ -252,6 +263,9 @@ static struct device_d *of_amba_device_create(struct device_node *np) > if (!of_device_is_available(np)) > return NULL; > > + if (np->dev) > + return np->dev; > + > dev = xzalloc(sizeof(*dev)); > > /* setup generic device info */ > @@ -275,6 +289,8 @@ static struct device_d *of_amba_device_create(struct device_node *np) > if (ret) > goto amba_err_free; > > + np->dev = &dev->dev; > + > return &dev->dev; > > amba_err_free: > @@ -364,3 +380,86 @@ int of_platform_populate(struct device_node *root, > return rc; > } > EXPORT_SYMBOL_GPL(of_platform_populate); > + > +struct device_d *of_device_create_on_demand(struct device_node *np) > +{ > + struct device_node *parent; > + struct device_d *parent_dev, *dev; > + > + if (!deep_probe_is_supported()) > + return NULL; > + > + parent = of_get_parent(np); > + if (!parent) > + return NULL; > + > + /* Create all parent devices needed for the requested device */ > + parent_dev = parent->dev ? : of_device_create_on_demand(parent); > + if (IS_ERR(parent_dev)) > + return parent_dev; > + > + /* > + * Parent devices like i2c/spi controllers are populating their own > + * devices. So it can be that the requested device already exist after > + * the parent device creation. > + */ > + if (np->dev) > + return np->dev; > + > + pr_debug("%s: Create %s (%s) on demand\n", __func__, > + np->name, np->full_name); > + > + if (of_device_is_compatible(np, "arm,primecell")) > + dev = of_amba_device_create(np); > + else > + dev = of_platform_device_create(np, parent_dev); > + > + return dev ? : ERR_PTR(-ENODEV); > +} > +EXPORT_SYMBOL_GPL(of_device_create_on_demand); > + > +struct device_d *of_device_create_on_demand_by_alias(const char *alias) > +{ > + struct device_node *dev_node; > + > + dev_node = of_find_node_by_alias(NULL, alias); > + return of_device_create_on_demand(dev_node); > +} > +EXPORT_SYMBOL_GPL(of_device_create_on_demand_by_alias); > + > +struct device_d * > +of_device_create_on_demand_by_dev_id(struct device_node *np, > + const struct of_device_id *ids) > +{ I find it surprising that this works recursively, while the previous by_alias doesn't > + struct device_node *child; > + > + if (of_match_node(ids, np)) > + return of_device_create_on_demand(np); > + > + for_each_child_of_node(np, child) { > + struct device_d *dev; > + > + dev = of_device_create_on_demand_by_dev_id(child, ids); > + if (!IS_ERR(dev)) > + return dev; > + } > + > + return ERR_PTR(-ENODEV); > +} > +EXPORT_SYMBOL_GPL(of_device_create_on_demand_by_dev_id); > + > +int of_devices_create_on_demand_by_property(const char *property_name) > +{ > + struct device_node *node; > + > + for_each_node_with_property(node, property_name) { > + struct device_d *dev; > + > + dev = of_device_create_on_demand(node); > + if (IS_ERR(dev)) > + return PTR_ERR(dev); > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(of_devices_create_on_demand_by_property); > diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c > index 6ea21a4609..25973acaf0 100644 > --- a/drivers/regulator/core.c > +++ b/drivers/regulator/core.c > @@ -175,6 +175,7 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node) > return PTR_ERR(ri); > > ri->node = node; > + node->dev = rd->dev; > > of_property_read_u32(node, "regulator-enable-ramp-delay", > &ri->enable_time_us); > @@ -188,6 +189,7 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node) > > static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply) > { > + struct device_d *dev_ondemand; > char *propname; > struct regulator_internal *ri; > struct device_node *node; > @@ -222,6 +224,10 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c > goto out; > } > > + dev_ondemand = of_device_create_on_demand(node); > + if (IS_ERR(dev_ondemand)) > + return ERR_CAST(dev_ondemand); > + > list_for_each_entry(ri, ®ulator_list, list) { > if (ri->node == node) { > dev_dbg(dev, "Using %s regulator from %s\n", > diff --git a/drivers/reset/core.c b/drivers/reset/core.c > index 99b9c80655..874de11b0c 100644 > --- a/drivers/reset/core.c > +++ b/drivers/reset/core.c > @@ -153,6 +153,7 @@ static struct reset_control *of_reset_control_get(struct device_node *node, > struct reset_control *rstc = ERR_PTR(-ENODEV); > struct reset_controller_dev *r, *rcdev; > struct of_phandle_args args; > + struct device_d *dev; > int index = 0; > int rstc_id; > int ret; > @@ -168,6 +169,10 @@ static struct reset_control *of_reset_control_get(struct device_node *node, > if (ret) > return ERR_PTR(ret); > > + dev = of_device_create_on_demand(args.np); > + if (IS_ERR(dev)) > + return ERR_CAST(dev); > + > rcdev = NULL; > list_for_each_entry(r, &reset_controller_list, list) { > if (args.np == r->of_node) { > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c > index 8421d9d7c1..d1d3bdcc41 100644 > --- a/drivers/spi/spi.c > +++ b/drivers/spi/spi.c > @@ -107,6 +107,8 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl, > if (status) > goto fail; > > + chip->device_node->dev = &proxy->dev; > + > return proxy; > fail: > free(proxy); > diff --git a/include/deep-probe.h b/include/deep-probe.h > new file mode 100644 > index 0000000000..f9a1f61fde > --- /dev/null > +++ b/include/deep-probe.h > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +#ifndef __DEEP_PROBE_H > +#define __DEEP_PROBE_H > + > +#include <linux/types.h> > + > +int deep_probe_add_board(const char *machine); > +bool deep_probe_is_supported(void); > + > +#define deep_probe_enable(func,board) \ > + static int __init func##_deep_probe_register(void) \ > + { \ > + return deep_probe_add_board(board); \ > + } \ > + pure_initcall(func##_deep_probe_register) > + > +#endif /* __DEEP_PROBE_H */ > diff --git a/include/of.h b/include/of.h > index 1b3ceaff40..0f8d6f7546 100644 > --- a/include/of.h > +++ b/include/of.h > @@ -35,6 +35,7 @@ struct device_node { > struct list_head parent_list; > struct list_head list; > phandle phandle; > + struct device_d *dev; > }; > > struct of_device_id { > @@ -266,6 +267,13 @@ extern struct device_d *of_device_enable_and_register_by_name(const char *name); > extern struct device_d *of_device_enable_and_register_by_alias( > const char *alias); > > +extern struct device_d *of_device_create_on_demand(struct device_node *np); > +extern struct device_d *of_device_create_on_demand_by_alias(const char *alias); > +extern struct device_d * > +of_device_create_on_demand_by_dev_id(struct device_node *np, > + const struct of_device_id *ids); > +extern int of_devices_create_on_demand_by_property(const char *property_name); > + > struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node); > int of_parse_partitions(struct cdev *cdev, struct device_node *node); > int of_partitions_register_fixup(struct cdev *cdev); > @@ -331,12 +339,37 @@ static inline int of_set_root_node(struct device_node *node) > return -ENOSYS; > } > > -static inline struct device_d *of_platform_device_create(struct device_node *np, > - struct device_d *parent) > +static inline struct device_d * > +of_platform_device_create(struct device_node *np, struct device_d *parent) > +{ > + return NULL; > +} > + > +static inline struct device_d * > +of_device_create_on_demand(struct device_node *np); Extra semicolon. Could you compile test without CONFIG_OFTREE? > +{ > + return NULL; > +} > + > +static inline struct device_d * > +of_device_create_on_demand_by_alias(struct device_node *np); > { > return NULL; > } > > +static inline struct device_d * > +of_device_create_on_demand_by_dev_id(struct device_node *np, > + const struct of_device_id *ids) > +{ > + return NULL; > +} > + > +static inline int > +of_devices_create_on_demand_by_property(const char *property_name) > +{ > + return 0; > +} > + > static inline int of_bus_n_addr_cells(struct device_node *np) > { > return 0; > -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox