[U-Boot] [RFC PATCH 07/22] dm: Allow drivers to be marked 'before relocation'

Jon Loeliger
2014-05-27T15:36:43Z

> Allow drivers to mark themselves as 'pre-reloc' which means that they will
> be initialised prior to relocation. This can be done either with a driver
> flag or with a 'dm,pre-reloc' device tree property.

Hmmm, "dm,pre-reloc" isn't really describing the hardware any more.
That's really a flag for the purpose of telling SW how it should work.
We really should be careful here to maintain the DTS content as a
description of the hardware.

HTH,
jdl

On Sat, May 24, 2014 at 4:21 PM, Simon Glass <sjg@chromium.org> wrote:
> Driver model currently only operates after relocation is complete. In this
> state U-Boot typically has a small amount of memory available. In adding
> support for driver model prior to relocation we must try to use as little
> memory as possible.
>
> In addition, on some machines the memory has not be inited and/or the CPU
> is not running at full speed or the data cache is off. These can reduce
> execution performance, so the less initialisation that is done before
> relocation the better.
>
> An immediately-obvious improvement is to only initialise drivers which are
> actually going to be used before relocation. On many boards the only such
> driver is a serial UART, so this provides a very large potential benefit.
>
> Allow drivers to mark themselves as 'pre-reloc' which means that they will
> be initialised prior to relocation. This can be done either with a driver
> flag or with a 'dm,pre-reloc' device tree property.
>
> To support this, the various dm scanning function now take a 'pre_reloc_only'
> parameter which indicates that only drivers marked pre-reloc should be
> bound.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
> common/board_r.c | 4 ++--
> doc/driver-model/README.txt | 23 +++++++++++++++-------
> drivers/core/device.c | 6 ++++--
> drivers/core/lists.c | 6 +++---
> drivers/core/root.c | 11 +++++++----
> include/dm/device-internal.h | 6 ++++--
> include/dm/device.h | 5 +++++
> include/dm/lists.h | 2 +-
> include/dm/root.h | 8 ++++++--
> test/dm/core.c | 47 ++++++++++++++++++++++++++++++++++----------
> test/dm/test-driver.c | 11 +++++++++++
> test/dm/test-fdt.c | 20 ++++++++++++++++++-
> test/dm/test-main.c | 4 ++--
> test/dm/test.dts | 11 +++++++++++
> 14 files changed, 128 insertions(+), 36 deletions(-)
>
> diff --git a/common/board_r.c b/common/board_r.c
> index d1f0aa9..d2a59ee 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -276,13 +276,13 @@ static int initr_dm(void)
> debug("dm_init() failed: %d\n", ret);
> return ret;
> }
> - ret = dm_scan_platdata();
> + ret = dm_scan_platdata(false);
> if (ret) {
> debug("dm_scan_platdata() failed: %d\n", ret);
> return ret;
> }
> #ifdef CONFIG_OF_CONTROL
> - ret = dm_scan_fdt(gd->fdt_blob);
> + ret = dm_scan_fdt(gd->fdt_blob, false);
> if (ret) {
> debug("dm_scan_fdt() failed: %d\n", ret);
> return ret;
> diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
> index e0b395a..deacfe9 100644
> --- a/doc/driver-model/README.txt
> +++ b/doc/driver-model/README.txt
> @@ -332,26 +332,35 @@ dealing with this might not be worth it.
> - Implemented a GPIO system, trying to keep it simple
>
>
> +Pre-Relocation Support
> +----------------------
> +
> +For pre-relocation we simply call the driver model init function. Only
> +drivers marked with DM_FLAG_PRE_RELOC or the device tree 'dm,pre-reloc'
> +flag are initialised prior to relocation. This helps to reduce the driver
> +model overhead.
> +
> +Then post relocation we throw that away and re-init driver model again.
> +For drivers which require some sort of continuity between pre- and
> +post-relocation devices, we can provide access to the pre-relocation
> +device pointers, but this is not currently implemented (the root device
> +pointer is saved but not made available).
> +
> +
> Things to punt for later
> ------------------------
>
> - SPL support - this will have to be present before many drivers can be
> converted, but it seems like we can add it once we are happy with the
> core implementation.
> -- Pre-relocation support - similar story
>
> -That is not to say that no thinking has gone into these - in fact there
> +That is not to say that no thinking has gone into this - in fact there
> is quite a lot there. However, getting these right is non-trivial and
> there is a high cost associated with going down the wrong path.
>
> For SPL, it may be possible to fit in a simplified driver model with only
> bind and probe methods, to reduce size.
>
> -For pre-relocation we can simply call the driver model init function. Then
> -post relocation we throw that away and re-init driver model again. For drivers
> -which require some sort of continuity between pre- and post-relocation
> -devices, we can provide access to the pre-relocation device pointers.
> -
> Uclasses are statically numbered at compile time. It would be possible to
> change this to dynamic numbering, but then we would require some sort of
> lookup service, perhaps searching by name. This is slightly less efficient
> diff --git a/drivers/core/device.c b/drivers/core/device.c
> index 55ba281..2c2634e 100644
> --- a/drivers/core/device.c
> +++ b/drivers/core/device.c
> @@ -129,14 +129,16 @@ fail_bind:
> return ret;
> }
>
> -int device_bind_by_name(struct device *parent, const struct driver_info *info,
> - struct device **devp)
> +int device_bind_by_name(struct device *parent, bool pre_reloc_only,
> + const struct driver_info *info, struct device **devp)
> {
> struct driver *drv;
>
> drv = lists_driver_lookup_name(info->name);
> if (!drv)
> return -ENOENT;
> + if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC))
> + return -EPERM;
>
> return device_bind(parent, drv, info->name, (void *)info->platdata,
> -1, devp);
> diff --git a/drivers/core/lists.c b/drivers/core/lists.c
> index 6a53362..9e38993 100644
> --- a/drivers/core/lists.c
> +++ b/drivers/core/lists.c
> @@ -61,7 +61,7 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
> return NULL;
> }
>
> -int lists_bind_drivers(struct device *parent)
> +int lists_bind_drivers(struct device *parent, bool pre_reloc_only)
> {
> struct driver_info *info =
> ll_entry_start(struct driver_info, driver_info);
> @@ -72,8 +72,8 @@ int lists_bind_drivers(struct device *parent)
> int ret;
>
> for (entry = info; entry != info + n_ents; entry++) {
> - ret = device_bind_by_name(parent, entry, &dev);
> - if (ret) {
> + ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev);
> + if (ret && ret != -EPERM) {
> dm_warn("No match for driver '%s'\n", entry->name);
> if (!result || ret != -ENOENT)
> result = ret;
> diff --git a/drivers/core/root.c b/drivers/core/root.c
> index 4873e7b..83299f7 100644
> --- a/drivers/core/root.c
> +++ b/drivers/core/root.c
> @@ -45,7 +45,7 @@ int dm_init(void)
> }
> INIT_LIST_HEAD(&DM_UCLASS_ROOT());
>
> - ret = device_bind_by_name(NULL, &root_info, &DM_ROOT());
> + ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT());
> if (ret)
> return ret;
> ret = device_probe(DM_ROOT());
> @@ -63,11 +63,11 @@ int dm_uninit(void)
> return 0;
> }
>
> -int dm_scan_platdata(void)
> +int dm_scan_platdata(bool pre_reloc_only)
> {
> int ret;
>
> - ret = lists_bind_drivers(DM_ROOT());
> + ret = lists_bind_drivers(DM_ROOT(), pre_reloc_only);
> if (ret == -ENOENT) {
> dm_warn("Some drivers were not found\n");
> ret = 0;
> @@ -79,7 +79,7 @@ int dm_scan_platdata(void)
> }
>
> #ifdef CONFIG_OF_CONTROL
> -int dm_scan_fdt(const void *blob)
> +int dm_scan_fdt(const void *blob, bool pre_reloc_only)
> {
> int offset = 0;
> int ret = 0, err;
> @@ -88,6 +88,9 @@ int dm_scan_fdt(const void *blob)
> do {
> offset = fdt_next_node(blob, offset, &depth);
> if (offset > 0 && depth == 1) {
> + if (pre_reloc_only &&
> + !fdt_getprop(blob, offset, "dm,pre-reloc", NULL))
> + continue;
> err = lists_bind_fdt(gd->dm_root, blob, offset);
> if (err && !ret)
> ret = err;
> diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
> index e95b7a9..fde67bb 100644
> --- a/include/dm/device-internal.h
> +++ b/include/dm/device-internal.h
> @@ -45,12 +45,14 @@ int device_bind(struct device *parent, struct driver *drv,
> * tree.
> *
> * @parent: Pointer to device's parent
> + * @pre_reloc_only: If true, bind the driver only if its DM_INIT_F flag is set.
> + * If false bind the driver always.
> * @info: Name and platdata for this device
> * @devp: Returns a pointer to the bound device
> * @return 0 if OK, -ve on error
> */
> -int device_bind_by_name(struct device *parent, const struct driver_info *info,
> - struct device **devp);
> +int device_bind_by_name(struct device *parent, bool pre_reloc_only,
> + const struct driver_info *info, struct device **devp);
>
> /**
> * device_probe() - Probe a device, activating it
> diff --git a/include/dm/device.h b/include/dm/device.h
> index b048b66..32650fd 100644
> --- a/include/dm/device.h
> +++ b/include/dm/device.h
> @@ -23,6 +23,9 @@ struct driver_info;
> /* DM is responsible for allocating and freeing platdata */
> #define DM_FLAG_ALLOC_PDATA (1 << 1)
>
> +/* DM should init this device prior to relocation */
> +#define DM_FLAG_PRE_RELOC (1 << 2)
> +
> /**
> * struct device - An instance of a driver
> *
> @@ -117,6 +120,7 @@ struct device_id {
> * ops: Driver-specific operations. This is typically a list of function
> * pointers defined by the driver, to implement driver functions required by
> * the uclass.
> + * @flags: driver flags - see DM_FLAGS_...
> */
> struct driver {
> char *name;
> @@ -130,6 +134,7 @@ struct driver {
> int priv_auto_alloc_size;
> int platdata_auto_alloc_size;
> const void *ops; /* driver-specific operations */
> + uint32_t flags;
> };
>
> /* Declare a new U-Boot driver */
> diff --git a/include/dm/lists.h b/include/dm/lists.h
> index d0b720b..13e025e 100644
> --- a/include/dm/lists.h
> +++ b/include/dm/lists.h
> @@ -42,7 +42,7 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id);
> * @early_only: If true, bind only drivers with the DM_INIT_F flag. If false
> * bind all drivers.
> */
> -int lists_bind_drivers(struct device *parent);
> +int lists_bind_drivers(struct device *parent, bool pre_reloc_only);
>
> /**
> * lists_bind_fdt() - bind a device tree node
> diff --git a/include/dm/root.h b/include/dm/root.h
> index 1631d5d..30ebe6c 100644
> --- a/include/dm/root.h
> +++ b/include/dm/root.h
> @@ -26,9 +26,11 @@ struct device *dm_root(void);
> *
> * This scans all available platdata and creates drivers for each
> *
> + * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
> + * flag. If false bind all drivers.
> * @return 0 if OK, -ve on error
> */
> -int dm_scan_platdata(void);
> +int dm_scan_platdata(bool pre_reloc_only);
>
> /**
> * dm_scan_fdt() - Scan the device tree and bind drivers
> @@ -36,9 +38,11 @@ int dm_scan_platdata(void);
> * This scans the device tree and creates a driver for each node
> *
> * @blob: Pointer to device tree blob
> + * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
> + * flag. If false bind all drivers.
> * @return 0 if OK, -ve on error
> */
> -int dm_scan_fdt(const void *blob);
> +int dm_scan_fdt(const void *blob, bool pre_reloc_only);
>
> /**
> * dm_init() - Initialise Driver Model structures
> diff --git a/test/dm/core.c b/test/dm/core.c
> index a889fad..63bb561 100644
> --- a/test/dm/core.c
> +++ b/test/dm/core.c
> @@ -25,6 +25,7 @@ enum {
> TEST_INTVAL2 = 3,
> TEST_INTVAL3 = 6,
> TEST_INTVAL_MANUAL = 101112,
> + TEST_INTVAL_PRE_RELOC = 7,
> };
>
> static const struct dm_test_pdata test_pdata[] = {
> @@ -37,6 +38,10 @@ static const struct dm_test_pdata test_pdata_manual = {
> .ping_add = TEST_INTVAL_MANUAL,
> };
>
> +static const struct dm_test_pdata test_pdata_pre_reloc = {
> + .ping_add = TEST_INTVAL_PRE_RELOC,
> +};
> +
> U_BOOT_DEVICE(dm_test_info1) = {
> .name = "test_drv",
> .platdata = &test_pdata[0],
> @@ -57,6 +62,11 @@ static struct driver_info driver_info_manual = {
> .platdata = &test_pdata_manual,
> };
>
> +static struct driver_info driver_info_pre_reloc = {
> + .name = "test_pre_reloc_drv",
> + .platdata = &test_pdata_manual,
> +};
> +
> /* Test that binding with platdata occurs correctly */
> static int dm_test_autobind(struct dm_test_state *dms)
> {
> @@ -71,7 +81,7 @@ static int dm_test_autobind(struct dm_test_state *dms)
> ut_asserteq(0, list_count_items(&gd->dm_root->child_head));
> ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]);
>
> - ut_assertok(dm_scan_platdata());
> + ut_assertok(dm_scan_platdata(false));
>
> /* We should have our test class now at least, plus more children */
> ut_assert(1 < list_count_items(&gd->uclass_root));
> @@ -181,7 +191,7 @@ static int dm_test_lifecycle(struct dm_test_state *dms)
>
> memcpy(op_count, dm_testdrv_op_count, sizeof(op_count));
>
> - ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
> + ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
> &dev));
> ut_assert(dev);
> ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND]
> @@ -232,15 +242,15 @@ static int dm_test_ordering(struct dm_test_state *dms)
> struct device *dev, *dev_penultimate, *dev_last, *test_dev;
> int pingret;
>
> - ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
> + ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
> &dev));
> ut_assert(dev);
>
> /* Bind two new devices (numbers 4 and 5) */
> - ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
> + ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
> &dev_penultimate));
> ut_assert(dev_penultimate);
> - ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
> + ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
> &dev_last));
> ut_assert(dev_last);
>
> @@ -255,7 +265,8 @@ static int dm_test_ordering(struct dm_test_state *dms)
> ut_assert(dev_last == test_dev);
>
> /* Add back the original device 3, now in position 5 */
> - ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, &dev));
> + ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
> + &dev));
> ut_assert(dev);
>
> /* Try ping */
> @@ -375,8 +386,8 @@ static int dm_test_leak(struct dm_test_state *dms)
> if (!start.uordblks)
> puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n");
>
> - ut_assertok(dm_scan_platdata());
> - ut_assertok(dm_scan_fdt(gd->fdt_blob));
> + ut_assertok(dm_scan_platdata(false));
> + ut_assertok(dm_scan_fdt(gd->fdt_blob, false));
>
> /* Scanning the uclass is enough to probe all the devices */
> for (id = UCLASS_ROOT; id < UCLASS_COUNT; id++) {
> @@ -444,8 +455,8 @@ static int create_children(struct dm_test_state *dms, struct device *parent,
> for (i = 0; i < count; i++) {
> struct dm_test_pdata *pdata;
>
> - ut_assertok(device_bind_by_name(parent, &driver_info_manual,
> - &dev));
> + ut_assertok(device_bind_by_name(parent, false,
> + &driver_info_manual, &dev));
> pdata = calloc(1, sizeof(*pdata));
> pdata->ping_add = key + i;
> dev->platdata = pdata;
> @@ -542,3 +553,19 @@ static int dm_test_children(struct dm_test_state *dms)
> return 0;
> }
> DM_TEST(dm_test_children, 0);
> +
> +static int dm_test_pre_reloc(struct dm_test_state *dms)
> +{
> + struct device *dev;
> +
> + /* The normal driver should refuse to bind before relocation */
> + ut_asserteq(-EPERM, device_bind_by_name(dms->root, true,
> + &driver_info_manual, &dev));
> +
> + /* But this one is marked pre-reloc */
> + ut_assertok(device_bind_by_name(dms->root, true,
> + &driver_info_pre_reloc, &dev));
> +
> + return 0;
> +}
> +DM_TEST(dm_test_pre_reloc, 0);
> diff --git a/test/dm/test-driver.c b/test/dm/test-driver.c
> index c4be8a1..e7fb199 100644
> --- a/test/dm/test-driver.c
> +++ b/test/dm/test-driver.c
> @@ -144,3 +144,14 @@ U_BOOT_DRIVER(test_manual_drv) = {
> .remove = test_manual_remove,
> .unbind = test_manual_unbind,
> };
> +
> +U_BOOT_DRIVER(test_pre_reloc_drv) = {
> + .name = "test_pre_reloc_drv",
> + .id = UCLASS_TEST,
> + .ops = &test_manual_ops,
> + .bind = test_manual_bind,
> + .probe = test_manual_probe,
> + .remove = test_manual_remove,
> + .unbind = test_manual_unbind,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
> index e1d982f..d6f5bb8 100644
> --- a/test/dm/test-fdt.c
> +++ b/test/dm/test-fdt.c
> @@ -99,7 +99,7 @@ static int dm_test_fdt(struct dm_test_state *dms)
> int ret;
> int i;
>
> - ret = dm_scan_fdt(gd->fdt_blob);
> + ret = dm_scan_fdt(gd->fdt_blob, false);
> ut_assert(!ret);
>
> ret = uclass_get(UCLASS_TEST_FDT, &uc);
> @@ -142,3 +142,21 @@ static int dm_test_fdt(struct dm_test_state *dms)
> return 0;
> }
> DM_TEST(dm_test_fdt, 0);
> +
> +static int dm_test_fdt_pre_reloc(struct dm_test_state *dms)
> +{
> + struct uclass *uc;
> + int ret;
> +
> + ret = dm_scan_fdt(gd->fdt_blob, true);
> + ut_assert(!ret);
> +
> + ret = uclass_get(UCLASS_TEST_FDT, &uc);
> + ut_assert(!ret);
> +
> + /* These is only one pre-reloc device */
> + ut_asserteq(1, list_count_items(&uc->dev_head));
> +
> + return 0;
> +}
> +DM_TEST(dm_test_fdt_pre_reloc, 0);
> diff --git a/test/dm/test-main.c b/test/dm/test-main.c
> index 828ed46..49805eb 100644
> --- a/test/dm/test-main.c
> +++ b/test/dm/test-main.c
> @@ -89,11 +89,11 @@ int dm_test_main(void)
> ut_assertok(dm_test_init(dms));
>
> if (test->flags & DM_TESTF_SCAN_PDATA)
> - ut_assertok(dm_scan_platdata());
> + ut_assertok(dm_scan_platdata(false));
> if (test->flags & DM_TESTF_PROBE_TEST)
> ut_assertok(do_autoprobe(dms));
> if (test->flags & DM_TESTF_SCAN_FDT)
> - ut_assertok(dm_scan_fdt(gd->fdt_blob));
> + ut_assertok(dm_scan_fdt(gd->fdt_blob, false));
>
> if (test->func(dms))
> break;
> diff --git a/test/dm/test.dts b/test/dm/test.dts
> index ec5364f..b2eaddd 100644
> --- a/test/dm/test.dts
> +++ b/test/dm/test.dts
> @@ -6,10 +6,21 @@
> #address-cells = <1>;
> #size-cells = <0>;
>
> + alias {
> + console = <&uart0>;
> + };
> +
> + uart0: serial {
> + compatible = "sandbox,serial";
> + dm,pre-reloc;
> + //text-colour = "cyan";
> + };
> +
> a-test {
> reg = <0>;
> compatible = "denx,u-boot-fdt-test";
> ping-add = <0>;
> + dm,pre-reloc;
> };
>
> junk {
> --
> 1.9.1.423.g4596e3a
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot