Merge branch '2021-05-13-extension-board-detection-and-DT-overlay-application'

- Improve support for various forms of extension boards and add DT
  overlay application support.
This commit is contained in:
Tom Rini 2021-05-13 13:09:14 -04:00
commit 530c8d4af2
36 changed files with 837 additions and 84 deletions

View File

@ -121,6 +121,7 @@ config SANDBOX
select SUPPORT_OF_CONTROL
select SYSRESET_CMD_POWEROFF
select IRQ
select SUPPORT_EXTENSION_SCAN
imply BITREVERSE
select BLOBLIST
imply CMD_DM
@ -165,6 +166,7 @@ config SANDBOX
imply BOOTARGS_SUBST
imply PHY_FIXED
imply DM_DSA
imply CMD_EXTENSION
config SH
bool "SuperH architecture"

View File

@ -34,6 +34,7 @@ config TARGET_AM335X_EVM
select DM_GPIO
select DM_SERIAL
select TI_I2C_BOARD_DETECT
select SUPPORT_EXTENSION_SCAN
imply CMD_DM
imply SPL_DM
imply SPL_DM_SEQ_ALIAS

View File

@ -220,6 +220,7 @@ void enable_basic_clocks(void)
&cmper->gpio2clkctrl,
&cmper->gpio3clkctrl,
&cmper->i2c1clkctrl,
&cmper->i2c2clkctrl,
&cmper->cpgmac0clkctrl,
&cmper->spi0clkctrl,
&cmrtc->rtcclkctrl,

View File

@ -36,6 +36,7 @@ config TARGET_AM57XX_EVM
select CMD_DDR3
select DRA7XX
select TI_I2C_BOARD_DETECT
select SUPPORT_EXTENSION_SCAN
imply DM_THERMAL
imply SCSI
imply SPL_THERMAL

View File

@ -1089,3 +1089,12 @@ config BLUETOOTH_DT_DEVICE_FIXUP
flipped elsewise.
endif
config CHIP_DIP_SCAN
bool "Enable DIPs detection for CHIP board"
select SUPPORT_EXTENSION_SCAN
select W1
select W1_GPIO
select W1_EEPROM
select W1_EEPROM_DS24XXX
select CMD_EXTENSION

View File

@ -6,6 +6,7 @@ else
dtb-$(CONFIG_SANDBOX) += sandbox.dtb
endif
dtb-$(CONFIG_UT_DM) += test.dtb
dtb-$(CONFIG_CMD_EXTENSION) += overlay0.dtbo overlay1.dtbo
targets += $(dtb-y)

View File

@ -0,0 +1,9 @@
/dts-v1/;
/plugin/;
&{/buttons} {
btn3 {
gpios = <&gpio_a 5 0>;
label = "button3";
};
};

View File

@ -0,0 +1,9 @@
/dts-v1/;
/plugin/;
&{/buttons} {
btn4 {
gpios = <&gpio_a 5 0>;
label = "button4";
};
};

View File

