[PATCH 2/3] dm: core: support dtb switching without devices/drivers unloading
Mikhail Kshevetskiy
mikhail.kshevetskiy at iopsys.eu
Thu Jun 27 13:35:45 CEST 2024
Consider a case when we have several similar boards but leds & buttons
connected differently. Some of leds uses gpio, other pwm, third gpio over
spi bitbang. Also different board may have different gpio hogs. And last
but not least: board type stored inside a ubi volume.
We want a single u-boot image that will support all these boards (we are
not using SPL). So we need runtime dtb switching.
We can't use MULTI_DTB_FIT and DTB_RESELECT because they tries to switch
dtb too early when flash & ubi drivers are not loaded/activated.
The following code do runtime switching
ret = fdtdec_resetup(&rescan);
if (!ret && rescan){
dm_uninit();
dm_init_and_scan(false);
}
but it have several nasty issues
* additional code required to reinitialize console otherwise board will hang.
* MTD subsystem is not cleared, we need to remove mtd partition before
switching to new dtb
* spi-nand flash will be named "spi-nand1" instead of "spi-nand0", thus
flash becomes unusable
* some drivers are unable to deinitialize properly (ex: network driver will
not release mdio interface)
* some memory will leak, because most of drivers assumes that it will never
be unloaded
To avoid this nasty issues the following hack was implemented
* Full DM and console reinitialization was skipped
* Only missed device/subdevice added from new dtb during device scan.
* Some driver renames their devices. As result we can't use device names
to match old device node with a new one. The special field was added to
struct udevice for this purposes.
* Driver must be binded to the old device one more time to add missed
subdevices.
* uclass must be post_bind to the old device one more time to add missed
subdevices.
* We can't use old dtb anymore, so replace old device node with a new one
for existing devices.
The following restrictions are present:
* initial dtb MUST be a subset of a new one
* old devices will NOT be reinitialised
* no devices will be deleted or deactivated
* only new devices will be added
Typical usage:
ret = fdtdec_resetup(&rescan);
if (!ret && rescan)
dm_rescan();
The code was tested with: gpio, gpio_hog, leds, buttons.
Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
---
drivers/core/device.c | 31 +++++++++++++++++++++++++++++++
drivers/core/root.c | 15 +++++++++++++++
dts/Kconfig | 8 ++++++++
include/dm/device.h | 3 +++
include/dm/root.h | 23 +++++++++++++++++++++++
5 files changed, 80 insertions(+)
diff --git a/drivers/core/device.c b/drivers/core/device.c
index bf7f261cbce..4f575ca82e7 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -62,6 +62,34 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
return ret;
}
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_RESCAN)
+ if (parent) {
+ struct list_head *entry;
+ ofnode dev_node;
+
+ list_for_each(entry, &parent->child_head) {
+ dev = list_entry(entry, struct udevice, sibling_node);
+ dev_node = dev_ofnode(dev);
+ if ((dev->driver == drv) && (dev->uclass == uc) &&
+ (dev->driver_data == driver_data) &&
+ (strcmp(dev->node_name, ofnode_get_name(node)) == 0))
+ {
+ ret = 0;
+ dev_set_ofnode(dev, node);
+ if (drv->bind)
+ ret = drv->bind(dev);
+
+ if (!ret && uc->uc_drv->post_bind)
+ ret = uc->uc_drv->post_bind(dev);
+
+ if (!ret && devp)
+ *devp = dev;
+ return ret;
+ }
+ }
+ }
+#endif
+
dev = calloc(1, sizeof(struct udevice));
if (!dev)
return -ENOMEM;
@@ -75,6 +103,9 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
dev_set_plat(dev, plat);
dev->driver_data = driver_data;
dev->name = name;
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_RESCAN)
+ dev->node_name = name;
+#endif
dev_set_ofnode(dev, node);
dev->parent = parent;
dev->driver = drv;
diff --git a/drivers/core/root.c b/drivers/core/root.c
index d4ae652bcfb..4bf1ff80636 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -340,6 +340,21 @@ static int dm_scan(bool pre_reloc_only)
return dm_probe_devices(gd->dm_root, pre_reloc_only);
}
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_RESCAN)
+int dm_rescan(void)
+{
+ int ret;
+
+ ret = dm_extended_scan(false);
+ if (ret) {
+ debug("dm_extended_scan() failed: %d\n", ret);
+ return ret;
+ }
+
+ return dm_probe_devices(gd->dm_root, false);
+}
+#endif
+
int dm_init_and_scan(bool pre_reloc_only)
{
int ret;
diff --git a/dts/Kconfig b/dts/Kconfig
index 6883a000a05..ab486b21fe2 100644
--- a/dts/Kconfig
+++ b/dts/Kconfig
@@ -292,6 +292,14 @@ config DTB_RESELECT
config allows boards to implement a function at a later point
during boot to switch to the "correct" dtb.
+config MULTI_DTB_FIT_RESCAN
+ bool "Support switching dtb without drivers unloading (experimental)"
+ depends on MULTI_DTB_FIT
+ help
+ This configs adds possibility to switch from initial (minimal) dtb
+ to a "correct" one without devices/drivers unloading (ex: board type
+ required to switch dtb stored in ubi volume)
+
config MULTI_DTB_FIT
bool "Support embedding several DTBs in a FIT image for u-boot"
help
diff --git a/include/dm/device.h b/include/dm/device.h
index add67f9ec06..409b1c4861a 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -170,6 +170,9 @@ enum {
struct udevice {
const struct driver *driver;
const char *name;
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_RESCAN)
+ const char *node_name;
+#endif
void *plat_;
void *parent_plat_;
void *uclass_plat_;
diff --git a/include/dm/root.h b/include/dm/root.h
index b2f30a842f5..dcab28f6c47 100644
--- a/include/dm/root.h
+++ b/include/dm/root.h
@@ -155,6 +155,29 @@ int dm_init(bool of_live);
*/
int dm_uninit(void);
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_RESCAN)
+/**
+ * dm_rescan - Rescan ftd and add missed DM devices (experimental)
+ *
+ * This function can be used for switching from initial (minimal) dtb
+ * to a "correct" one without devices/drivers unloading.
+ *
+ * Restrictions:
+ * - initial dtb MUST be a subset of a new one
+ * - old devices will NOT be reinitialised
+ * - no devices will be deleted or deactivated
+ * - only new devices will be added
+ *
+ * Typical usage:
+ * ret = fdtdec_resetup(&rescan);
+ * if (!ret && rescan)
+ * dm_rescan();
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int dm_rescan(void);
+#endif
+
#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
/**
* dm_remove_devices_flags - Call remove function of all drivers with
--
2.43.0
More information about the U-Boot
mailing list