[U-Boot] [PATCH 1/3] dm: core: Clarify uclass_first/next_device() comments

These are not as clear as they could be. Tidy them up a bit. Also fix a tiny code-style nit.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/uclass.c | 3 +-- include/dm/uclass.h | 13 +++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 04fb45b01a..42613031ff 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -342,8 +342,7 @@ int uclass_get_device_by_driver(enum uclass_id id, return -ENODEV; }
-int uclass_get_device_tail(struct udevice *dev, int ret, - struct udevice **devp) +int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) { if (ret) return ret; diff --git a/include/dm/uclass.h b/include/dm/uclass.h index b583aa869b..63da868ae5 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -224,8 +224,13 @@ int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv, * * The device returned is probed if necessary, and ready for use * + * This function is useful to start iterating through a list of devices which + * are functioning correctly and can be probed. + * * @id: Uclass ID to look up - * @devp: Returns pointer to the first device in that uclass, or NULL if none + * @devp: Returns pointer to the first device in that uclass if no error + * occurred, or NULL if there is no first device, or an error occurred with + * that device. * @return 0 if OK (found or not found), other -ve on error */ int uclass_first_device(enum uclass_id id, struct udevice **devp); @@ -246,8 +251,12 @@ int uclass_first_device_err(enum uclass_id id, struct udevice **devp); * * The device returned is probed if necessary, and ready for use * + * This function is useful to start iterating through a list of devices which + * are functioning correctly and can be probed. + * * @devp: On entry, pointer to device to lookup. On exit, returns pointer - * to the next device in the same uclass, or NULL if none + * to the next device in the uclass if no error occurred, or NULL if there is + * no next device, or an error occurred with that next device. * @return 0 if OK (found or not found), other -ve on error */ int uclass_next_device(struct udevice **devp);

Add some tests which check the behaviour of uclass_first_device() and uclass_next_device() when probing of a device fails.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/sandbox/dts/test.dts | 19 +++++++++++++ include/dm/uclass-id.h | 1 + test/dm/test-fdt.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index fff175d1b7..e5cdbab965 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -418,6 +418,25 @@ }; }; }; + + probing { + compatible = "simple-bus"; + test1 { + compatible = "denx,u-boot-probe-test"; + }; + + test2 { + compatible = "denx,u-boot-probe-test"; + }; + + test3 { + compatible = "denx,u-boot-probe-test"; + }; + + test4 { + compatible = "denx,u-boot-probe-test"; + }; + }; };
#include "sandbox_pmic.dtsi" diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 1b635e4110..316304744e 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -18,6 +18,7 @@ enum uclass_id { UCLASS_TEST, UCLASS_TEST_FDT, UCLASS_TEST_BUS, + UCLASS_TEST_PROBE, UCLASS_SPI_EMUL, /* sandbox SPI device emulator */ UCLASS_I2C_EMUL, /* sandbox I2C device emulator */ UCLASS_PCI_EMUL, /* sandbox PCI device emulator */ diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 3048a7b890..1770b86fed 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -12,6 +12,7 @@ #include <asm/io.h> #include <dm/test.h> #include <dm/root.h> +#include <dm/device-internal.h> #include <dm/uclass-internal.h> #include <dm/util.h> #include <test/ut.h> @@ -99,6 +100,36 @@ UCLASS_DRIVER(testfdt) = { .flags = DM_UC_FLAG_SEQ_ALIAS, };
+struct dm_testprobe_pdata { + int probe_err; +}; + +static int testprobe_drv_probe(struct udevice *dev) +{ + struct dm_testprobe_pdata *pdata = dev_get_platdata(dev); + + return pdata->probe_err; +} + +static const struct udevice_id testprobe_ids[] = { + { .compatible = "denx,u-boot-probe-test" }, + { } +}; + +U_BOOT_DRIVER(testprobe_drv) = { + .name = "testprobe_drv", + .of_match = testprobe_ids, + .id = UCLASS_TEST_PROBE, + .probe = testprobe_drv_probe, + .platdata_auto_alloc_size = sizeof(struct dm_testprobe_pdata), +}; + +UCLASS_DRIVER(testprobe) = { + .name = "testprobe", + .id = UCLASS_TEST_PROBE, + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; + int dm_check_devices(struct unit_test_state *uts, int num_devices) { struct udevice *dev; @@ -266,3 +297,44 @@ static int dm_test_fdt_offset(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_fdt_offset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/** + * Test various error conditions with uclass_first_device() and + * uclass_next_device() + */ +static int dm_test_first_next_device(struct unit_test_state *uts) +{ + struct dm_testprobe_pdata *pdata; + struct udevice *dev, *parent = NULL; + int count; + int ret; + + /* There should be 4 devices */ + for (ret = uclass_first_device(UCLASS_TEST_PROBE, &dev), count = 0; + dev; + ret = uclass_next_device(&dev)) { + count++; + parent = dev_get_parent(dev); + } + ut_assertok(ret); + ut_asserteq(4, count); + + /* Remove them and try again, with an error on the second one */ + ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 1, &dev)); + pdata = dev_get_platdata(dev); + pdata->probe_err = -ENOMEM; + device_remove(parent, DM_REMOVE_NORMAL); + ut_assertok(uclass_first_device(UCLASS_TEST_PROBE, &dev)); + ut_asserteq(-ENOMEM, uclass_next_device(&dev)); + ut_asserteq_ptr(dev, NULL); + + /* Now an error on the first one */ + ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 0, &dev)); + pdata = dev_get_platdata(dev); + pdata->probe_err = -ENOENT; + device_remove(parent, DM_REMOVE_NORMAL); + ut_asserteq(-ENOENT, uclass_first_device(UCLASS_TEST_PROBE, &dev)); + + return 0; +} +DM_TEST(dm_test_first_next_device, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

Sometimes it is useful to iterate through all devices in a uclass and skip over those which do not work correctly (e.g fail to probe). Add two new functions to provide this feature.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/uclass.c | 29 +++++++++++++++++++++++++++++ include/dm/uclass.h | 31 +++++++++++++++++++++++++++++++ test/dm/test-fdt.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 42613031ff..eac30d965d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -456,6 +456,35 @@ int uclass_next_device(struct udevice **devp) return uclass_get_device_tail(dev, ret, devp); }
+struct udevice *uclass_first_ok_device(enum uclass_id id) +{ + struct udevice *dev; + int ret; + + ret = uclass_find_first_device(id, &dev); + if (!dev) + return NULL; + ret = device_probe(dev); + if (ret) + return uclass_next_ok_device(dev); + + return dev; +} + +struct udevice *uclass_next_ok_device(struct udevice *dev) +{ + int ret; + + do { + ret = uclass_find_next_device(&dev); + if (!dev) + return NULL; + ret = device_probe(dev); + } while (ret); + + return dev; +} + int uclass_bind_device(struct udevice *dev) { struct uclass *uc; diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 63da868ae5..b53fba5c2c 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -262,6 +262,37 @@ int uclass_first_device_err(enum uclass_id id, struct udevice **devp); int uclass_next_device(struct udevice **devp);
/** + * uclass_first_ok_device() - Get the first functioning device in a uclass + * + * The device returned is probed if necessary, and ready for use. Any devices + * which fail to probe are skipped. It is not possible to discover the error + * for any devices which fail to probe. You should use uclass_first_device() + * if you care about that. + * + * @id: Uclass ID to look up + * @return: pointer to the first functioning device in that uclass if one + * can be found, or NULL if there is no such device (e.g. if the rest of the + * devices cannot be probed). + */ +struct udevice *uclass_first_ok_device(enum uclass_id id); + +/** + * uclass_next_ok_device() - Get the next functioning device in a uclass + * + * The device returned is probed if necessary, and ready for use. Any devices + * which fail to probe are skipped. It is not possible to discover the error + * for any devices which fail to probe. You should use uclass_next_device() + * if you care about that. + * + * @dev: Pointer to the previous device (e.g. returned from + * uclass_first_ok_device() or uclass_next_ok_device() + * @return: pointer to the next functioning device in the same uclass if one + * can be found, or NULL if there is no such device (e.g. if the rest of the + * devices cannot be probed). + */ +struct udevice *uclass_next_ok_device(struct udevice *dev); + +/** * uclass_resolve_seq() - Resolve a device's sequence number * * On entry dev->seq is -1, and dev->req_seq may be -1 (to allocate a diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 1770b86fed..69c1a89246 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -338,3 +338,48 @@ static int dm_test_first_next_device(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_first_next_device, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test uclass_first_ok_device() and uclass_next_ok_device() */ +static int dm_test_first_next_ok_device(struct unit_test_state *uts) +{ + struct dm_testprobe_pdata *pdata; + struct udevice *dev, *parent = NULL, *devlist[4]; + int count; + + /* There should be 4 devices */ + for (dev = uclass_first_ok_device(UCLASS_TEST_PROBE), count = 0; + dev; + dev = uclass_next_ok_device(dev)) { + devlist[count++] = dev; + parent = dev_get_parent(dev); + } + ut_asserteq(4, count); + + /* Remove them and try again, with an error on the second one */ + pdata = dev_get_platdata(devlist[1]); + pdata->probe_err = -ENOMEM; + device_remove(parent, DM_REMOVE_NORMAL); + ut_asserteq_ptr(devlist[0], uclass_first_ok_device(UCLASS_TEST_PROBE)); + ut_asserteq_ptr(devlist[2], uclass_next_ok_device(devlist[0])); + ut_asserteq_ptr(devlist[3], uclass_next_ok_device(devlist[2])); + ut_asserteq_ptr(NULL, uclass_next_ok_device(devlist[3])); + + /* Now an error on the first one */ + pdata = dev_get_platdata(devlist[0]); + pdata->probe_err = -ENOENT; + device_remove(parent, DM_REMOVE_NORMAL); + ut_asserteq_ptr(devlist[2], uclass_first_ok_device(UCLASS_TEST_PROBE)); + ut_asserteq_ptr(devlist[3], uclass_next_ok_device(devlist[2])); + ut_asserteq_ptr(NULL, uclass_next_ok_device(devlist[3])); + + /* Now errors on all */ + pdata = dev_get_platdata(devlist[2]); + pdata->probe_err = -ENOENT; + pdata = dev_get_platdata(devlist[3]); + pdata->probe_err = -ENOENT; + device_remove(parent, DM_REMOVE_NORMAL); + ut_asserteq_ptr(NULL, uclass_first_ok_device(UCLASS_TEST_PROBE)); + + return 0; +} +DM_TEST(dm_test_first_next_ok_device, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
participants (1)
-
Simon Glass