@ -14,6 +14,9 @@
#include <asm/global_data.h>
#include <asm/test.h>
#include <asm/u-boot-sandbox.h>
#include <malloc.h>
#include <extension_board.h>
/*
* Pointer to initial global data area
@ -79,6 +82,26 @@ int ft_board_setup(void *fdt, struct bd_info *bd)
return fdt_add_mem_rsv(fdt, 0x00d02000, 0x4000);
}
#ifdef CONFIG_CMD_EXTENSION
int extension_board_scan(struct list_head *extension_list)
{
struct extension *extension;
int i;
for (i = 0; i < 2; i++) {
extension = calloc(1, sizeof(struct extension));
snprintf(extension->overlay, sizeof(extension->overlay), "overlay%d.dtbo", i);
snprintf(extension->name, sizeof(extension->name), "extension board %d", i);
snprintf(extension->owner, sizeof(extension->owner), "sandbox");
snprintf(extension->version, sizeof(extension->version), "1.1");
snprintf(extension->other, sizeof(extension->other), "Fictionnal extension board");
list_add_tail(&extension->list, extension_list);
}
return i;
}
#endif
#ifdef CONFIG_BOARD_LATE_INIT
int board_late_init(void)
{

View File

@ -11,3 +11,4 @@ obj-$(CONFIG_SUN7I_GMAC) += gmac.o
obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o
obj-$(CONFIG_MACH_SUN7I) += dram_sun5i_auto.o
obj-$(CONFIG_CHIP_DIP_SCAN) += chip.o

100
board/sunxi/chip.c Normal file
View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2021
* Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
* Based on initial code from Maxime Ripard
*/
#include <common.h>
#include <malloc.h>
#include <dm.h>
#include <w1.h>
#include <w1-eeprom.h>
#include <dm/device-internal.h>
#include <asm/arch/gpio.h>
#include <extension_board.h>
#define for_each_w1_device(b, d) \
for (device_find_first_child(b, d); *d; device_find_next_child(d))
#define dip_convert(field) \
( \
(sizeof(field) == 1) ? field : \
(sizeof(field) == 2) ? be16_to_cpu(field) : \
(sizeof(field) == 4) ? be32_to_cpu(field) : \
-1 \
)
#define DIP_MAGIC 0x50494843 /* CHIP */
struct dip_w1_header {
u32 magic; /* CHIP */
u8 version; /* spec version */
u32 vendor_id;
u16 product_id;
u8 product_version;
char vendor_name[32];
char product_name[32];
u8 rsvd[36]; /* rsvd for future spec versions */
u8 data[16]; /* user data, per-dip specific */
} __packed;
int extension_board_scan(struct list_head *extension_list)
{
struct extension *dip;
struct dip_w1_header w1_header;
struct udevice *bus, *dev;
u32 vid;
u16 pid;
int ret;
int num_dip = 0;
sunxi_gpio_set_pull(SUNXI_GPD(2), SUNXI_GPIO_PULL_UP);
ret = w1_get_bus(0, &bus);
if (ret) {
printf("one wire interface not found\n");
return 0;
}
for_each_w1_device(bus, &dev) {
if (w1_get_device_family(dev) != W1_FAMILY_DS2431)
continue;
ret = device_probe(dev);
if (ret) {
printf("Couldn't probe device %s: error %d",
dev->name, ret);
continue;
}
w1_eeprom_read_buf(dev, 0, (u8 *)&w1_header, sizeof(w1_header));
if (w1_header.magic != DIP_MAGIC)
continue;
vid = dip_convert(w1_header.vendor_id);
pid = dip_convert(w1_header.product_id);
printf("DIP: %s (0x%x) from %s (0x%x)\n",
w1_header.product_name, pid,
w1_header.vendor_name, vid);
dip = calloc(1, sizeof(struct extension));
if (!dip) {
printf("Error in memory allocation\n");
return num_dip;
}
snprintf(dip->overlay, sizeof(dip->overlay), "dip-%x-%x.dtbo",
vid, pid);
strncpy(dip->name, w1_header.product_name, 32);
strncpy(dip->owner, w1_header.vendor_name, 32);
list_add_tail(&dip->list, extension_list);
num_dip++;
}
return num_dip;
}

View File

@ -44,6 +44,7 @@
#include <env_internal.h>
#include <watchdog.h>
#include "../common/board_detect.h"
#include "../common/cape_detect.h"
#include "board.h"
DECLARE_GLOBAL_DATA_PTR;
@ -77,8 +78,10 @@ static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE;
void do_board_detect(void)
{
enable_i2c0_pin_mux();
enable_i2c2_pin_mux();
#if !CONFIG_IS_ENABLED(DM_I2C)
i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);
i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED2, CONFIG_SYS_OMAP24_I2C_SLAVE2);
#endif
if (ti_i2c_eeprom_am_get(CONFIG_EEPROM_BUS_ADDRESS,
CONFIG_EEPROM_CHIP_ADDRESS))

View File

@ -93,5 +93,6 @@ void enable_uart3_pin_mux(void);
void enable_uart4_pin_mux(void);
void enable_uart5_pin_mux(void);
void enable_i2c0_pin_mux(void);
void enable_i2c2_pin_mux(void);
void enable_board_pin_mux(void);
#endif

View File

