forked from OERV-BSP/u-boot
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) <kory.maincent@bootlin.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
committed by
Tom Rini
parent
b7edeac950
commit
78a06090f4
@@ -1187,6 +1187,7 @@ M: Kory Maincent <kory.maincent@bootlin.com>
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2025 Köry Maincent <kory.maincent@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <alist.h>
|
||||
#include <command.h>
|
||||
#include <env.h>
|
||||
#include <extension_board.h>
|
||||
#include <fdt_support.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/uclass.h>
|
||||
|
||||
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 <address>\" 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,
|
||||
};
|
||||
+1
-1
@@ -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
|
||||
|
||||
+48
-4
@@ -4,6 +4,7 @@
|
||||
* Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <alist.h>
|
||||
#include <exports.h>
|
||||
#include <command.h>
|
||||
#include <extension_board.h>
|
||||
@@ -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,32 +55,54 @@ static int do_extension_scan(struct cmd_tbl *cmdtp, int flag,
|
||||
{
|
||||
int extension_num;
|
||||
|
||||
extension_num = extension_scan(true);
|
||||
if (extension_num < 0)
|
||||
#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 && extension_num != -ENODEV)
|
||||
return CMD_RET_FAILURE;
|
||||
#endif
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
|
||||
+17
-12
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -7,10 +7,55 @@
|
||||
#ifndef __EXTENSION_SUPPORT_H
|
||||
#define __EXTENSION_SUPPORT_H
|
||||
|
||||
#include <alist.h>
|
||||
#include <dm/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <dm/platdata.h>
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user