[PATCH 15/18] common: add initial barebox deep-probe support

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Sep 28 12:53:47 EDT 2020



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 at pengutronix.de>
> ---
>  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, &regulator_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 |



More information about the barebox mailing list