@ -124,6 +124,14 @@ static struct module_pin_mux i2c1_pin_mux[] = {
{-1},
};
static struct module_pin_mux i2c2_pin_mux[] = {
{OFFSET(uart1_ctsn), (MODE(3) | RXACTIVE |
PULLUDEN | PULLUP_EN | SLEWCTRL)}, /* I2C_DATA */
{OFFSET(uart1_rtsn), (MODE(3) | RXACTIVE |
PULLUDEN | PULLUP_EN | SLEWCTRL)}, /* I2C_SCLK */
{-1},
};
static struct module_pin_mux spi0_pin_mux[] = {
{OFFSET(spi0_sclk), (MODE(0) | RXACTIVE | PULLUDEN)}, /* SPI0_SCLK */
{OFFSET(spi0_d0), (MODE(0) | RXACTIVE |
@ -308,6 +316,11 @@ void enable_i2c0_pin_mux(void)
configure_module_pin_mux(i2c0_pin_mux);
}
void enable_i2c2_pin_mux(void)
{
configure_module_pin_mux(i2c2_pin_mux);
}
/*
* The AM335x GP EVM, if daughter card(s) are connected, can have 8
* different profiles. These profiles determine what peripherals are
@ -367,6 +380,7 @@ void enable_board_pin_mux(void)
#else
configure_module_pin_mux(mmc1_pin_mux);
#endif
configure_module_pin_mux(i2c2_pin_mux);
} else if (board_is_gp_evm()) {
/* General Purpose EVM */
unsigned short profile = detect_daughter_board_profile();
@ -411,6 +425,7 @@ void enable_board_pin_mux(void)
#else
configure_module_pin_mux(mmc1_pin_mux);
#endif
configure_module_pin_mux(i2c2_pin_mux);
} else if (board_is_pb()) {
configure_module_pin_mux(mii1_pin_mux);
configure_module_pin_mux(mmc0_pin_mux);

View File

@ -43,6 +43,7 @@
#include <hang.h>
#include "../common/board_detect.h"
#include "../common/cape_detect.h"
#include "mux_data.h"
#ifdef CONFIG_SUPPORT_EMMC_BOOT

View File

@ -16,6 +16,12 @@ config EEPROM_CHIP_ADDRESS
default 0x50
depends on TI_I2C_BOARD_DETECT
config CAPE_EEPROM_BUS_NUM
int "Cape EEPROM's I2C bus address"
range 0 8
default 2
depends on CMD_EXTENSION
config TI_COMMON_CMD_OPTIONS
bool "Enable cmd options on TI platforms"
imply CMD_ASKENV

View File

@ -2,3 +2,4 @@
# Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
obj-${CONFIG_TI_I2C_BOARD_DETECT} += board_detect.o
obj-${CONFIG_CMD_EXTENSION} += cape_detect.o

View File

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2021
* Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
*/
#include <common.h>
#include <malloc.h>
#include <i2c.h>
#include <extension_board.h>
#include "cape_detect.h"
static void sanitize_field(char *text, size_t size)
{
char *c = NULL;
for (c = text; c < text + (int)size; c++) {
if (*c == 0xFF)
*c = 0;
}
}
int extension_board_scan(struct list_head *extension_list)
{
struct extension *cape;
struct am335x_cape_eeprom_id eeprom_header;
int num_capes = 0;
int ret, i;
struct udevice *dev;
unsigned char addr;
char process_cape_part_number[17] = {'0'};
char process_cape_version[5] = {'0'};
uint8_t cursor = 0;
for (addr = CAPE_EEPROM_FIRST_ADDR; addr <= CAPE_EEPROM_LAST_ADDR; addr++) {
ret = i2c_get_chip_for_busnum(CONFIG_CAPE_EEPROM_BUS_NUM, addr, 1, &dev);
if (ret)
continue;
/* Move the read cursor to the beginning of the EEPROM */
dm_i2c_write(dev, 0, &cursor, 1);
ret = dm_i2c_read(dev, 0, (uint8_t *)&eeprom_header,
sizeof(struct am335x_cape_eeprom_id));
if (ret) {
printf("Cannot read i2c EEPROM\n");
continue;
}
if (eeprom_header.header != CAPE_MAGIC)
continue;
sanitize_field(eeprom_header.board_name, sizeof(eeprom_header.board_name));
sanitize_field(eeprom_header.version, sizeof(eeprom_header.version));
sanitize_field(eeprom_header.manufacturer, sizeof(eeprom_header.manufacturer));
sanitize_field(eeprom_header.part_number, sizeof(eeprom_header.part_number));
/* Process cape part_number */
memset(process_cape_part_number, 0, sizeof(process_cape_part_number));
strncpy(process_cape_part_number, eeprom_header.part_number, 16);
/* Some capes end with '.' */
for (i = 15; i >= 0; i--) {
if (process_cape_part_number[i] == '.')
process_cape_part_number[i] = '\0';
else
break;
}
/* Process cape version */
memset(process_cape_version, 0, sizeof(process_cape_version));
strncpy(process_cape_version, eeprom_header.version, 4);
for (i = 0; i < 4; i++) {
if (process_cape_version[i] == 0)
process_cape_version[i] = '0';
}
printf("BeagleBone Cape: %s (0x%x)\n", eeprom_header.board_name, addr);
cape = calloc(1, sizeof(struct extension));
if (!cape) {
printf("Error in memory allocation\n");
return num_capes;
}
snprintf(cape->overlay, sizeof(cape->overlay), "%s-%s.dtbo",
process_cape_part_number, process_cape_version);
strncpy(cape->name, eeprom_header.board_name, 32);
strncpy(cape->version, process_cape_version, 4);
strncpy(cape->owner, eeprom_header.manufacturer, 16);
list_add_tail(&cape->list, extension_list);
num_capes++;
}
return num_capes;
}

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2021
* Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
*/
#ifndef __CAPE_DETECT_H
#define __CAPE_DETECT_H
struct am335x_cape_eeprom_id {
unsigned int header;
char eeprom_rev[2];
char board_name[32];
char version[4];
char manufacturer[16];
char part_number[16];
};
#define CAPE_EEPROM_FIRST_ADDR 0x54
#define CAPE_EEPROM_LAST_ADDR 0x57
#define CAPE_EEPROM_ADDR_LEN 0x10
#define CAPE_MAGIC 0xEE3355AA
int extension_board_scan(struct list_head *extension_list);
#endif /* __CAPE_DETECT_H */

View 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

View 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

167
cmd/extension_board.c Normal file
View File

@ -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"
);

View File

@ -27,7 +27,6 @@
*/
DECLARE_GLOBAL_DATA_PTR;
static int fdt_valid(struct fdt_header **blobp);
static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
static int fdt_print(const char *pathp, char *prop, int depth);
static int is_printable_string(const void *data, int len);
@ -732,54 +731,6 @@ static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
/****************************************************************************/
/**
* fdt_valid() - Check if an FDT is valid. If not, change it to NULL
*
* @blobp: Pointer to FDT pointer
* @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
*/
static int fdt_valid(struct fdt_header **blobp)
{
const void *blob = *blobp;
int err;
if (blob == NULL) {
printf ("The address of the fdt is invalid (NULL).\n");
return 0;
}
err = fdt_check_header(blob);
if (err == 0)
return 1; /* valid */
if (err < 0) {
printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
/*
* Be more informative on bad version.
*/
if (err == -FDT_ERR_BADVERSION) {
if (fdt_version(blob) <
FDT_FIRST_SUPPORTED_VERSION) {
printf (" - too old, fdt %d < %d",
fdt_version(blob),
FDT_FIRST_SUPPORTED_VERSION);
}
if (fdt_last_comp_version(blob) >
FDT_LAST_SUPPORTED_VERSION) {
printf (" - too new, fdt %d > %d",
fdt_version(blob),
FDT_LAST_SUPPORTED_VERSION);
}
}
printf("\n");
*blobp = NULL;
return 0;
}
return 1;
}
/****************************************************************************/
/*
* Parse the user's input, partially heuristic. Valid formats:
* <0x00112233 4 05> - an array of cells. Numbers follow standard

View File

@ -1904,3 +1904,49 @@ int fdt_overlay_apply_verbose(void *fdt, void *fdto)
return err;
}
#endif
/**
* fdt_valid() - Check if an FDT is valid. If not, change it to NULL
*
* @blobp: Pointer to FDT pointer
* @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
*/
int fdt_valid(struct fdt_header **blobp)
{
const void *blob = *blobp;
int err;
if (!blob) {
printf("The address of the fdt is invalid (NULL).\n");
return 0;
}
err = fdt_check_header(blob);
if (err == 0)
return 1; /* valid */
if (err < 0) {
printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
/*
* Be more informative on bad version.
*/
if (err == -FDT_ERR_BADVERSION) {
if (fdt_version(blob) <
FDT_FIRST_SUPPORTED_VERSION) {
printf(" - too old, fdt %d < %d",
fdt_version(blob),
FDT_FIRST_SUPPORTED_VERSION);
}
if (fdt_last_comp_version(blob) >
FDT_LAST_SUPPORTED_VERSION) {
printf(" - too new, fdt %d > %d",
fdt_version(blob),
FDT_LAST_SUPPORTED_VERSION);
}
}
printf("\n");
*blobp = NULL;
return 0;
}
return 1;
}

View File

@ -1,6 +1,7 @@
CONFIG_ARM=y
CONFIG_ARCH_SUNXI=y
CONFIG_SPL=y
CONFIG_CHIP_DIP_SCAN=y
CONFIG_MACH_SUN5I=y
CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J=y
CONFIG_USB0_VBUS_PIN="PB10"

View 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

111
doc/usage/extension.rst Normal file
View File

@ -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;
}

