mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-06-09 23:36:03 +09:00
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:
commit
530c8d4af2
|
@ -121,6 +121,7 @@ config SANDBOX
|
||||||
select SUPPORT_OF_CONTROL
|
select SUPPORT_OF_CONTROL
|
||||||
select SYSRESET_CMD_POWEROFF
|
select SYSRESET_CMD_POWEROFF
|
||||||
select IRQ
|
select IRQ
|
||||||
|
select SUPPORT_EXTENSION_SCAN
|
||||||
imply BITREVERSE
|
imply BITREVERSE
|
||||||
select BLOBLIST
|
select BLOBLIST
|
||||||
imply CMD_DM
|
imply CMD_DM
|
||||||
|
@ -165,6 +166,7 @@ config SANDBOX
|
||||||
imply BOOTARGS_SUBST
|
imply BOOTARGS_SUBST
|
||||||
imply PHY_FIXED
|
imply PHY_FIXED
|
||||||
imply DM_DSA
|
imply DM_DSA
|
||||||
|
imply CMD_EXTENSION
|
||||||
|
|
||||||
config SH
|
config SH
|
||||||
bool "SuperH architecture"
|
bool "SuperH architecture"
|
||||||
|
|
|
@ -34,6 +34,7 @@ config TARGET_AM335X_EVM
|
||||||
select DM_GPIO
|
select DM_GPIO
|
||||||
select DM_SERIAL
|
select DM_SERIAL
|
||||||
select TI_I2C_BOARD_DETECT
|
select TI_I2C_BOARD_DETECT
|
||||||
|
select SUPPORT_EXTENSION_SCAN
|
||||||
imply CMD_DM
|
imply CMD_DM
|
||||||
imply SPL_DM
|
imply SPL_DM
|
||||||
imply SPL_DM_SEQ_ALIAS
|
imply SPL_DM_SEQ_ALIAS
|
||||||
|
|
|
@ -220,6 +220,7 @@ void enable_basic_clocks(void)
|
||||||
&cmper->gpio2clkctrl,
|
&cmper->gpio2clkctrl,
|
||||||
&cmper->gpio3clkctrl,
|
&cmper->gpio3clkctrl,
|
||||||
&cmper->i2c1clkctrl,
|
&cmper->i2c1clkctrl,
|
||||||
|
&cmper->i2c2clkctrl,
|
||||||
&cmper->cpgmac0clkctrl,
|
&cmper->cpgmac0clkctrl,
|
||||||
&cmper->spi0clkctrl,
|
&cmper->spi0clkctrl,
|
||||||
&cmrtc->rtcclkctrl,
|
&cmrtc->rtcclkctrl,
|
||||||
|
|
|
@ -36,6 +36,7 @@ config TARGET_AM57XX_EVM
|
||||||
select CMD_DDR3
|
select CMD_DDR3
|
||||||
select DRA7XX
|
select DRA7XX
|
||||||
select TI_I2C_BOARD_DETECT
|
select TI_I2C_BOARD_DETECT
|
||||||
|
select SUPPORT_EXTENSION_SCAN
|
||||||
imply DM_THERMAL
|
imply DM_THERMAL
|
||||||
imply SCSI
|
imply SCSI
|
||||||
imply SPL_THERMAL
|
imply SPL_THERMAL
|
||||||
|
|
|
@ -1089,3 +1089,12 @@ config BLUETOOTH_DT_DEVICE_FIXUP
|
||||||
flipped elsewise.
|
flipped elsewise.
|
||||||
|
|
||||||
endif
|
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
|
||||||
|
|
|
@ -6,6 +6,7 @@ else
|
||||||
dtb-$(CONFIG_SANDBOX) += sandbox.dtb
|
dtb-$(CONFIG_SANDBOX) += sandbox.dtb
|
||||||
endif
|
endif
|
||||||
dtb-$(CONFIG_UT_DM) += test.dtb
|
dtb-$(CONFIG_UT_DM) += test.dtb
|
||||||
|
dtb-$(CONFIG_CMD_EXTENSION) += overlay0.dtbo overlay1.dtbo
|
||||||
|
|
||||||
targets += $(dtb-y)
|
targets += $(dtb-y)
|
||||||
|
|
||||||
|
|
9
arch/sandbox/dts/overlay0.dts
Normal file
9
arch/sandbox/dts/overlay0.dts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/dts-v1/;
|
||||||
|
/plugin/;
|
||||||
|
|
||||||
|
&{/buttons} {
|
||||||
|
btn3 {
|
||||||
|
gpios = <&gpio_a 5 0>;
|
||||||
|
label = "button3";
|
||||||
|
};
|
||||||
|
};
|
9
arch/sandbox/dts/overlay1.dts
Normal file
9
arch/sandbox/dts/overlay1.dts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/dts-v1/;
|
||||||
|
/plugin/;
|
||||||
|
|
||||||
|
&{/buttons} {
|
||||||
|
btn4 {
|
||||||
|
gpios = <&gpio_a 5 0>;
|
||||||
|
label = "button4";
|
||||||
|
};
|
||||||
|
};
|
|
@ -14,6 +14,9 @@
|
||||||
#include <asm/global_data.h>
|
#include <asm/global_data.h>
|
||||||
#include <asm/test.h>
|
#include <asm/test.h>
|
||||||
#include <asm/u-boot-sandbox.h>
|
#include <asm/u-boot-sandbox.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include <extension_board.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pointer to initial global data area
|
* 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);
|
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
|
#ifdef CONFIG_BOARD_LATE_INIT
|
||||||
int board_late_init(void)
|
int board_late_init(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,3 +11,4 @@ obj-$(CONFIG_SUN7I_GMAC) += gmac.o
|
||||||
obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
|
obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
|
||||||
obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o
|
obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o
|
||||||
obj-$(CONFIG_MACH_SUN7I) += 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
100
board/sunxi/chip.c
Normal 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;
|
||||||
|
}
|
|
@ -44,6 +44,7 @@
|
||||||
#include <env_internal.h>
|
#include <env_internal.h>
|
||||||
#include <watchdog.h>
|
#include <watchdog.h>
|
||||||
#include "../common/board_detect.h"
|
#include "../common/board_detect.h"
|
||||||
|
#include "../common/cape_detect.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
@ -77,8 +78,10 @@ static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE;
|
||||||
void do_board_detect(void)
|
void do_board_detect(void)
|
||||||
{
|
{
|
||||||
enable_i2c0_pin_mux();
|
enable_i2c0_pin_mux();
|
||||||
|
enable_i2c2_pin_mux();
|
||||||
#if !CONFIG_IS_ENABLED(DM_I2C)
|
#if !CONFIG_IS_ENABLED(DM_I2C)
|
||||||
i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);
|
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
|
#endif
|
||||||
if (ti_i2c_eeprom_am_get(CONFIG_EEPROM_BUS_ADDRESS,
|
if (ti_i2c_eeprom_am_get(CONFIG_EEPROM_BUS_ADDRESS,
|
||||||
CONFIG_EEPROM_CHIP_ADDRESS))
|
CONFIG_EEPROM_CHIP_ADDRESS))
|
||||||
|
|
|
@ -93,5 +93,6 @@ void enable_uart3_pin_mux(void);
|
||||||
void enable_uart4_pin_mux(void);
|
void enable_uart4_pin_mux(void);
|
||||||
void enable_uart5_pin_mux(void);
|
void enable_uart5_pin_mux(void);
|
||||||
void enable_i2c0_pin_mux(void);
|
void enable_i2c0_pin_mux(void);
|
||||||
|
void enable_i2c2_pin_mux(void);
|
||||||
void enable_board_pin_mux(void);
|
void enable_board_pin_mux(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -124,6 +124,14 @@ static struct module_pin_mux i2c1_pin_mux[] = {
|
||||||
{-1},
|
{-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[] = {
|
static struct module_pin_mux spi0_pin_mux[] = {
|
||||||
{OFFSET(spi0_sclk), (MODE(0) | RXACTIVE | PULLUDEN)}, /* SPI0_SCLK */
|
{OFFSET(spi0_sclk), (MODE(0) | RXACTIVE | PULLUDEN)}, /* SPI0_SCLK */
|
||||||
{OFFSET(spi0_d0), (MODE(0) | RXACTIVE |
|
{OFFSET(spi0_d0), (MODE(0) | RXACTIVE |
|
||||||
|
@ -308,6 +316,11 @@ void enable_i2c0_pin_mux(void)
|
||||||
configure_module_pin_mux(i2c0_pin_mux);
|
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
|
* The AM335x GP EVM, if daughter card(s) are connected, can have 8
|
||||||
* different profiles. These profiles determine what peripherals are
|
* different profiles. These profiles determine what peripherals are
|
||||||
|
@ -367,6 +380,7 @@ void enable_board_pin_mux(void)
|
||||||
#else
|
#else
|
||||||
configure_module_pin_mux(mmc1_pin_mux);
|
configure_module_pin_mux(mmc1_pin_mux);
|
||||||
#endif
|
#endif
|
||||||
|
configure_module_pin_mux(i2c2_pin_mux);
|
||||||
} else if (board_is_gp_evm()) {
|
} else if (board_is_gp_evm()) {
|
||||||
/* General Purpose EVM */
|
/* General Purpose EVM */
|
||||||
unsigned short profile = detect_daughter_board_profile();
|
unsigned short profile = detect_daughter_board_profile();
|
||||||
|
@ -411,6 +425,7 @@ void enable_board_pin_mux(void)
|
||||||
#else
|
#else
|
||||||
configure_module_pin_mux(mmc1_pin_mux);
|
configure_module_pin_mux(mmc1_pin_mux);
|
||||||
#endif
|
#endif
|
||||||
|
configure_module_pin_mux(i2c2_pin_mux);
|
||||||
} else if (board_is_pb()) {
|
} else if (board_is_pb()) {
|
||||||
configure_module_pin_mux(mii1_pin_mux);
|
configure_module_pin_mux(mii1_pin_mux);
|
||||||
configure_module_pin_mux(mmc0_pin_mux);
|
configure_module_pin_mux(mmc0_pin_mux);
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <hang.h>
|
#include <hang.h>
|
||||||
|
|
||||||
#include "../common/board_detect.h"
|
#include "../common/board_detect.h"
|
||||||
|
#include "../common/cape_detect.h"
|
||||||
#include "mux_data.h"
|
#include "mux_data.h"
|
||||||
|
|
||||||
#ifdef CONFIG_SUPPORT_EMMC_BOOT
|
#ifdef CONFIG_SUPPORT_EMMC_BOOT
|
||||||
|
|
|
@ -16,6 +16,12 @@ config EEPROM_CHIP_ADDRESS
|
||||||
default 0x50
|
default 0x50
|
||||||
depends on TI_I2C_BOARD_DETECT
|
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
|
config TI_COMMON_CMD_OPTIONS
|
||||||
bool "Enable cmd options on TI platforms"
|
bool "Enable cmd options on TI platforms"
|
||||||
imply CMD_ASKENV
|
imply CMD_ASKENV
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
# Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
# Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
|
||||||
obj-${CONFIG_TI_I2C_BOARD_DETECT} += board_detect.o
|
obj-${CONFIG_TI_I2C_BOARD_DETECT} += board_detect.o
|
||||||
|
obj-${CONFIG_CMD_EXTENSION} += cape_detect.o
|
||||||
|
|
96
board/ti/common/cape_detect.c
Normal file
96
board/ti/common/cape_detect.c
Normal 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;
|
||||||
|
}
|
28
board/ti/common/cape_detect.h
Normal file
28
board/ti/common/cape_detect.h
Normal 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 */
|
12
cmd/Kconfig
12
cmd/Kconfig
|
@ -332,6 +332,18 @@ config CMD_FDT
|
||||||
help
|
help
|
||||||
Do FDT related setup before booting into the Operating System.
|
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
|
config CMD_GO
|
||||||
bool "go"
|
bool "go"
|
||||||
default y
|
default y
|
||||||
|
|
|
@ -54,6 +54,7 @@ obj-$(CONFIG_CMD_DIAG) += diag.o
|
||||||
endif
|
endif
|
||||||
obj-$(CONFIG_CMD_ADTIMG) += adtimg.o
|
obj-$(CONFIG_CMD_ADTIMG) += adtimg.o
|
||||||
obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o
|
obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o
|
||||||
|
obj-$(CONFIG_CMD_EXTENSION) += extension_board.o
|
||||||
obj-$(CONFIG_CMD_ECHO) += echo.o
|
obj-$(CONFIG_CMD_ECHO) += echo.o
|
||||||
obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o
|
obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o
|
||||||
obj-$(CONFIG_CMD_EEPROM) += eeprom.o
|
obj-$(CONFIG_CMD_EEPROM) += eeprom.o
|
||||||
|
|
167
cmd/extension_board.c
Normal file
167
cmd/extension_board.c
Normal 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"
|
||||||
|
);
|
49
cmd/fdt.c
49
cmd/fdt.c
|
@ -27,7 +27,6 @@
|
||||||
*/
|
*/
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
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_parse_prop(char *const*newval, int count, char *data, int *len);
|
||||||
static int fdt_print(const char *pathp, char *prop, int depth);
|
static int fdt_print(const char *pathp, char *prop, int depth);
|
||||||
static int is_printable_string(const void *data, int len);
|
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:
|
* Parse the user's input, partially heuristic. Valid formats:
|
||||||
* <0x00112233 4 05> - an array of cells. Numbers follow standard
|
* <0x00112233 4 05> - an array of cells. Numbers follow standard
|
||||||
|
|
|
@ -1904,3 +1904,49 @@ int fdt_overlay_apply_verbose(void *fdt, void *fdto)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
CONFIG_ARM=y
|
CONFIG_ARM=y
|
||||||
CONFIG_ARCH_SUNXI=y
|
CONFIG_ARCH_SUNXI=y
|
||||||
CONFIG_SPL=y
|
CONFIG_SPL=y
|
||||||
|
CONFIG_CHIP_DIP_SCAN=y
|
||||||
CONFIG_MACH_SUN5I=y
|
CONFIG_MACH_SUN5I=y
|
||||||
CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J=y
|
CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J=y
|
||||||
CONFIG_USB0_VBUS_PIN="PB10"
|
CONFIG_USB0_VBUS_PIN="PB10"
|
||||||
|
|
|
@ -8,6 +8,7 @@ CONFIG_MISC_INIT_F=y
|
||||||
# CONFIG_CMD_BOOTD is not set
|
# CONFIG_CMD_BOOTD is not set
|
||||||
# CONFIG_CMD_BOOTM is not set
|
# CONFIG_CMD_BOOTM is not set
|
||||||
# CONFIG_CMD_ELF is not set
|
# CONFIG_CMD_ELF is not set
|
||||||
|
# CONFIG_CMD_EXTENSION is not set
|
||||||
CONFIG_BOOTP_DNS2=y
|
CONFIG_BOOTP_DNS2=y
|
||||||
# CONFIG_CMD_DATE is not set
|
# CONFIG_CMD_DATE is not set
|
||||||
CONFIG_OF_CONTROL=y
|
CONFIG_OF_CONTROL=y
|
||||||
|
|
111
doc/usage/extension.rst
Normal file
111
doc/usage/extension.rst
Normal 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;
|
||||||
|
}
|
|
@ -53,3 +53,10 @@ U_BOOT_DRIVER(ds24xxx) = {
|
||||||
.ops = &ds24xxx_ops,
|
.ops = &ds24xxx_ops,
|
||||||
.probe = ds24xxx_probe,
|
.probe = ds24xxx_probe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u8 family_supported[] = {
|
||||||
|
W1_FAMILY_DS24B33,
|
||||||
|
W1_FAMILY_DS2431,
|
||||||
|
};
|
||||||
|
|
||||||
|
U_BOOT_W1_DEVICE(ds24xxx, family_supported);
|
||||||
|
|
|
@ -243,3 +243,9 @@ U_BOOT_DRIVER(ds2502) = {
|
||||||
.ops = &ds2502_ops,
|
.ops = &ds2502_ops,
|
||||||
.probe = ds2502_probe,
|
.probe = ds2502_probe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u8 family_supported[] = {
|
||||||
|
W1_FAMILY_DS2502,
|
||||||
|
};
|
||||||
|
|
||||||
|
U_BOOT_W1_DEVICE(ds2502, family_supported);
|
||||||
|
|
|
@ -37,37 +37,6 @@ int w1_eeprom_read_buf(struct udevice *dev, unsigned int offset,
|
||||||
return ops->read_buf(dev, offset, buf, count);
|
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)
|
int w1_eeprom_get_id(struct udevice *dev, u64 *id)
|
||||||
{
|
{
|
||||||
struct w1_device *w1 = dev_get_parent_plat(dev);
|
struct w1_device *w1 = dev_get_parent_plat(dev);
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
* Copyright (c) 2015 Free Electrons
|
* Copyright (c) 2015 Free Electrons
|
||||||
* Copyright (c) 2015 NextThing Co.
|
* Copyright (c) 2015 NextThing Co.
|
||||||
* Copyright (c) 2018 Microchip Technology, Inc.
|
* Copyright (c) 2018 Microchip Technology, Inc.
|
||||||
|
* Copyright (c) 2021 Bootlin
|
||||||
*
|
*
|
||||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||||
* Eugen Hristev <eugen.hristev@microchip.com>
|
* Eugen Hristev <eugen.hristev@microchip.com>
|
||||||
|
* Kory Maincent <kory.maincent@bootlin.com>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -26,6 +28,76 @@ struct w1_bus {
|
||||||
u64 search_id;
|
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)
|
static int w1_enumerate(struct udevice *bus)
|
||||||
{
|
{
|
||||||
const struct w1_ops *ops = device_get_ops(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",
|
debug("%s: Detected new device 0x%llx (family 0x%x)\n",
|
||||||
bus->name, rn, (u8)(rn & 0xff));
|
bus->name, rn, (u8)(rn & 0xff));
|
||||||
|
|
||||||
/* attempt to register as w1-eeprom device */
|
/* attempt to register as w1 device */
|
||||||
w1_eeprom_register_new_device(rn);
|
w1_register_new_device(rn, bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
31
include/extension_board.h
Normal file
31
include/extension_board.h
Normal 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 */
|
|
@ -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_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
|
* fdt_get_cells_len() - Get the length of a type of cell in top-level nodes
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,7 +27,5 @@ int w1_eeprom_read_buf(struct udevice *dev, unsigned int offset,
|
||||||
|
|
||||||
int w1_eeprom_dm_init(void);
|
int w1_eeprom_dm_init(void);
|
||||||
|
|
||||||
int w1_eeprom_register_new_device(u64 id);
|
|
||||||
|
|
||||||
int w1_eeprom_get_id(struct udevice *dev, u64 *id);
|
int w1_eeprom_get_id(struct udevice *dev, u64 *id);
|
||||||
#endif
|
#endif
|
||||||
|
|
17
include/w1.h
17
include/w1.h
|
@ -15,6 +15,23 @@ struct udevice;
|
||||||
#define W1_FAMILY_DS2502 0x09
|
#define W1_FAMILY_DS2502 0x09
|
||||||
#define W1_FAMILY_EEP_SANDBOX 0xfe
|
#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 {
|
struct w1_device {
|
||||||
u64 id;
|
u64 id;
|
||||||
};
|
};
|
||||||
|
|
53
test/py/tests/test_extension.py
Normal file
53
test/py/tests/test_extension.py
Normal 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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user