]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
cmd: add support for a new "extension" command
authorKory Maincent <kory.maincent@bootlin.com>
Tue, 4 May 2021 17:31:22 +0000 (19:31 +0200)
committerTom Rini <trini@konsulko.com>
Thu, 13 May 2021 17:09:05 +0000 (13:09 -0400)
This patch adds a new "extension" command, which aims at detecting
extension boards connected to the hardware platform, and apply the
Device Tree overlays that describe the hardware present on those
extension boards.

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.

Based on this:

 - "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 <number>|all" allows to apply the Device Tree
   overlay(s) corresponding to one, or all, extension boards

The latter requires two environment variables to exist and set one variable
to run:

 - 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.

 - extension_overlay_name: set by the command: the name of the DT which
   will be load during the execution.

When calling the command described in the extension_overlay_cmd
variable, the variable extension_overlay_name will be defined. So a
typical extension_overlay_cmd will look like this:

  extension_overlay_cmd=load mmc 0:1 $extension_overlay_addr /boot/$extension_overlay_name

Here is an example on how to use it:
=> run loadfdt
=> fdt addr $fdtaddr
=> setenv extension_overlay_addr 0x1000
=> setenv extension_overlay_cmd 'load mmc 0:1 ${extension_overlay_addr} /boot/${extension_overlay_name}'
=> extension scan
Found 1 extension board(s).
=> extension apply 0
519 bytes read in 3 ms (168.9 KiB/s)

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Reviewed-by: Maxime Ripard <maxime@cerno.tech>
cmd/Kconfig
cmd/Makefile
cmd/extension_board.c [new file with mode: 0644]
configs/tools-only_defconfig
doc/usage/extension.rst [new file with mode: 0644]
include/extension_board.h [new file with mode: 0644]

index 9e8b69258ff7210872f5409e471a7608c0b1ea4e..f962bb71411789a5d8a203cdbcded9c5321a367f 100644 (file)
@@ -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
index 4977fa15f43a592fe6f06519bc0e89e65c5c7e37..9d10e07f0ef070ef2cba5d5df19719f89ac28e10 100644 (file)
@@ -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 (file)
index 0000000..bbb4812
--- /dev/null
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2021
+ * Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <extension_board.h>
+#include <mapmem.h>
+#include <linux/libfdt.h>
+#include <fdt_support.h>
+
+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 <address>\" 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 <extension number|all> - applies DT overlays corresponding to extension boards\n"
+);
index e16f702f00ae4f214d4b72cd237f925c22f5f23d..c6f287e9cbfc3c14537d63dd12b35fc393ff4408 100644 (file)
@@ -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 (file)
index 0000000..2b88398
--- /dev/null
@@ -0,0 +1,111 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2021, Kory Maincent <kory.maincent@bootlin.com>
+
+U-Boot extension board usage (CONFIG_EXTENSION)
+===============================================
+
+Synopsis
+--------
+
+::
+
+    extension scan
+    extension list
+    extension apply <extension number|all>
+
+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 <number>|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 (file)
index 0000000..c530a0a
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2021
+ * Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
+ */
+
+#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 */