From 78a06090f4860d52dfd1577619f53a7ec75122c9 Mon Sep 17 00:00:00 2001 From: "Kory Maincent (TI.com)" Date: Thu, 30 Oct 2025 17:45:01 +0100 Subject: [PATCH] boot: Add UCLASS support for extension boards Introduce UCLASS-based extension board support to enable more standardized and automatic loading of extension board device tree overlays in preparation for integration with bootstd and pxe_utils. Several #if CONFIG_IS_ENABLED are used in cmd/extension_board.c to ease the development but don't worry they are removed later in the series. Signed-off-by: Kory Maincent (TI.com) Reviewed-by: Simon Glass --- MAINTAINERS | 1 + boot/Kconfig | 4 + boot/Makefile | 1 + boot/extension-uclass.c | 151 ++++++++++++++++++++++++++++++++++++ cmd/Kconfig | 2 +- cmd/extension_board.c | 50 +++++++++++- doc/usage/cmd/extension.rst | 29 ++++--- include/dm/uclass-id.h | 1 + include/extension_board.h | 71 +++++++++++++++++ 9 files changed, 294 insertions(+), 16 deletions(-) create mode 100644 boot/extension-uclass.c diff --git a/MAINTAINERS b/MAINTAINERS index c67d5ae9d6b..a63ffa14ef5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1187,6 +1187,7 @@ M: Kory Maincent S: Maintained F: board/sunxi/chip.c F: board/ti/common/cape_detect.c +F: boot/extension-uclass.c F: boot/extension.c F: cmd/extension_board.c F: include/extension_board.h diff --git a/boot/Kconfig b/boot/Kconfig index a75f6b641ed..08d666d4b93 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1910,6 +1910,10 @@ config SUPPORT_EXTENSION_SCAN select OF_LIBFDT_OVERLAY bool +config SUPPORT_DM_EXTENSION_SCAN + select OF_LIBFDT_OVERLAY + bool + config USE_BOOTARGS bool "Enable boot arguments" help diff --git a/boot/Makefile b/boot/Makefile index f60d13130b1..aa26070fbb8 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_CMD_BOOTM) += bootm.o bootm_os.o obj-$(CONFIG_CMD_BOOTZ) += bootm.o bootm_os.o obj-$(CONFIG_CMD_BOOTI) += bootm.o bootm_os.o obj-$(CONFIG_SUPPORT_EXTENSION_SCAN) += extension.o +obj-$(CONFIG_SUPPORT_DM_EXTENSION_SCAN) += extension-uclass.o obj-$(CONFIG_PXE_UTILS) += pxe_utils.o diff --git a/boot/extension-uclass.c b/boot/extension-uclass.c new file mode 100644 index 00000000000..914971215af --- /dev/null +++ b/boot/extension-uclass.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2025 Köry Maincent + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct alist *dm_extension_get_list(void) +{ + struct udevice *dev; + + if (uclass_first_device_err(UCLASS_EXTENSION, &dev)) + return NULL; + + return dev_get_priv(dev); +} + +int dm_extension_probe(struct udevice *dev) +{ + struct alist *extension_list = dev_get_priv(dev); + + alist_init_struct(extension_list, struct extension); + return 0; +} + +int dm_extension_remove(struct udevice *dev) +{ + struct alist *extension_list = dev_get_priv(dev); + + alist_uninit(extension_list); + return 0; +} + +int dm_extension_scan(void) +{ + struct alist *extension_list = dm_extension_get_list(); + const struct extension_ops *ops; + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_EXTENSION, &dev); + if (ret) + return ret; + + if (!extension_list) + return -ENODEV; + + ops = extension_get_ops(dev); + alist_empty(extension_list); + return ops->scan(dev, extension_list); +} + +static int _extension_apply(const struct extension *extension) +{ + ulong extrasize, overlay_addr; + struct fdt_header *blob; + char *overlay_cmd; + int ret; + + if (!working_fdt) { + printf("No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr
\" command.\n"); + return -EINVAL; + } + + overlay_cmd = env_get("extension_overlay_cmd"); + if (!overlay_cmd) { + printf("Environment extension_overlay_cmd is missing\n"); + return -EINVAL; + } + + overlay_addr = env_get_hex("extension_overlay_addr", 0); + if (!overlay_addr) { + printf("Environment extension_overlay_addr is missing\n"); + return -EINVAL; + } + + env_set("extension_overlay_name", extension->overlay); + ret = run_command(overlay_cmd, 0); + if (ret) + return ret; + + extrasize = env_get_hex("filesize", 0); + if (!extrasize) + return -EINVAL; + + fdt_shrink_to_minimum(working_fdt, extrasize); + + blob = map_sysmem(overlay_addr, 0); + if (!fdt_valid(&blob)) { + printf("Invalid overlay devicetree %s\n", extension->overlay); + return -EINVAL; + } + + /* Apply method prints messages on error */ + ret = fdt_overlay_apply_verbose(working_fdt, blob); + if (ret) + printf("Failed to apply overlay %s\n", extension->overlay); + + return ret; +} + +int dm_extension_apply(int extension_num) +{ + struct alist *extension_list = dm_extension_get_list(); + const struct extension *extension; + + if (!extension_list) + return -ENOENT; + + extension = alist_get(extension_list, extension_num, + struct extension); + if (!extension) { + printf("Wrong extension number\n"); + return -EINVAL; + } + + return _extension_apply(extension); +} + +int dm_extension_apply_all(void) +{ + struct alist *extension_list = dm_extension_get_list(); + const struct extension *extension; + int ret; + + if (!extension_list) + return -ENOENT; + + alist_for_each(extension, extension_list) { + ret = _extension_apply(extension); + if (ret) + return ret; + } + + return 0; +} + +UCLASS_DRIVER(extension) = { + .name = "extension", + .id = UCLASS_EXTENSION, +}; diff --git a/cmd/Kconfig b/cmd/Kconfig index 986eeeba807..721bdb87c8a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -548,7 +548,7 @@ config CMD_FDT config CMD_EXTENSION bool "Extension board management command" select CMD_FDT - depends on SUPPORT_EXTENSION_SCAN + depends on SUPPORT_EXTENSION_SCAN || SUPPORT_DM_EXTENSION_SCAN help Enables the "extension" command, which allows to detect extension boards connected to the system, and apply diff --git a/cmd/extension_board.c b/cmd/extension_board.c index 129f3db9e4c..1f1eddbe976 100644 --- a/cmd/extension_board.c +++ b/cmd/extension_board.c @@ -4,6 +4,7 @@ * Köry Maincent, Bootlin, */ +#include #include #include #include @@ -11,9 +12,28 @@ static int do_extension_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { +#if CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) + struct alist *dm_extension_list; +#endif struct extension *extension; int i = 0; +#if CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) + dm_extension_list = dm_extension_get_list(); + + if (!alist_get_ptr(dm_extension_list, 0)) { + printf("No extension registered - Please run \"extension scan\"\n"); + return CMD_RET_SUCCESS; + } + + alist_for_each(extension, dm_extension_list) { + printf("Extension %d: %s\n", i++, extension->name); + printf("\tManufacturer: \t\t%s\n", extension->owner); + printf("\tVersion: \t\t%s\n", extension->version); + printf("\tDevicetree overlay: \t%s\n", extension->overlay); + printf("\tOther information: \t%s\n", extension->other); + } +#else if (list_empty(&extension_list)) { printf("No extension registered - Please run \"extension scan\"\n"); return CMD_RET_SUCCESS; @@ -26,6 +46,7 @@ static int do_extension_list(struct cmd_tbl *cmdtp, int flag, printf("\tDevicetree overlay: \t%s\n", extension->overlay); printf("\tOther information: \t%s\n", extension->other); } +#endif return CMD_RET_SUCCESS; } @@ -34,9 +55,19 @@ static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, { int extension_num; +#if CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) + extension_num = dm_extension_scan(); + if (extension_num == -ENODEV) + extension_num = 0; + else if (extension_num < 0) + return CMD_RET_FAILURE; + + printf("Found %d extension board(s).\n", extension_num); +#else extension_num = extension_scan(true); - if (extension_num < 0) + if (extension_num < 0 && extension_num != -ENODEV) return CMD_RET_FAILURE; +#endif return CMD_RET_SUCCESS; } @@ -44,22 +75,34 @@ static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { +#if !CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) struct extension *extension = NULL; - int i = 0, extension_id, ret; struct list_head *entry; + int i = 0; +#endif + int extension_id, ret; if (argc < 2) return CMD_RET_USAGE; if (strcmp(argv[1], "all") == 0) { ret = CMD_RET_FAILURE; +#if CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) + if (dm_extension_apply_all()) + return CMD_RET_FAILURE; +#else list_for_each_entry(extension, &extension_list, list) { ret = extension_apply(extension); if (ret != CMD_RET_SUCCESS) break; } +#endif } else { extension_id = simple_strtol(argv[1], NULL, 10); +#if CONFIG_IS_ENABLED(SUPPORT_DM_EXTENSION_SCAN) + if (dm_extension_apply(extension_id)) + return CMD_RET_FAILURE; +#else list_for_each(entry, &extension_list) { if (i == extension_id) { extension = list_entry(entry, struct extension, list); @@ -74,9 +117,10 @@ static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, } ret = extension_apply(extension); +#endif } - return ret; + return CMD_RET_SUCCESS; } static struct cmd_tbl cmd_extension[] = { diff --git a/doc/usage/cmd/extension.rst b/doc/usage/cmd/extension.rst index 4c261e74951..fbe95aace79 100644 --- a/doc/usage/cmd/extension.rst +++ b/doc/usage/cmd/extension.rst @@ -25,9 +25,8 @@ Device Tree overlays depending on the detected extension boards. The "extension" command comes with three sub-commands: - - "extension scan" makes the generic code call the board-specific - extension_board_scan() function to retrieve the list of detected - extension boards. + - "extension scan" makes the generic code call a board-specific extension + function to retrieve the list of detected extension boards. - "extension list" allows to list the detected extension boards. @@ -98,17 +97,23 @@ Simple extension_board_scan function example .. code-block:: c - int extension_board_scan(struct list_head *extension_list) + static int foo_extension_board_scan(struct alist *extension_list) { - struct extension *extension; + struct extension extension = {0}; - extension = calloc(1, sizeof(struct extension)); - snprintf(extension->overlay, sizeof(extension->overlay), "overlay.dtbo"); - snprintf(extension->name, sizeof(extension->name), "extension board"); - snprintf(extension->owner, sizeof(extension->owner), "sandbox"); - snprintf(extension->version, sizeof(extension->version), "1.1"); - snprintf(extension->other, sizeof(extension->other), "Extension board information"); - list_add_tail(&extension->list, extension_list); + snprintf(extension.overlay, sizeof(extension.overlay), "overlay.dtbo"); + snprintf(extension.name, sizeof(extension.name), "extension board"); + snprintf(extension.owner, sizeof(extension.owner), "sandbox"); + snprintf(extension.version, sizeof(extension.version), "1.1"); + snprintf(extension.other, sizeof(extension.other), "Extension board information"); + if (!alist_add(extension_list, extension)) + return -ENOMEM; return 1; } + + U_BOOT_EXTENSION(foo_extension_name, foo_extension_board_scan); + + U_BOOT_DRVINFO(foo_extension_name) = { + .name = "foo_extension_name", + }; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 6be59093160..eb6416b5917 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -63,6 +63,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_EXTCON, /* External Connector Class */ + UCLASS_EXTENSION, /* Extension board */ UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FFA_EMUL, /* sandbox FF-A device emulator */ UCLASS_FIRMWARE, /* Firmware */ diff --git a/include/extension_board.h b/include/extension_board.h index 0821dddd8ff..a74a9c67c01 100644 --- a/include/extension_board.h +++ b/include/extension_board.h @@ -7,10 +7,55 @@ #ifndef __EXTENSION_SUPPORT_H #define __EXTENSION_SUPPORT_H +#include +#include #include +#include extern struct list_head extension_list; +/** + * dm_extension_get_list - Get the extension list + * Return: The extension alist pointer, or NULL if no such list exists. + * + * The caller must not free the list. + */ +struct alist *dm_extension_get_list(void); + +/** + * dm_extension_probe - Probe extension device + * @dev: Extension device that needs to be probed + * Return: Zero on success, negative on failure. + */ +int dm_extension_probe(struct udevice *dev); + +/** + * dm_extension_remove - Remove extension device + * @dev: Extension device that needs to be removed + * Return: Zero on success, negative on failure. + */ +int dm_extension_remove(struct udevice *dev); + +/** + * dm_extension_scan - Scan extension boards available. + * Return: Zero on success, negative on failure. + */ +int dm_extension_scan(void); + +/** + * dm_extension_apply - Apply extension board overlay to the devicetree + * @extension_num: Extension number to be applied + * Return: Zero on success, negative on failure. + */ +int dm_extension_apply(int extension_num); + +/** + * dm_extension_apply_all - Apply all extension board overlays to the + * devicetree + * Return: Zero on success, negative on failure. + */ +int dm_extension_apply_all(void); + /** * extension - Description fields of an extension board * @list: List head @@ -29,6 +74,32 @@ struct extension { char other[32]; }; +struct extension_ops { + /** + * scan - Add system-specific function to scan extension boards. + * @dev: extension device + * @extension_list: alist of extension to expand + * Return: The number of extension or a negative value in case of + * error. + */ + int (*scan)(struct udevice *dev, struct alist *extension_list); +}; + +#define extension_get_ops(dev) ((struct extension_ops *)(dev)->driver->ops) + +/* Currently, only one extension driver enabled at a time is supported */ +#define U_BOOT_EXTENSION(_name, _scan_func) \ + U_BOOT_DRIVER(_name) = { \ + .name = #_name, \ + .id = UCLASS_EXTENSION, \ + .probe = dm_extension_probe, \ + .remove = dm_extension_remove, \ + .ops = &(struct extension_ops) { \ + .scan = _scan_func, \ + }, \ + .priv_auto = sizeof(struct alist), \ + } + /** * extension_board_scan - Add system-specific function to scan extension board. * @param extension_list List of extension board information to update. -- 2.47.3