diff options
35 files changed, 835 insertions, 84 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index e61a752..6c4b81a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -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" diff --git a/arch/arm/mach-omap2/am33xx/Kconfig b/arch/arm/mach-omap2/am33xx/Kconfig index 9a98e8a..11e54cd 100644 --- a/arch/arm/mach-omap2/am33xx/Kconfig +++ b/arch/arm/mach-omap2/am33xx/Kconfig @@ -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 diff --git a/arch/arm/mach-omap2/am33xx/clock_am33xx.c b/arch/arm/mach-omap2/am33xx/clock_am33xx.c index cf71192..3a7ac60 100644 --- a/arch/arm/mach-omap2/am33xx/clock_am33xx.c +++ b/arch/arm/mach-omap2/am33xx/clock_am33xx.c @@ -220,6 +220,7 @@ void enable_basic_clocks(void) &cmper->gpio2clkctrl, &cmper->gpio3clkctrl, &cmper->i2c1clkctrl, + &cmper->i2c2clkctrl, &cmper->cpgmac0clkctrl, &cmper->spi0clkctrl, &cmrtc->rtcclkctrl, diff --git a/arch/arm/mach-omap2/omap5/Kconfig b/arch/arm/mach-omap2/omap5/Kconfig index a7132da..4c2f990 100644 --- a/arch/arm/mach-omap2/omap5/Kconfig +++ b/arch/arm/mach-omap2/omap5/Kconfig @@ -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 diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 8e9012d..bc8509b 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -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 diff --git a/arch/sandbox/dts/Makefile b/arch/sandbox/dts/Makefile index d231dc2..3e5dc67 100644 --- a/arch/sandbox/dts/Makefile +++ b/arch/sandbox/dts/Makefile @@ -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) diff --git a/arch/sandbox/dts/overlay0.dts b/arch/sandbox/dts/overlay0.dts new file mode 100644 index 0000000..70c6cf7 --- /dev/null +++ b/arch/sandbox/dts/overlay0.dts @@ -0,0 +1,9 @@ +/dts-v1/; +/plugin/; + +&{/buttons} { + btn3 { + gpios = <&gpio_a 5 0>; + label = "button3"; + }; +}; diff --git a/arch/sandbox/dts/overlay1.dts b/arch/sandbox/dts/overlay1.dts new file mode 100644 index 0000000..51621b3 --- /dev/null +++ b/arch/sandbox/dts/overlay1.dts @@ -0,0 +1,9 @@ +/dts-v1/; +/plugin/; + +&{/buttons} { + btn4 { + gpios = <&gpio_a 5 0>; + label = "button4"; + }; +}; diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 902b99e..dcd7345 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -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) { diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile index c4e13f8..d96b789 100644 --- a/board/sunxi/Makefile +++ b/board/sunxi/Makefile @@ -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 diff --git a/board/sunxi/chip.c b/board/sunxi/chip.c new file mode 100644 index 0000000..cde04be --- /dev/null +++ b/board/sunxi/chip.c @@ -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; +} diff --git a/board/ti/am335x/board.c b/board/ti/am335x/board.c index bc1657e..5959ff7 100644 --- a/board/ti/am335x/board.c +++ b/board/ti/am335x/board.c @@ -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)) diff --git a/board/ti/am335x/board.h b/board/ti/am335x/board.h index 48df914..c296211 100644 --- a/board/ti/am335x/board.h +++ b/board/ti/am335x/board.h @@ -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 diff --git a/board/ti/am335x/mux.c b/board/ti/am335x/mux.c index 03adcd2..e450ff6 100644 --- a/board/ti/am335x/mux.c +++ b/board/ti/am335x/mux.c @@ -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); diff --git a/board/ti/am57xx/board.c b/board/ti/am57xx/board.c index 73063fa..05c26c7 100644 --- a/board/ti/am57xx/board.c +++ b/board/ti/am57xx/board.c @@ -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 diff --git a/board/ti/common/Kconfig b/board/ti/common/Kconfig index 9ead7ca..49edd98 100644 --- a/board/ti/common/Kconfig +++ b/board/ti/common/Kconfig @@ -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 diff --git a/board/ti/common/Makefile b/board/ti/common/Makefile index cb97f22..3172d87 100644 --- a/board/ti/common/Makefile +++ b/board/ti/common/Makefile @@ -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 diff --git a/board/ti/common/cape_detect.c b/board/ti/common/cape_detect.c new file mode 100644 index 0000000..2e6105c --- /dev/null +++ b/board/ti/common/cape_detect.c @@ -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; +} diff --git a/board/ti/common/cape_detect.h b/board/ti/common/cape_detect.h new file mode 100644 index 0000000..b0d5c9f --- /dev/null +++ b/board/ti/common/cape_detect.h @@ -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 */ diff --git a/cmd/Kconfig b/cmd/Kconfig index 9e8b692..f962bb7 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 4977fa1..9d10e07 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 0000000..6ad9765 --- /dev/null +++ b/cmd/extension_board.c @@ -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; + 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" +); @@ -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 diff --git a/common/fdt_support.c b/common/fdt_support.c index 695d8e1..a9a32df 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -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; +} diff --git a/configs/CHIP_defconfig b/configs/CHIP_defconfig index a70ee31..8d40da0 100644 --- a/configs/CHIP_defconfig +++ b/configs/CHIP_defconfig @@ -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" diff --git a/doc/usage/extension.rst b/doc/usage/extension.rst new file mode 100644 index 0000000..2b88398 --- /dev/null +++ b/doc/usage/extension.rst @@ -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/drivers/w1-eeprom/ds24xxx.c b/drivers/w1-eeprom/ds24xxx.c index d12fd57..4be378b 100644 --- a/drivers/w1-eeprom/ds24xxx.c +++ b/drivers/w1-eeprom/ds24xxx.c @@ -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); diff --git a/drivers/w1-eeprom/ds2502.c b/drivers/w1-eeprom/ds2502.c index b3d68d7..a67f5ed 100644 --- a/drivers/w1-eeprom/ds2502.c +++ b/drivers/w1-eeprom/ds2502.c @@ -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); diff --git a/drivers/w1-eeprom/w1-eeprom-uclass.c b/drivers/w1-eeprom/w1-eeprom-uclass.c index 97a9d43..7a02af3 100644 --- a/drivers/w1-eeprom/w1-eeprom-uclass.c +++ b/drivers/w1-eeprom/w1-eeprom-uclass.c @@ -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); diff --git a/drivers/w1/w1-uclass.c b/drivers/w1/w1-uclass.c index 8bc6cb1..16aeeb6 100644 --- a/drivers/w1/w1-uclass.c +++ b/drivers/w1/w1-uclass.c @@ -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; + 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); } } diff --git a/include/extension_board.h b/include/extension_board.h new file mode 100644 index 0000000..c530a0a --- /dev/null +++ b/include/extension_board.h @@ -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 */ diff --git a/include/fdt_support.h b/include/fdt_support.h index e2a4689..1e4dbc0 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -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 * diff --git a/include/w1-eeprom.h b/include/w1-eeprom.h index 2233736..b3cf77a 100644 --- a/include/w1-eeprom.h +++ b/include/w1-eeprom.h @@ -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 diff --git a/include/w1.h b/include/w1.h index 77f439e..b18078b 100644 --- a/include/w1.h +++ b/include/w1.h @@ -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; }; diff --git a/test/py/tests/test_extension.py b/test/py/tests/test_extension.py new file mode 100644 index 0000000..4712f9b --- /dev/null +++ b/test/py/tests/test_extension.py @@ -0,0 +1,52 @@ +# 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') +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) + |