View File

@ -53,3 +53,10 @@ U_BOOT_DRIVER(ds24xxx) = {
.ops = &ds24xxx_ops,
.probe = ds24xxx_probe,
};
u8 family_supported[] = {
W1_FAMILY_DS24B33,
W1_FAMILY_DS2431,
};
U_BOOT_W1_DEVICE(ds24xxx, family_supported);

View File

@ -243,3 +243,9 @@ U_BOOT_DRIVER(ds2502) = {
.ops = &ds2502_ops,
.probe = ds2502_probe,
};
u8 family_supported[] = {
W1_FAMILY_DS2502,
};
U_BOOT_W1_DEVICE(ds2502, family_supported);

View File

@ -37,37 +37,6 @@ int w1_eeprom_read_buf(struct udevice *dev, unsigned int offset,
return ops->read_buf(dev, offset, buf, count);
}
int w1_eeprom_register_new_device(u64 id)
{
u8 family = id & 0xff;
int ret;
struct udevice *dev;
for (ret = uclass_first_device(UCLASS_W1_EEPROM, &dev);
!ret && dev;
uclass_next_device(&dev)) {
if (ret || !dev) {
debug("cannot find w1 eeprom dev\n");
return ret;
}
if (dev_get_driver_data(dev) == family) {
struct w1_device *w1;
w1 = dev_get_parent_plat(dev);
if (w1->id) /* device already in use */
continue;
w1->id = id;
debug("%s: Match found: %s:%s %llx\n", __func__,
dev->name, dev->driver->name, id);
return 0;
}
}
debug("%s: No matches found: error %d\n", __func__, ret);
return ret;
}
int w1_eeprom_get_id(struct udevice *dev, u64 *id)
{
struct w1_device *w1 = dev_get_parent_plat(dev);

View File

@ -4,9 +4,11 @@
* Copyright (c) 2015 Free Electrons
* Copyright (c) 2015 NextThing Co.
* Copyright (c) 2018 Microchip Technology, Inc.
* Copyright (c) 2021 Bootlin
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
* Eugen Hristev <eugen.hristev@microchip.com>
* Kory Maincent <kory.maincent@bootlin.com>
*
*/
@ -26,6 +28,76 @@ struct w1_bus {
u64 search_id;
};
int w1_bus_find_dev(const struct udevice *bus, u64 id, struct udevice
**devp)
{
struct udevice *dev;
u8 family = id & 0xff;
int ret;
for (ret = uclass_first_device(UCLASS_W1_EEPROM, &dev);
!ret && dev;
uclass_next_device(&dev)) {
if (ret || !dev) {
debug("cannot find w1 eeprom dev\n");
return -ENODEV;
}
if (dev_get_driver_data(dev) == family) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
int w1_register_new_device(u64 id, struct udevice *bus)
{
u8 family = id & 0xff;
int n_ents, ret = 0;
struct udevice *dev;
struct w1_driver_entry *start, *entry;
start = ll_entry_start(struct w1_driver_entry, w1_driver_entry);
n_ents = ll_entry_count(struct w1_driver_entry, w1_driver_entry);
for (entry = start; entry != start + n_ents; entry++) {
const u8 *match_family;
const struct driver *drv;
struct w1_device *w1;
for (match_family = entry->family; match_family;
match_family++) {
if (*match_family != family)
continue;
ret = w1_bus_find_dev(bus, id, &dev);
/* If nothing in the device tree, bind a device */
if (ret == -ENODEV) {
drv = entry->driver;
ret = device_bind(bus, drv, drv->name,
NULL, ofnode_null(), &dev);
if (ret)
return ret;
}
device_probe(dev);
w1 = dev_get_parent_plat(dev);
w1->id = id;
return 0;
}
}
debug("%s: No matches found: error %d\n", __func__, ret);
return ret;
}
static int w1_enumerate(struct udevice *bus)
{
const struct w1_ops *ops = device_get_ops(bus);
@ -97,8 +169,8 @@ static int w1_enumerate(struct udevice *bus)
debug("%s: Detected new device 0x%llx (family 0x%x)\n",
bus->name, rn, (u8)(rn & 0xff));
/* attempt to register as w1-eeprom device */
w1_eeprom_register_new_device(rn);
/* attempt to register as w1 device */
w1_register_new_device(rn, bus);
}
}

31
include/extension_board.h Normal file
View File

@ -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 */

View File

@ -352,6 +352,8 @@ int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
int fdt_overlay_apply_verbose(void *fdt, void *fdto);
int fdt_valid(struct fdt_header **blobp);
/**
* fdt_get_cells_len() - Get the length of a type of cell in top-level nodes
*

View File

@ -27,7 +27,5 @@ int w1_eeprom_read_buf(struct udevice *dev, unsigned int offset,
int w1_eeprom_dm_init(void);
int w1_eeprom_register_new_device(u64 id);
int w1_eeprom_get_id(struct udevice *dev, u64 *id);
#endif

View File

@ -15,6 +15,23 @@ struct udevice;
#define W1_FAMILY_DS2502 0x09
#define W1_FAMILY_EEP_SANDBOX 0xfe
struct w1_driver_entry {
struct driver *driver;
u8 *family;
};
/* U_BOOT_W1_DEVICE() tells U-Boot to create a one-wire device.
*
* @__name: Device name (C identifier, not a string. E.g. gpio7_at_ff7e0000)
* @__driver: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000)
* @__family: Family code number of the one-wire
*/
#define U_BOOT_W1_DEVICE(__name, __family) \
ll_entry_declare(struct w1_driver_entry, __name, w1_driver_entry) = { \
.driver = llsym(struct driver, __name, driver), \
.family = __family, \
}
struct w1_device {
u64 id;
};

View File

@ -0,0 +1,53 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2020
# Author: Kory Maincent <kory.maincent@bootlin.com>
# Test U-Boot's "extension" commands.
import os
import pytest
import u_boot_utils
overlay_addr = 0x1000
SANDBOX_DTB='arch/sandbox/dts/sandbox.dtb'
OVERLAY_DIR='arch/sandbox/dts/'
def load_dtb(u_boot_console):
u_boot_console.log.action('Loading devicetree to RAM...')
u_boot_console.run_command('host load hostfs - $fdt_addr_r %s' % (os.path.join(u_boot_console.config.build_dir, SANDBOX_DTB)))
u_boot_console.run_command('fdt addr $fdt_addr_r')
@pytest.mark.buildconfigspec('cmd_fdt')
@pytest.mark.boardspec('sandbox')
def test_extension(u_boot_console):
"""Test the 'extension' command."""
load_dtb(u_boot_console)
output = u_boot_console.run_command('extension list')
assert('No extension' in output)
output = u_boot_console.run_command('extension scan')
assert output == 'Found 2 extension board(s).'
output = u_boot_console.run_command('extension list')
assert('overlay0.dtbo' in output)
assert('overlay1.dtbo' in output)
u_boot_console.run_command_list([
'setenv extension_overlay_addr %s' % (overlay_addr),
'setenv extension_overlay_cmd \'host load hostfs - ${extension_overlay_addr} %s${extension_overlay_name}\'' % (os.path.join(u_boot_console.config.build_dir, OVERLAY_DIR))])
output = u_boot_console.run_command('extension apply 0')
assert('bytes read' in output)
output = u_boot_console.run_command('fdt print')
assert('button3' in output)
output = u_boot_console.run_command('extension apply all')
assert('bytes read' in output)
output = u_boot_console.run_command('fdt print')
assert('button4' in output)