diff --git a/cmd/Kconfig b/cmd/Kconfig index 9e8b69258f..f962bb7141 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -332,6 +332,18 @@ config CMD_FDT help Do FDT related setup before booting into the Operating System. +config SUPPORT_EXTENSION_SCAN + bool + +config CMD_EXTENSION + bool "Extension board management command" + select CMD_FDT + depends on SUPPORT_EXTENSION_SCAN + help + Enables the "extension" command, which allows to detect + extension boards connected to the system, and apply + corresponding Device Tree overlays. + config CMD_GO bool "go" default y diff --git a/cmd/Makefile b/cmd/Makefile index 4977fa15f4..9d10e07f0e 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_CMD_DIAG) += diag.o endif obj-$(CONFIG_CMD_ADTIMG) += adtimg.o obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o +obj-$(CONFIG_CMD_EXTENSION) += extension_board.o obj-$(CONFIG_CMD_ECHO) += echo.o obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o obj-$(CONFIG_CMD_EEPROM) += eeprom.o diff --git a/cmd/extension_board.c b/cmd/extension_board.c new file mode 100644 index 0000000000..bbb4812ff8 --- /dev/null +++ b/cmd/extension_board.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021 + * Köry Maincent, Bootlin, + */ + +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(extension_list); + +static int extension_apply(struct extension *extension) +{ + char *overlay_cmd; + ulong extrasize, overlay_addr; + struct fdt_header *blob; + + if (!working_fdt) { + printf("No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr
\" command.\n"); + return CMD_RET_FAILURE; + } + + overlay_cmd = env_get("extension_overlay_cmd"); + if (!overlay_cmd) { + printf("Environment extension_overlay_cmd is missing\n"); + return CMD_RET_FAILURE; + } + + overlay_addr = env_get_hex("extension_overlay_addr", 0); + if (!overlay_addr) { + printf("Environment extension_overlay_addr is missing\n"); + return CMD_RET_FAILURE; + } + + env_set("extension_overlay_name", extension->overlay); + if (run_command(overlay_cmd, 0) != 0) + return CMD_RET_FAILURE; + + extrasize = env_get_hex("filesize", 0); + if (!extrasize) + return CMD_RET_FAILURE; + + fdt_shrink_to_minimum(working_fdt, extrasize); + + blob = map_sysmem(overlay_addr, 0); + if (!fdt_valid(&blob)) + return CMD_RET_FAILURE; + + /* apply method prints messages on error */ + if (fdt_overlay_apply_verbose(working_fdt, blob)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int do_extension_list(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int i = 0; + struct extension *extension; + + if (list_empty(&extension_list)) { + printf("No extension registered - Please run \"extension scan\"\n"); + return CMD_RET_SUCCESS; + } + + list_for_each_entry(extension, &extension_list, 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); + } + return CMD_RET_SUCCESS; +} + +static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct extension *extension, *next; + int extension_num; + + list_for_each_entry_safe(extension, next, &extension_list, list) { + list_del(&extension->list); + free(extension); + } + extension_num = extension_board_scan(&extension_list); + + if (extension_num < 0) + return CMD_RET_FAILURE; + + printf("Found %d extension board(s).\n", extension_num); + + return CMD_RET_SUCCESS; +} + +static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct extension *extension = NULL; + struct list_head *entry; + int i = 0, extension_id, ret; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "all") == 0) { + list_for_each_entry(extension, &extension_list, list) { + ret = extension_apply(extension); + if (ret != CMD_RET_SUCCESS) + break; + } + } else { + extension_id = simple_strtol(argv[1], NULL, 10); + list_for_each(entry, &extension_list) { + if (i == extension_id) { + extension = list_entry(entry, struct extension, list); + break; + } + i++; + } + + if (!extension) { + printf("Wrong extension number\n"); + return CMD_RET_FAILURE; + } + + ret = extension_apply(extension); + } + + return ret; +} + +static struct cmd_tbl cmd_extension[] = { + U_BOOT_CMD_MKENT(scan, 1, 1, do_extension_scan, "", ""), + U_BOOT_CMD_MKENT(list, 1, 0, do_extension_list, "", ""), + U_BOOT_CMD_MKENT(apply, 2, 0, do_extension_apply, "", ""), +}; + +static int do_extensionops(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + /* Drop the extension command */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_extension, ARRAY_SIZE(cmd_extension)); + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(extension, 3, 1, do_extensionops, + "Extension board management sub system", + "scan - scan plugged extension(s) board(s)\n" + "extension list - lists available extension(s) board(s)\n" + "extension apply - applies DT overlays corresponding to extension boards\n" +); diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig index e16f702f00..c6f287e9cb 100644 --- a/configs/tools-only_defconfig +++ b/configs/tools-only_defconfig @@ -8,6 +8,7 @@ CONFIG_MISC_INIT_F=y # CONFIG_CMD_BOOTD is not set # CONFIG_CMD_BOOTM is not set # CONFIG_CMD_ELF is not set +# CONFIG_CMD_EXTENSION is not set CONFIG_BOOTP_DNS2=y # CONFIG_CMD_DATE is not set CONFIG_OF_CONTROL=y diff --git a/doc/usage/extension.rst b/doc/usage/extension.rst new file mode 100644 index 0000000000..2b88398b18 --- /dev/null +++ b/doc/usage/extension.rst @@ -0,0 +1,111 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright 2021, Kory Maincent + +U-Boot extension board usage (CONFIG_EXTENSION) +=============================================== + +Synopsis +-------- + +:: + + extension scan + extension list + extension apply + +Description +----------- + +The "extension" command proposes a generic U-Boot mechanism to detect +extension boards connected to the HW platform, and apply the appropriate +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 list" allows to list the detected extension boards. + + - "extension apply |all" allows to apply the Device Tree + overlay(s) corresponding to one, or all, extension boards + +The latter requires two environment variables to exist: + + - extension_overlay_addr: the RAM address where to load the Device + Tree overlays + + - extension_overlay_cmd: the U-Boot command to load one overlay. + Indeed, the location and mechanism to load DT overlays is very setup + specific. + +In order to enable this mechanism, board-specific code must implement +the extension_board_scan() function that fills in a linked list of +"struct extension", each describing one extension board. In addition, +the board-specific code must select the SUPPORT_EXTENSION_SCAN Kconfig +boolean. + +Usage example +------------- + +1. Make sure your devicetree is loaded and set as the working fdt tree. + +:: + + => run loadfdt + => fdt addr $fdtaddr + +2. Prepare the environment variables + +:: + + => setenv extension_overlay_addr 0x88080000 + => setenv extension_overlay_cmd 'load mmc 0:1 ${extension_overlay_addr} /boot/${extension_overlay_name}' + +3. Detect the plugged extension board + +:: + + => extension scan + +4. List the plugged extension board information and the devicetree + overlay name + +:: + + => extension list + +5. Apply the appropriate devicetree overlay + +For apply the selected overlay: + +:: + + => extension apply 0 + +For apply all the overlays: + +:: + + => extension apply all + +Simple extension_board_scan function example +-------------------------------------------- + +.. code-block:: c + + int extension_board_scan(struct list_head *extension_list) + { + struct extension *extension; + + 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); + + return 1; + } diff --git a/include/extension_board.h b/include/extension_board.h new file mode 100644 index 0000000000..c530a0ad8a --- /dev/null +++ b/include/extension_board.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2021 + * Köry Maincent, Bootlin, + */ + +#ifndef __EXTENSION_SUPPORT_H +#define __EXTENSION_SUPPORT_H + +struct extension { + struct list_head list; + char name[32]; + char owner[32]; + char version[32]; + char overlay[32]; + char other[32]; +}; + +/** + * extension_board_scan - Add system-specific function to scan extension board. + * @param extension_list List of extension board information to update. + * @return the number of extension. + * + * This function is called if CONFIG_CMD_EXTENSION is defined. + * Needs to fill the list extension_list with elements. + * Each element need to be allocated to an extension structure. + * + */ +int extension_board_scan(struct list_head *extension_list); + +#endif /* __EXTENSION_SUPPORT_H */