diff options
author | Tom Rini <trini@konsulko.com> | 2021-11-28 20:38:01 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2021-11-28 20:38:13 -0500 |
commit | c087b5ad974441d1408c028eb7087d86b6d127e9 (patch) | |
tree | 1d5efba0d146dcd1a6a449963ea738cc6e4ff73e | |
parent | 1943f2a2a7c58b76812fcad2d3012036af7464ce (diff) | |
parent | 452e8c9086a9f95739582da5ccc2130e4bf1ae8b (diff) | |
download | u-boot-WIP/28Nov2021.zip u-boot-WIP/28Nov2021.tar.gz u-boot-WIP/28Nov2021.tar.bz2 |
Merge tag 'dm-pull-28nov21' of https://source.denx.de/u-boot/custodians/u-boot-dm into nextWIP/28Nov2021
SPI flash documentation and tidy-ups
Various driver model enhancements
Fix up some missing unit tests with pytest
32 files changed, 670 insertions, 69 deletions
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index b72dafc..873f85a 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -211,6 +211,16 @@ int os_map_file(const char *pathname, int os_flags, void **bufp, int *sizep) return 0; } +int os_unmap(void *buf, int size) +{ + if (munmap(buf, size)) { + printf("Can't unmap %p %x\n", buf, size); + return -EIO; + } + + return 0; +} + /* Restore tty state when we exit */ static struct termios orig_term; static bool term_setup; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8cd688e..e5261bb 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -207,6 +207,7 @@ test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>; test5-gpios = <&gpio_a 19>; + bool-value; int-value = <1234>; uint-value = <(-1234)>; int64-value = /bits/ 64 <0x1111222233334444>; @@ -244,12 +244,12 @@ static int do_verify_mbr(struct blk_desc *dev, const char *str) for (i = 0; i < count; i++) { struct disk_partition p; - if (part_get_info(dev, i+1, &p)) + if (part_get_info(dev, i + 1, &p)) goto fail; - if ((partitions[i].size && p.size < partitions[i].size) || - (partitions[i].start && p.start < partitions[i].start) || - (p.sys_ind != partitions[i].sys_ind)) + if ((partitions[i].size && p.size != partitions[i].size) || + (partitions[i].start && p.start != partitions[i].start) || + p.sys_ind != partitions[i].sys_ind) goto fail; } ret = 0; @@ -384,7 +384,6 @@ static int do_spi_protect(int argc, char *const argv[]) return ret == 0 ? 0 : 1; } -#ifdef CONFIG_CMD_SF_TEST enum { STAGE_ERASE, STAGE_CHECK, @@ -394,7 +393,7 @@ enum { STAGE_COUNT, }; -static char *stage_name[STAGE_COUNT] = { +static const char *stage_name[STAGE_COUNT] = { "erase", "check", "write", @@ -548,7 +547,6 @@ static int do_spi_flash_test(int argc, char *const argv[]) return 0; } -#endif /* CONFIG_CMD_SF_TEST */ static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) @@ -582,10 +580,8 @@ static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc, ret = do_spi_flash_erase(argc, argv); else if (strcmp(cmd, "protect") == 0) ret = do_spi_protect(argc, argv); -#ifdef CONFIG_CMD_SF_TEST - else if (!strcmp(cmd, "test")) + else if (IS_ENABLED(CONFIG_CMD_SF_TEST) && !strcmp(cmd, "test")) ret = do_spi_flash_test(argc, argv); -#endif else ret = -1; @@ -597,16 +593,8 @@ usage: return CMD_RET_USAGE; } -#ifdef CONFIG_CMD_SF_TEST -#define SF_TEST_HELP "\nsf test offset len " \ - "- run a very basic destructive test" -#else -#define SF_TEST_HELP -#endif - -U_BOOT_CMD( - sf, 5, 1, do_spi_flash, - "SPI flash sub-system", +#ifdef CONFIG_SYS_LONGHELP +static const char long_help[] = "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" " and chip select\n" "sf read addr offset|partition len - read `len' bytes starting at\n" @@ -622,6 +610,14 @@ U_BOOT_CMD( " at `addr' to flash at `offset'\n" " or to start of mtd `partition'\n" "sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n" - " at address 'sector'\n" - SF_TEST_HELP + " at address 'sector'" +#ifdef CONFIG_CMD_SF_TEST + "\nsf test offset len - run a very basic destructive test" +#endif +#endif /* CONFIG_SYS_LONGHELP */ + ; + +U_BOOT_CMD( + sf, 5, 1, do_spi_flash, + "SPI flash sub-system", long_help ); diff --git a/common/Kconfig b/common/Kconfig index fdcf453..fede7e4 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -32,6 +32,16 @@ config CONSOLE_RECORD_OUT_SIZE more data will be recorded until some is removed. The buffer is allocated immediately after the malloc() region is ready. +config CONSOLE_RECORD_OUT_SIZE_F + hex "Output buffer size before relocation" + depends on CONSOLE_RECORD + default 0x400 if CONSOLE_RECORD + help + Set the size of the console output buffer before relocation. When + this fills up, no more data will be recorded until some is removed. + The buffer is allocated immediately after the early malloc() region is + ready. + config CONSOLE_RECORD_IN_SIZE hex "Input buffer size" depends on CONSOLE_RECORD diff --git a/common/console.c b/common/console.c index 0013d18..0c9099c 100644 --- a/common/console.c +++ b/common/console.c @@ -735,7 +735,9 @@ int console_record_init(void) int ret; ret = membuff_new((struct membuff *)&gd->console_out, - CONFIG_CONSOLE_RECORD_OUT_SIZE); + gd->flags & GD_FLG_RELOC ? + CONFIG_CONSOLE_RECORD_OUT_SIZE : + CONFIG_CONSOLE_RECORD_OUT_SIZE_F); if (ret) return ret; ret = membuff_new((struct membuff *)&gd->console_in, diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 7cc76bf..f184723 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -203,6 +203,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y +CONFIG_HEXDUMP=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y diff --git a/disk/part_dos.c b/disk/part_dos.c index 9e29aa6..94fae71 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -459,10 +459,12 @@ int layout_mbr_partitions(struct disk_partition *p, int count, ext = &p[i]; } - if (i >= 4 && !ext) { - printf("%s: extended partition is needed for more than 4 partitions\n", - __func__); - return -1; + if (count < 4) + return 0; + + if (!ext) { + log_err("extended partition is needed for more than 4 partitions\n"); + return -EINVAL; } /* calculate extended volumes start and size if needed */ diff --git a/doc/device-tree-bindings/mmc/sandbox,mmc.txt b/doc/device-tree-bindings/mmc/sandbox,mmc.txt new file mode 100644 index 0000000..1170bcd --- /dev/null +++ b/doc/device-tree-bindings/mmc/sandbox,mmc.txt @@ -0,0 +1,18 @@ +Sandbox MMC +=========== + +Required properties: +- compatible : "sandbox,mmc" + +Optional properties: +- filename : Name of backing file, if any. This is mapped into the MMC device + so can be used to provide a filesystem or other test data + + +Example +------- + +mmc2 { + compatible = "sandbox,mmc"; + non-removable; +}; diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 04dea9f..3905540 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -45,6 +45,7 @@ Shell commands qfw reset sbi + sf scp03 setexpr size diff --git a/doc/usage/sf.rst b/doc/usage/sf.rst new file mode 100644 index 0000000..71bd1be --- /dev/null +++ b/doc/usage/sf.rst @@ -0,0 +1,245 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +sf command +========== + +Synopis +------- + +:: + + sf probe [[[<bus>:]<cs>] [<hz> [<mode>]]] + sf read <addr> <offset>|<partition> <len> + sf write <addr> <offset>|<partition> <len> + sf erase <offset>|<partition> <len> + sf update <addr> <offset>|<partition> <len> + sf protect lock|unlock <sector> <len> + sf test <offset>|<partition> <len> + +Description +----------- + +The *sf* command is used to access SPI flash, supporting read/write/erase and +a few other functions. + +Probe +----- + +The flash must first be probed with *sf probe* before any of the other +subcommands can be used. All of the parameters are optional: + +bus + SPI bus number containing the SPI-flash chip, e.g. 0. If you don't know + the number, you can use 'dm uclass' to see all the spi devices, + and check the value for 'seq' for each one (here 0 and 2):: + + uclass 89: spi + 0 spi@0 @ 05484960, seq 0 + 1 spi@1 @ 05484b40, seq 2 + +cs + SPI chip-select to use for the chip. This is often 0 and can be omitted, + but in some cases multiple slaves are attached to a SPI controller, + selected by a chip-select line for each one. + +hz + Speed of the SPI bus in hertz. This normally defaults to 100000, i.e. + 100KHz, which is very slow. Note that if the device exists in the + device tree, there might be a speed provided there, in which case this + setting is ignored. + +mode + SPI mode to use: + + ===== ================ + Mode Meaning + ===== ================ + 0 CPOL=0, CPHA=0 + 1 CPOL=0, CPHA=1 + 2 CPOL=1, CPHA=0 + 3 CPOL=1, CPHA=1 + ===== ================ + + Clock phase (CPHA) 0 means that data is transferred (sampled) on the + first clock edge; 1 means the second. + + Clock polarity (CPOL) controls the idle state of the clock, 0 for low, + 1 for high. + The active state is the opposite of idle. + + You may find this `SPI documentation`_ useful. + +Parameters for other subcommands (described below) are as follows: + +addr + Memory address to start transfer + +offset + Flash offset to start transfer + +partition + If the parameter is not numeric, it is assumed to be a partition + description in the format <dev_type><dev_num>,<part_num> which is not + covered here. This requires CONFIG_CMD_MTDPARTS. + +len + Number of bytes to transfer + +Read +~~~~ + +Use *sf read* to read from SPI flash to memory. The read will fail if an +attempt is made to read past the end of the flash. + + +Write +~~~~~ + +Use *sf write* to write from memory to SPI flash. The SPI flash should be +erased first, since otherwise the result is undefined. + +The write will fail if an attempt is made to read past the end of the flash. + + +Erase +~~~~~ + +Use *sf erase* to erase a region of SPI flash. The erase will fail if any part +of the region to be erased is protected or lies past the end of the flash. It +may also fail if the start offset or length are not aligned to an erase region +(e.g. 256 bytes). + + +Update +~~~~~~ + +Use *sf update* to automatically erase and update a region of SPI flash from +memory. This works a sector at a time (typical 4KB or 64KB). For each +sector it first checks if the sector already has the right data. If so it is +skipped. If not, the sector is erased and the new data written. Note that if +the length is not a multiple of the erase size, the space after the data in +the last sector will be erased. If the offset does not start at the beginning +of an erase block, the operation will fail. + +Speed statistics are shown including the number of bytes that were already +correct. + + +Protect +~~~~~~~ + +SPI-flash chips often have a protection feature where the chip is split up into +regions which can be locked or unlocked. With *sf protect* it is possible to +change these settings, if supported by the driver. + +lock|unlock + Selects whether to lock or unlock the sectors + +<sector> + Start sector number to lock/unlock. This may be the byte offset or some + other value, depending on the chip. + +<len> + Number of bytes to lock/unlock + + +Test +~~~~ + +A convenient and fast *sf test* subcommand provides a way to check that SPI +flash is working as expected. This works in four stages: + + * erase - erases the entire region + * check - checks that the region is erased + * write - writes a test pattern to the region, consisting of the U-Boot code + * read - reads back the test pattern to check that it was written correctly + +Memory is allocated for two buffers, each <len> bytes in size. At typical +size is 64KB to 1MB. The offset and size must be aligned to an erase boundary. + +Note that this test will fail if any part of the SPI flash is write-protected. + + +Examples +-------- + +This first example uses sandbox:: + + => sf probe + SF: Detected m25p16 with page size 256 Bytes, erase size 64 KiB, total 2 MiB + => sf read 1000 1100 80000 + device 0 offset 0x1100, size 0x80000 + SF: 524288 bytes @ 0x1100 Read: OK + => md 1000 + 00001000: edfe0dd0 f33a0000 78000000 84250000 ......:....x..%. + 00001010: 28000000 11000000 10000000 00000000 ...(............ + 00001020: 6f050000 0c250000 00000000 00000000 ...o..%......... + 00001030: 00000000 00000000 00000000 00000000 ................ + 00001040: 00000000 00000000 00000000 00000000 ................ + 00001050: 00000000 00000000 00000000 00000000 ................ + 00001060: 00000000 00000000 00000000 00000000 ................ + 00001070: 00000000 00000000 01000000 00000000 ................ + 00001080: 03000000 04000000 00000000 01000000 ................ + 00001090: 03000000 04000000 0f000000 01000000 ................ + 000010a0: 03000000 08000000 1b000000 646e6173 ............sand + 000010b0: 00786f62 03000000 08000000 21000000 box............! + 000010c0: 646e6173 00786f62 01000000 61696c61 sandbox.....alia + 000010d0: 00736573 03000000 07000000 2c000000 ses............, + 000010e0: 6332692f 00003040 03000000 07000000 /i2c@0.......... + 000010f0: 31000000 6963702f 00003040 03000000 ...1/pci@0...... + => sf erase 0 80000 + SF: 524288 bytes @ 0x0 Erased: OK + => sf read 1000 1100 80000 + device 0 offset 0x1100, size 0x80000 + SF: 524288 bytes @ 0x1100 Read: OK + => md 1000 + 00001000: ffffffff ffffffff ffffffff ffffffff ................ + 00001010: ffffffff ffffffff ffffffff ffffffff ................ + 00001020: ffffffff ffffffff ffffffff ffffffff ................ + 00001030: ffffffff ffffffff ffffffff ffffffff ................ + 00001040: ffffffff ffffffff ffffffff ffffffff ................ + 00001050: ffffffff ffffffff ffffffff ffffffff ................ + 00001060: ffffffff ffffffff ffffffff ffffffff ................ + 00001070: ffffffff ffffffff ffffffff ffffffff ................ + 00001080: ffffffff ffffffff ffffffff ffffffff ................ + 00001090: ffffffff ffffffff ffffffff ffffffff ................ + 000010a0: ffffffff ffffffff ffffffff ffffffff ................ + 000010b0: ffffffff ffffffff ffffffff ffffffff ................ + 000010c0: ffffffff ffffffff ffffffff ffffffff ................ + 000010d0: ffffffff ffffffff ffffffff ffffffff ................ + 000010e0: ffffffff ffffffff ffffffff ffffffff ................ + 000010f0: ffffffff ffffffff ffffffff ffffffff ................ + +This second example is running on coral, an x86 Chromebook:: + + => sf probe + SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB + => sf erase 300000 80000 + SF: 524288 bytes @ 0x300000 Erased: OK + => sf update 1110000 300000 80000 + device 0 offset 0x300000, size 0x80000 + 524288 bytes written, 0 bytes skipped in 0.457s, speed 1164578 B/s + + # This does nothing as the flash is already updated + => sf update 1110000 300000 80000 + device 0 offset 0x300000, size 0x80000 + 0 bytes written, 524288 bytes skipped in 0.196s, speed 2684354 B/s + => sf test 00000 80000 # try a protected region + SPI flash test: + Erase failed (err = -5) + Test failed + => sf test 800000 80000 + SPI flash test: + 0 erase: 18 ticks, 28444 KiB/s 227.552 Mbps + 1 check: 192 ticks, 2666 KiB/s 21.328 Mbps + 2 write: 227 ticks, 2255 KiB/s 18.040 Mbps + 3 read: 189 ticks, 2708 KiB/s 21.664 Mbps + Test passed + 0 erase: 18 ticks, 28444 KiB/s 227.552 Mbps + 1 check: 192 ticks, 2666 KiB/s 21.328 Mbps + 2 write: 227 ticks, 2255 KiB/s 18.040 Mbps + 3 read: 189 ticks, 2708 KiB/s 21.664 Mbps + + +.. _SPI documentation: + https://en.wikipedia.org/wiki/Serial_Peripheral_Interface diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 11d3959..69c50da 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -95,6 +95,9 @@ int device_unbind(struct udevice *dev) if (ret) return log_msg_ret("child unbind", ret); + ret = uclass_pre_unbind_device(dev); + if (ret) + return log_msg_ret("uc", ret); if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { free(dev_get_plat(dev)); dev_set_plat(dev, NULL); @@ -142,10 +145,8 @@ void device_free(struct udevice *dev) } if (dev->parent) { size = dev->parent->driver->per_child_auto; - if (!size) { - size = dev->parent->uclass->uc_drv-> - per_child_auto; - } + if (!size) + size = dev->parent->uclass->uc_drv->per_child_auto; if (size) { free(dev_get_parent_priv(dev)); dev_set_parent_priv(dev, NULL); diff --git a/drivers/core/device.c b/drivers/core/device.c index efd0717..aed093c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -902,15 +902,16 @@ int device_find_first_child_by_uclass(const struct udevice *parent, return -ENODEV; } -int device_find_child_by_name(const struct udevice *parent, const char *name, - struct udevice **devp) +int device_find_child_by_namelen(const struct udevice *parent, const char *name, + int len, struct udevice **devp) { struct udevice *dev; *devp = NULL; list_for_each_entry(dev, &parent->child_head, sibling_node) { - if (!strcmp(dev->name, name)) { + if (!strncmp(dev->name, name, len) && + strlen(dev->name) == len) { *devp = dev; return 0; } @@ -919,6 +920,12 @@ int device_find_child_by_name(const struct udevice *parent, const char *name, return -ENODEV; } +int device_find_child_by_name(const struct udevice *parent, const char *name, + struct udevice **devp) +{ + return device_find_child_by_namelen(parent, name, strlen(name), devp); +} + int device_first_child_err(struct udevice *parent, struct udevice **devp) { struct udevice *dev; diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 9960e6b..3707143 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -581,7 +581,8 @@ int of_property_match_string(const struct device_node *np, const char *propname, * @propname: name of the property to be searched. * @out_strs: output array of string pointers. * @sz: number of array elements to read. - * @skip: Number of strings to skip over at beginning of list. + * @skip: Number of strings to skip over at beginning of list (cannot be + * negative) * * Don't call this function directly. It is a utility helper for the * of_property_read_string*() family of functions. diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 08705ef..709bea2 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -456,6 +456,32 @@ int ofnode_read_string_count(ofnode node, const char *property) } } +int ofnode_read_string_list(ofnode node, const char *property, + const char ***listp) +{ + const char **prop; + int count; + int i; + + *listp = NULL; + count = ofnode_read_string_count(node, property); + if (count < 0) + return count; + if (!count) + return 0; + + prop = calloc(count + 1, sizeof(char *)); + if (!prop) + return -ENOMEM; + + for (i = 0; i < count; i++) + ofnode_read_string_index(node, property, i, &prop[i]); + prop[count] = NULL; + *listp = prop; + + return count; +} + static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, struct ofnode_phandle_args *out) { diff --git a/drivers/core/read.c b/drivers/core/read.c index 4307ca4..31f9e78 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -205,6 +205,12 @@ int dev_read_string_count(const struct udevice *dev, const char *propname) return ofnode_read_string_count(dev_ofnode(dev), propname); } +int dev_read_string_list(const struct udevice *dev, const char *propname, + const char ***listp) +{ + return ofnode_read_string_list(dev_ofnode(dev), propname, listp); +} + int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, const char *cells_name, int cell_count, int index, struct ofnode_phandle_args *out_args) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index c5a5095..2aa2143 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -180,20 +180,25 @@ void uclass_set_priv(struct uclass *uc, void *priv) uc->priv_ = priv; } -enum uclass_id uclass_get_by_name(const char *name) +enum uclass_id uclass_get_by_name_len(const char *name, int len) { int i; for (i = 0; i < UCLASS_COUNT; i++) { struct uclass_driver *uc_drv = lists_uclass_lookup(i); - if (uc_drv && !strcmp(uc_drv->name, name)) + if (uc_drv && !strncmp(uc_drv->name, name, len)) return i; } return UCLASS_INVALID; } +enum uclass_id uclass_get_by_name(const char *name) +{ + return uclass_get_by_name_len(name, strlen(name)); +} + int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp) { struct udevice *iter; @@ -682,7 +687,7 @@ err: } #if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) -int uclass_unbind_device(struct udevice *dev) +int uclass_pre_unbind_device(struct udevice *dev) { struct uclass *uc; int ret; @@ -694,7 +699,13 @@ int uclass_unbind_device(struct udevice *dev) return ret; } + return 0; +} + +int uclass_unbind_device(struct udevice *dev) +{ list_del(&dev->uclass_node); + return 0; } #endif @@ -783,6 +794,18 @@ int uclass_probe_all(enum uclass_id id) return 0; } +int uclass_id_count(enum uclass_id id) +{ + struct udevice *dev; + struct uclass *uc; + int count = 0; + + uclass_id_foreach_dev(id, dev, uc) + count++; + + return count; +} + UCLASS_DRIVER(nop) = { .id = UCLASS_NOP, .name = "nop", diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 3ee92d0..b80e838 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -320,7 +320,7 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) struct blk_desc *desc; struct udevice *dev; - device_find_first_child(mmc->dev, &dev); + device_find_first_child_by_uclass(mmc->dev, UCLASS_BLK, &dev); if (!dev) return NULL; desc = dev_get_uclass_plat(dev); @@ -425,7 +425,7 @@ int mmc_unbind(struct udevice *dev) { struct udevice *bdev; - device_find_first_child(dev, &bdev); + device_find_first_child_by_uclass(dev, UCLASS_BLK, &bdev); if (bdev) { device_remove(bdev, DM_REMOVE_NORMAL); device_unbind(bdev); diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index 895fbff..451fe4a 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -9,23 +9,26 @@ #include <errno.h> #include <fdtdec.h> #include <log.h> +#include <malloc.h> #include <mmc.h> +#include <os.h> #include <asm/test.h> struct sandbox_mmc_plat { struct mmc_config cfg; struct mmc mmc; + const char *fname; }; -#define MMC_CSIZE 0 -#define MMC_CMULT 8 /* 8 because the card is high-capacity */ -#define MMC_BL_LEN_SHIFT 10 -#define MMC_BL_LEN BIT(MMC_BL_LEN_SHIFT) -#define MMC_CAPACITY (((MMC_CSIZE + 1) << (MMC_CMULT + 2)) \ - * MMC_BL_LEN) /* 1 MiB */ +#define MMC_CMULT 8 /* 8 because the card is high-capacity */ +#define MMC_BL_LEN_SHIFT 10 +#define MMC_BL_LEN BIT(MMC_BL_LEN_SHIFT) +#define SIZE_MULTIPLE ((1 << (MMC_CMULT + 2)) * MMC_BL_LEN) struct sandbox_mmc_priv { - u8 buf[MMC_CAPACITY]; + char *buf; + int csize; /* CSIZE value to report */ + int size; }; /** @@ -60,8 +63,8 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, case MMC_CMD_SEND_CSD: cmd->response[0] = 0; cmd->response[1] = (MMC_BL_LEN_SHIFT << 16) | - ((MMC_CSIZE >> 16) & 0x3f); - cmd->response[2] = (MMC_CSIZE & 0xffff) << 16; + ((priv->csize >> 16) & 0x3f); + cmd->response[2] = (priv->csize & 0xffff) << 16; cmd->response[3] = 0; break; case SD_CMD_SWITCH_FUNC: { @@ -143,6 +146,8 @@ static int sandbox_mmc_of_to_plat(struct udevice *dev) struct blk_desc *blk; int ret; + plat->fname = dev_read_string(dev, "filename"); + ret = mmc_of_parse(dev, cfg); if (ret) return ret; @@ -156,10 +161,46 @@ static int sandbox_mmc_of_to_plat(struct udevice *dev) static int sandbox_mmc_probe(struct udevice *dev) { struct sandbox_mmc_plat *plat = dev_get_plat(dev); + struct sandbox_mmc_priv *priv = dev_get_priv(dev); + int ret; + + if (plat->fname) { + ret = os_map_file(plat->fname, OS_O_RDWR | OS_O_CREAT, + (void **)&priv->buf, &priv->size); + if (ret) { + log_err("%s: Unable to map file '%s'\n", dev->name, + plat->fname); + return ret; + } + priv->csize = priv->size / SIZE_MULTIPLE - 1; + } else { + priv->csize = 0; + priv->size = (priv->csize + 1) * SIZE_MULTIPLE; /* 1 MiB */ + + priv->buf = malloc(priv->size); + if (!priv->buf) { + log_err("%s: Not enough memory (%x bytes)\n", + dev->name, priv->size); + return -ENOMEM; + } + } return mmc_init(&plat->mmc); } +static int sandbox_mmc_remove(struct udevice *dev) +{ + struct sandbox_mmc_plat *plat = dev_get_plat(dev); + struct sandbox_mmc_priv *priv = dev_get_priv(dev); + + if (plat->fname) + os_unmap(priv->buf, priv->size); + else + free(priv->buf); + + return 0; +} + static int sandbox_mmc_bind(struct udevice *dev) { struct sandbox_mmc_plat *plat = dev_get_plat(dev); @@ -196,6 +237,7 @@ U_BOOT_DRIVER(mmc_sandbox) = { .unbind = sandbox_mmc_unbind, .of_to_plat = sandbox_mmc_of_to_plat, .probe = sandbox_mmc_probe, + .remove = sandbox_mmc_remove, .priv_auto = sizeof(struct sandbox_mmc_priv), .plat_auto = sizeof(struct sandbox_mmc_plat), }; diff --git a/include/command.h b/include/command.h index 137cfbc..f8e07a5 100644 --- a/include/command.h +++ b/include/command.h @@ -45,7 +45,7 @@ struct cmd_tbl { char *const argv[]); char *usage; /* Usage message (short) */ #ifdef CONFIG_SYS_LONGHELP - char *help; /* Help message (long) */ + const char *help; /* Help message (long) */ #endif #ifdef CONFIG_AUTO_COMPLETE /* do auto completion on the arguments */ diff --git a/include/dm/device.h b/include/dm/device.h index 3028d00..daf28a0 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -763,6 +763,18 @@ int device_find_first_child_by_uclass(const struct udevice *parent, * * @parent: Parent device to search * @name: Name to look for + * @len: Length of the name + * @devp: Returns device found, if any + * @return 0 if found, else -ENODEV + */ +int device_find_child_by_namelen(const struct udevice *parent, const char *name, + int len, struct udevice **devp); + +/** + * device_find_child_by_name() - Find a child by device name + * + * @parent: Parent device to search + * @name: Name to look for * @devp: Returns device found, if any * @return 0 if found, else -ENODEV */ diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 0f680e5..6601bd8 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -590,11 +590,11 @@ int ofnode_stringlist_search(ofnode node, const char *propname, * * @node: node to check * @propname: name of the property containing the string list - * @index: index of the string to return + * @index: index of the string to return (cannot be negative) * @lenp: return location for the string length or an error code on failure * * @return: - * length of string, if found or -ve error value if not found + * 0 if found or -ve error value if not found */ int ofnode_read_string_index(ofnode node, const char *propname, int index, const char **outp); @@ -610,6 +610,26 @@ int ofnode_read_string_index(ofnode node, const char *propname, int index, int ofnode_read_string_count(ofnode node, const char *property); /** + * ofnode_read_string_list() - read a list of strings + * + * This produces a list of string pointers with each one pointing to a string + * in the string list. If the property does not exist, it returns {NULL}. + * + * The data is allocated and the caller is reponsible for freeing the return + * value (the list of string pointers). The strings themselves may not be + * changed as they point directly into the devicetree property. + * + * @node: node to check + * @listp: returns an allocated, NULL-terminated list of strings if the return + * value is > 0, else is set to NULL + * @return number of strings in list, 0 if none, -ENOMEM if out of memory, + * -EINVAL if no such property, -EENODATA if property is empty + * @return: NULL-terminated list of strings (NULL if no property or empty) + */ +int ofnode_read_string_list(ofnode node, const char *property, + const char ***listp); + +/** * ofnode_parse_phandle_with_args() - Find a node pointed by phandle in a list * * This function is useful to parse lists of phandles and their arguments. diff --git a/include/dm/read.h b/include/dm/read.h index 890bf3d..75c6ad6 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -371,6 +371,27 @@ int dev_read_string_index(const struct udevice *dev, const char *propname, * number of strings in the list, or -ve error value if not found */ int dev_read_string_count(const struct udevice *dev, const char *propname); + +/** + * dev_read_string_list() - read a list of strings + * + * This produces a list of string pointers with each one pointing to a string + * in the string list. If the property does not exist, it returns {NULL}. + * + * The data is allocated and the caller is reponsible for freeing the return + * value (the list of string pointers). The strings themselves may not be + * changed as they point directly into the devicetree property. + * + * @dev: device to examine + * @propname: name of the property containing the string list + * @listp: returns an allocated, NULL-terminated list of strings if the return + * value is > 0, else is set to NULL + * @return number of strings in list, 0 if none, -ENOMEM if out of memory, + * -ENOENT if no such property + */ +int dev_read_string_list(const struct udevice *dev, const char *propname, + const char ***listp); + /** * dev_read_phandle_with_args() - Find a node pointed by phandle in a list * @@ -906,6 +927,13 @@ static inline int dev_read_string_count(const struct udevice *dev, return ofnode_read_string_count(dev_ofnode(dev), propname); } +static inline int dev_read_string_list(const struct udevice *dev, + const char *propname, + const char ***listp) +{ + return ofnode_read_string_list(dev_ofnode(dev), propname, listp); +} + static inline int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, const char *cells_name, int cell_count, int index, struct ofnode_phandle_args *out_args) diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index 57c664c..49808c5 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -243,6 +243,17 @@ int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, */ int uclass_bind_device(struct udevice *dev); +#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) +/** + * uclass_pre_unbind_device() - Prepare to deassociate device with a uclass + * + * Call any handled needed before uclass_unbind_device() is called + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_pre_unbind_device(struct udevice *dev); + /** * uclass_unbind_device() - Deassociate device with a uclass * @@ -251,9 +262,10 @@ int uclass_bind_device(struct udevice *dev); * @dev: Pointer to the device * #return 0 on success, -ve on error */ -#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) int uclass_unbind_device(struct udevice *dev); + #else +static inline int uclass_pre_unbind_device(struct udevice *dev) { return 0; } static inline int uclass_unbind_device(struct udevice *dev) { return 0; } #endif diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 15e5f9e..f1fd2ba 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -176,6 +176,15 @@ const char *uclass_get_name(enum uclass_id id); * uclass_get_by_name() - Look up a uclass by its driver name * * @name: Name to look up + * @len: Length of name + * @returns the associated uclass ID, or UCLASS_INVALID if not found + */ +enum uclass_id uclass_get_by_name_len(const char *name, int len); + +/** + * uclass_get_by_name() - Look up a uclass by its driver name + * + * @name: Name to look up * @returns the associated uclass ID, or UCLASS_INVALID if not found */ enum uclass_id uclass_get_by_name(const char *name); @@ -417,6 +426,14 @@ int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data, int uclass_probe_all(enum uclass_id id); /** + * uclass_id_count() - Count the number of devices in a uclass + * + * @id: uclass ID to look up + * @return number of devices in that uclass (0 if none) + */ +int uclass_id_count(enum uclass_id id); + +/** * uclass_id_foreach_dev() - Helper function to iteration through devices * * This creates a for() loop which works through the available devices in diff --git a/include/os.h b/include/os.h index 770d76e..4cbcbd9 100644 --- a/include/os.h +++ b/include/os.h @@ -419,6 +419,15 @@ int os_read_file(const char *name, void **bufp, int *sizep); */ int os_map_file(const char *pathname, int os_flags, void **bufp, int *sizep); +/** + * os_unmap() - Unmap a file previously mapped + * + * @buf: Mapped address + * @size: Size in bytes + * Return: 0 if OK, -ve on error + */ +int os_unmap(void *buf, int size); + /* * os_find_text_base() - Find the text section in this running process * diff --git a/scripts/env2string.awk b/scripts/env2string.awk index 57d0fc8..1bfe9ed 100644 --- a/scripts/env2string.awk +++ b/scripts/env2string.awk @@ -21,29 +21,39 @@ BEGIN { # Skip empty lines, as these are generated by the clang preprocessor NF { + do_output = 0 + # Quote quotes gsub("\"", "\\\"") + # Avoid using the non-POSIX third parameter to match(), by splitting + # the work into several steps. + has_var = match($0, "^([^ \t=][^ =]*)=(.*)$") + # Is this the start of a new environment variable? - if (match($0, "^([^ \t=][^ =]*)=(.*)$", arr)) { + if (has_var) { if (length(env) != 0) { # Record the value of the variable now completed vars[var] = env + do_output = 1 } - var = arr[1] - env = arr[2] + + # Collect the variable name. The value follows the '=' + match($0, "^([^ \t=][^ =]*)=") + var = substr($0, 1, RLENGTH - 1) + env = substr($0, RLENGTH + 1) # Deal with += which concatenates the new string to the existing - # variable - if (length(env) != 0 && match(var, "^(.*)[+]$", var_arr)) - { + # variable. Again we are careful to use POSIX match() + if (length(env) != 0 && match(var, "^(.*)[+]$")) { + plusname = substr(var, RSTART, RLENGTH - 1) # Allow var\+=val to indicate that the variable name is # var+ and this is not actually a concatenation - if (substr(var_arr[1], length(var_arr[1])) == "\\") { + if (substr(plusname, length(plusname)) == "\\") { # Drop the backslash sub(/\\[+]$/, "+", var) } else { - var = var_arr[1] + var = plusname env = vars[var] env } } @@ -65,9 +75,10 @@ END { # empty it is not set. if (length(env) != 0) { vars[var] = env + do_output = 1 } - if (length(vars) != 0) { + if (do_output) { printf("%s", "#define CONFIG_EXTRA_ENV_TEXT \"") # Print out all the variables diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index cea0746..5e7c968 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -351,3 +351,99 @@ static int dm_test_ofnode_for_each_compatible_node(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_for_each_compatible_node, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_string(struct unit_test_state *uts) +{ + const char **val; + const char *out; + ofnode node; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + /* single string */ + ut_asserteq(1, ofnode_read_string_count(node, "str-value")); + ut_assertok(ofnode_read_string_index(node, "str-value", 0, &out)); + ut_asserteq_str("test string", out); + ut_asserteq(0, ofnode_stringlist_search(node, "str-value", + "test string")); + ut_asserteq(1, ofnode_read_string_list(node, "str-value", &val)); + ut_asserteq_str("test string", val[0]); + ut_assertnull(val[1]); + free(val); + + /* list of strings */ + ut_asserteq(5, ofnode_read_string_count(node, "mux-control-names")); + ut_assertok(ofnode_read_string_index(node, "mux-control-names", 0, + &out)); + ut_asserteq_str("mux0", out); + ut_asserteq(0, ofnode_stringlist_search(node, "mux-control-names", + "mux0")); + ut_asserteq(5, ofnode_read_string_list(node, "mux-control-names", + &val)); + ut_asserteq_str("mux0", val[0]); + ut_asserteq_str("mux1", val[1]); + ut_asserteq_str("mux2", val[2]); + ut_asserteq_str("mux3", val[3]); + ut_asserteq_str("mux4", val[4]); + ut_assertnull(val[5]); + free(val); + + ut_assertok(ofnode_read_string_index(node, "mux-control-names", 4, + &out)); + ut_asserteq_str("mux4", out); + ut_asserteq(4, ofnode_stringlist_search(node, "mux-control-names", + "mux4")); + + return 0; +} +DM_TEST(dm_test_ofnode_string, 0); + +static int dm_test_ofnode_string_err(struct unit_test_state *uts) +{ + const char **val; + const char *out; + ofnode node; + + /* + * Test error codes only on livetree, as they are different with + * flattree + */ + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + /* non-existent property */ + ut_asserteq(-EINVAL, ofnode_read_string_count(node, "missing")); + ut_asserteq(-EINVAL, ofnode_read_string_index(node, "missing", 0, + &out)); + ut_asserteq(-EINVAL, ofnode_read_string_list(node, "missing", &val)); + + /* empty property */ + ut_asserteq(-ENODATA, ofnode_read_string_count(node, "bool-value")); + ut_asserteq(-ENODATA, ofnode_read_string_index(node, "bool-value", 0, + &out)); + ut_asserteq(-ENODATA, ofnode_read_string_list(node, "bool-value", + &val)); + + /* badly formatted string list */ + ut_asserteq(-EILSEQ, ofnode_read_string_count(node, "int64-value")); + ut_asserteq(-EILSEQ, ofnode_read_string_index(node, "int64-value", 0, + &out)); + ut_asserteq(-EILSEQ, ofnode_read_string_list(node, "int64-value", + &val)); + + /* out of range / not found */ + ut_asserteq(-ENODATA, ofnode_read_string_index(node, "str-value", 1, + &out)); + ut_asserteq(-ENODATA, ofnode_stringlist_search(node, "str-value", + "other")); + + /* negative value for index is not allowed, so don't test for that */ + + ut_asserteq(-ENODATA, ofnode_read_string_index(node, + "mux-control-names", 5, + &out)); + + return 0; +} +DM_TEST(dm_test_ofnode_string_err, UT_TESTF_LIVE_TREE); diff --git a/test/py/conftest.py b/test/py/conftest.py index 11a3f30..16e445c 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -226,7 +226,7 @@ def pytest_configure(config): import u_boot_console_exec_attach console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) -re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_\1_test_(.*)\s*$') +re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_(.*)\s*$') def generate_ut_subtest(metafunc, fixture_name, sym_path): """Provide parametrization for a ut_subtest fixture. diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py index 545a774..6688207 100644 --- a/test/py/multiplexed_log.py +++ b/test/py/multiplexed_log.py @@ -109,7 +109,7 @@ class RunAndLog(object): """Clean up any resources managed by this object.""" pass - def run(self, cmd, cwd=None, ignore_errors=False): + def run(self, cmd, cwd=None, ignore_errors=False, stdin=None): """Run a command as a sub-process, and log the results. The output is available at self.output which can be useful if there is @@ -123,6 +123,7 @@ class RunAndLog(object): function will simply return if the command cannot be executed or exits with an error code, otherwise an exception will be raised if such problems occur. + stdin: Input string to pass to the command as stdin (or None) Returns: The output as a string. @@ -135,8 +136,9 @@ class RunAndLog(object): try: p = subprocess.Popen(cmd, cwd=cwd, - stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (stdout, stderr) = p.communicate() + stdin=subprocess.PIPE if stdin else None, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (stdout, stderr) = p.communicate(input=stdin) if stdout is not None: stdout = stdout.decode('utf-8') if stderr is not None: @@ -163,7 +165,7 @@ class RunAndLog(object): if output and not output.endswith('\n'): output += '\n' if exit_status and not exception and not ignore_errors: - exception = Exception('Exit code: ' + str(exit_status)) + exception = ValueError('Exit code: ' + str(exit_status)) if exception: output += str(exception) + '\n' self.logfile.write(self, output) diff --git a/test/py/u_boot_utils.py b/test/py/u_boot_utils.py index e816c7f..f44442e 100644 --- a/test/py/u_boot_utils.py +++ b/test/py/u_boot_utils.py @@ -154,7 +154,7 @@ def wait_until_file_open_fails(fn, ignore_errors): return raise Exception('File can still be opened') -def run_and_log(u_boot_console, cmd, ignore_errors=False): +def run_and_log(u_boot_console, cmd, ignore_errors=False, stdin=None): """Run a command and log its output. Args: @@ -166,6 +166,7 @@ def run_and_log(u_boot_console, cmd, ignore_errors=False): will simply return if the command cannot be executed or exits with an error code, otherwise an exception will be raised if such problems occur. + stdin: Input string to pass to the command as stdin (or None) Returns: The output as a string. @@ -173,7 +174,7 @@ def run_and_log(u_boot_console, cmd, ignore_errors=False): if isinstance(cmd, str): cmd = cmd.split() runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) - output = runner.run(cmd, ignore_errors=ignore_errors) + output = runner.run(cmd, ignore_errors=ignore_errors, stdin=stdin) runner.close() return output diff --git a/tools/binman/control.py b/tools/binman/control.py index 304fc70..7da69ba 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -355,6 +355,7 @@ def ReplaceEntries(image_fname, input_fname, indir, entry_paths, Returns: List of EntryInfo records that were written """ + image_fname = os.path.abspath(image_fname) image = Image.FromFile(image_fname) # Replace an entry from a single file, as a special case |