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 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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -220,6 +220,7 @@ void enable_basic_clocks(void)
 | 
			
		|||
		&cmper->gpio2clkctrl,
 | 
			
		||||
		&cmper->gpio3clkctrl,
 | 
			
		||||
		&cmper->i2c1clkctrl,
 | 
			
		||||
		&cmper->i2c2clkctrl,
 | 
			
		||||
		&cmper->cpgmac0clkctrl,
 | 
			
		||||
		&cmper->spi0clkctrl,
 | 
			
		||||
		&cmrtc->rtcclkctrl,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
/dts-v1/;
 | 
			
		||||
/plugin/;
 | 
			
		||||
 | 
			
		||||
&{/buttons} {
 | 
			
		||||
	btn3 {
 | 
			
		||||
		gpios = <&gpio_a 5 0>;
 | 
			
		||||
		label = "button3";
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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/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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	  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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
	.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,
 | 
			
		||||
	.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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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_valid(struct fdt_header **blobp);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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_register_new_device(u64 id);
 | 
			
		||||
 | 
			
		||||
int w1_eeprom_get_id(struct udevice *dev, u64 *id);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								include/w1.h
								
								
								
								
							
							
						
						
									
										17
									
								
								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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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