dtoc support for of-platdata-inst

driver model support for of-platdata-inst
 support of-platdata-inst on x86 / coral
 binman support for exapanded entries
 binman convert docs to reST
 ti-sysc fix for duplicate uclass driver
 patman minor improvements
 pylibfdt build only if needed
 correct obscure CI error with OF_PLATDATA_INST
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmBdZYURHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreb2rQgAyr1rufrt1UGjZlVjk0HtqX0sdmcOoE4e
 6NIuatWXPcP2QR93O9zeGgf/yqNIf3lVIa6Cy3baJBfP6fceZ8B24/xgKKEauPFf
 g99sec+q7LPL/oigajXIaWorFXK/NDRtQcSIQFu/EmvCmi0m8Iu0HKFa7YBqa7dc
 YdhGnJZZOdYuQmlKewq9q4cIr6qNtTZczsozt4PNZVgB2lKusBNyKRPZErNEFiN6
 7A56bb8tcfWgGXenKVTUUcyjWRXM2tt4QtrPFedZsF6cLa8D5v4MfWMs0QBRFl/u
 fMzxyeb46x0wsYBNqIBOC7DQWaU/ZeFlr5mQObIH0ypdcsUnEX98sw==
 =Ld3P
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-26mar21-take2' of git://git.denx.de/u-boot-dm into next

dtoc support for of-platdata-inst
driver model support for of-platdata-inst
support of-platdata-inst on x86 / coral
binman support for exapanded entries
binman convert docs to reST
ti-sysc fix for duplicate uclass driver
patman minor improvements
pylibfdt build only if needed
correct obscure CI error with OF_PLATDATA_INST
This commit is contained in:
Tom Rini 2021-03-26 12:15:26 -04:00
commit 9c7335e4e6
192 changed files with 6561 additions and 1546 deletions

View File

@ -148,7 +148,7 @@ jobs:
export UBOOT_TRAVIS_BUILD_DIR=/tmp/sandbox_spl
export PYTHONPATH=${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt
export PATH=${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}
./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w sandbox_spl
./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w --board sandbox_spl
./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test
./tools/buildman/buildman -t
./tools/dtoc/dtoc -t
@ -187,6 +187,9 @@ jobs:
sandbox_spl:
TEST_PY_BD: "sandbox_spl"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
sandbox_noinst:
TEST_PY_BD: "sandbox_noinst"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
sandbox_flattree:
TEST_PY_BD: "sandbox_flattree"
evb_ast2500:

View File

@ -168,7 +168,8 @@ Run binman, buildman, dtoc, Kconfig and patman testsuites:
export UBOOT_TRAVIS_BUILD_DIR=/tmp/sandbox_spl;
export PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt";
export PATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}";
./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w sandbox_spl;
./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w
--board sandbox_spl;
./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test;
./tools/buildman/buildman -t;
./tools/dtoc/dtoc -t;
@ -204,6 +205,13 @@ sandbox_spl test.py:
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
<<: *buildman_and_testpy_dfn
sandbox_noinst_test.py:
tags: [ 'all' ]
variables:
TEST_PY_BD: "sandbox_noinst"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
<<: *buildman_and_testpy_dfn
evb-ast2500 test.py:
tags: [ 'all' ]
variables:

View File

@ -17,9 +17,13 @@ NAME =
# o Look for make include files relative to root of kernel src
MAKEFLAGS += -rR --include-dir=$(CURDIR)
# Determine host architecture
# Determine target architecture for the sandbox
include include/host_arch.h
MK_ARCH="${shell uname -m}"
ifeq ("", "$(CROSS_COMPILE)")
MK_ARCH="${shell uname -m}"
else
MK_ARCH="${shell echo $(CROSS_COMPILE) | sed -n 's/^\s*\([^\/]*\/\)*\([^-]*\)-\S*/\2/p'}"
endif
unexport HOST_ARCH
ifeq ("x86_64", $(MK_ARCH))
export HOST_ARCH=$(HOST_ARCH_X86_64)
@ -27,7 +31,7 @@ else ifneq (,$(findstring $(MK_ARCH), "i386" "i486" "i586" "i686"))
export HOST_ARCH=$(HOST_ARCH_X86)
else ifneq (,$(findstring $(MK_ARCH), "aarch64" "armv8l"))
export HOST_ARCH=$(HOST_ARCH_AARCH64)
else ifeq ("armv7l", $(MK_ARCH))
else ifneq (,$(findstring $(MK_ARCH), "arm" "armv7" "armv7l"))
export HOST_ARCH=$(HOST_ARCH_ARM)
else ifeq ("riscv32", $(MK_ARCH))
export HOST_ARCH=$(HOST_ARCH_RISCV32)
@ -1328,6 +1332,11 @@ u-boot.ldr: u-boot
# Use 'make BINMAN_DEBUG=1' to enable debugging
# Use 'make BINMAN_VERBOSE=3' to set vebosity level
default_dt := $(if $(DEVICE_TREE),$(DEVICE_TREE),$(CONFIG_DEFAULT_DEVICE_TREE))
# Tell binman whether we have a devicetree for SPL and TPL
have_spl_dt := $(if $(CONFIG_SPL_OF_PLATDATA),,$(CONFIG_SPL_OF_CONTROL))
have_tpl_dt := $(if $(CONFIG_TPL_OF_PLATDATA),,$(CONFIG_TPL_OF_CONTROL))
quiet_cmd_binman = BINMAN $@
cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
--toolpath $(objtree)/tools \
@ -1338,6 +1347,9 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-a atf-bl31-path=${BL31} \
-a default-dt=$(default_dt) \
-a scp-path=$(SCP) \
-a spl-bss-pad=$(if $(CONFIG_SPL_SEPARATE_BSS),,1) \
-a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \
-a spl-dtb=$(have_spl_dt) -a tpl-dtb=$(have_tpl_dt) \
$(BINMAN_$(@F))
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex

View File

@ -845,7 +845,6 @@ int os_spl_to_uboot(const char *fname)
{
struct sandbox_state *state = state_get_current();
printf("%s\n", __func__);
/* U-Boot will delete ram buffer after read: "--rm_memory"*/
state->ram_buf_rm = true;
return os_jump_to_file(fname);

View File

@ -13,6 +13,14 @@ SECTIONS
KEEP(*(SORT(.u_boot_list*)));
}
/* Private data for devices with OF_PLATDATA_RT */
. = ALIGN(4);
.priv_data : {
__priv_data_start = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.priv_data*)))
__priv_data_end = .;
}
__u_boot_sandbox_option_start = .;
_u_boot_sandbox_getopt : { KEEP(*(.u_boot_sandbox_getopt)) }
__u_boot_sandbox_option_end = .;

View File

@ -31,7 +31,7 @@
clk_fixed: clk-fixed {
u-boot,dm-pre-reloc;
compatible = "fixed-clock";
compatible = "sandbox,fixed-clock";
#clock-cells = <0>;
clock-frequency = <1234>;
};
@ -101,15 +101,19 @@
};
i2c_emul: emul {
u-boot,dm-pre-reloc;
reg = <0xff>;
compatible = "sandbox,i2c-emul-parent";
emul_eeprom: emul-eeprom {
compatible = "sandbox,i2c-eeprom";
sandbox,filename = "i2c.bin";
sandbox,size = <256>;
#emul-cells = <0>;
};
emul0: emul0 {
compatible = "sandbox,i2c-rtc";
u-boot,dm-pre-reloc;
compatible = "sandbox,i2c-rtc-emul";
#emul-cells = <0>;
};
};
};
@ -260,14 +264,10 @@
stringarray = "pre-proper";
};
test-bus {
compatible = "simple-bus";
spl-test7 {
u-boot,dm-spl;
spl-test7 {
u-boot,dm-spl;
compatible = "sandbox,spl-test";
stringarray = "spl";
};
compatible = "sandbox,spl-test";
stringarray = "spl";
};
square {

View File

@ -604,10 +604,10 @@
sandbox,size = <256>;
};
emul0: emul0 {
compatible = "sandbox,i2c-rtc";
compatible = "sandbox,i2c-rtc-emul";
};
emul1: emull {
compatible = "sandbox,i2c-rtc";
compatible = "sandbox,i2c-rtc-emul";
};
};
@ -1402,3 +1402,4 @@
};
#include "sandbox_pmic.dtsi"
#include "cros-ec-keyboard.dtsi"

View File

@ -7,6 +7,9 @@
#define __SANDBOX_CLK_H
#include <common.h>
#include <clk.h>
#include <dt-structs.h>
#include <linux/clk-provider.h>
struct udevice;
@ -45,6 +48,27 @@ enum sandbox_clk_test_id {
#define SANDBOX_CLK_TEST_NON_DEVM_COUNT SANDBOX_CLK_TEST_ID_DEVM1
struct sandbox_clk_priv {
bool probed;
ulong rate[SANDBOX_CLK_ID_COUNT];
bool enabled[SANDBOX_CLK_ID_COUNT];
bool requested[SANDBOX_CLK_ID_COUNT];
};
struct sandbox_clk_test {
struct clk clks[SANDBOX_CLK_TEST_NON_DEVM_COUNT];
struct clk *clkps[SANDBOX_CLK_TEST_ID_COUNT];
struct clk_bulk bulk;
};
/* Platform data for the sandbox fixed-rate clock driver */
struct sandbox_clk_fixed_rate_plat {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct dtd_sandbox_fixed_clock dtplat;
#endif
struct clk_fixed_rate fixed;
};
/**
* sandbox_clk_query_rate - Query the current rate of a sandbox clock.
*

View File

@ -11,4 +11,19 @@ struct sandbox_i2c_priv {
bool test_mode;
};
/**
* struct i2c_emul_uc_plat - information about the emulator for this device
*
* This is used by devices in UCLASS_I2C_EMUL to record information about the
* device being emulated. It is accessible with dev_get_uclass_plat()
*
* @dev: Device being emulated
* @idx: of-platdata index, set up by the device's bind() method if of-platdata
* is in use
*/
struct i2c_emul_uc_plat {
struct udevice *dev;
int idx;
};
#endif /* __asn_i2c_h */

View File

@ -9,6 +9,8 @@
#ifndef __asm_rtc_h
#define __asm_rtc_h
#include <dt-structs.h>
/* Register numbers in the sandbox RTC */
enum {
REG_SEC = 5,
@ -29,4 +31,26 @@ enum {
REG_COUNT = 0x80,
};
/**
* struct sandbox_i2c_rtc_plat_data - platform data for the RTC
*
* @base_time: Base system time when RTC device was bound
* @offset: RTC offset from current system time
* @use_system_time: true to use system time, false to use @base_time
* @reg: Register values
*/
struct sandbox_i2c_rtc_plat_data {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct dtd_sandbox_i2c_rtc_emul dtplat;
#endif
long base_time;
long offset;
bool use_system_time;
u8 reg[REG_COUNT];
};
struct sandbox_i2c_rtc {
unsigned int offset_secs;
};
#endif

View File

@ -21,6 +21,8 @@ config INTEL_APOLLOLAKE
select INTEL_GMA_SWSMISCI
select ACPI_GNVS_EXTERNAL
select TPL_OF_PLATDATA_PARENT
select TPL_OF_PLATDATA_INST
select TPL_READ_ONLY
imply ENABLE_MRC_CACHE
imply AHCI_PCI
imply SCSI

View File

@ -9,8 +9,8 @@
#define LOG_CATEGORY UCLASS_ACPI_PMC
#include <common.h>
#include <dt-structs.h>
#include <dm.h>
#include <dt-structs.h>
#include <log.h>
#include <spl.h>
#include <acpi/acpi_s3.h>

View File

@ -93,4 +93,5 @@ U_BOOT_DRIVER(intel_apl_punit) = {
.id = UCLASS_SYSCON,
.of_match = apl_syscon_ids,
.probe = apl_punit_probe,
DM_HEADER(<asm/cpu.h>) /* for X86_SYSCON_PUNIT */
};

View File

@ -153,8 +153,9 @@ static int route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num)
static int itss_bind(struct udevice *dev)
{
/* This is not set with of-platdata, so set it manually */
if (CONFIG_IS_ENABLED(OF_PLATDATA))
/* This is not set with basic of-platdata, so set it manually */
if (CONFIG_IS_ENABLED(OF_PLATDATA) &&
!CONFIG_IS_ENABLED(OF_PLATDATA_INST))
dev->driver_data = X86_IRQT_ITSS;
return 0;

View File

@ -32,6 +32,14 @@ SECTIONS
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.priv_data : {
__priv_data_start = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.priv_data*)))
__priv_data_end = .;
}
. = ALIGN(4);
.data : { *(.data*) }

View File

@ -100,7 +100,7 @@
clk: clock {
compatible = "intel,apl-clk";
#clock-cells = <1>;
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
};
cpus {
@ -141,7 +141,7 @@
};
acpi_gpe: general-purpose-events {
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
reg = <IOMAP_ACPI_BASE IOMAP_ACPI_SIZE>;
compatible = "intel,acpi-gpe";
interrupt-controller;
@ -423,7 +423,7 @@
compatible = "intel,apl-i2c", "snps,designware-i2c-pci";
reg = <0x0200b210 0 0 0 0>;
early-regs = <IOMAP_I2C2_BASE 0x1000>;
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <400000>;
@ -434,7 +434,7 @@
tpm: tpm@50 {
reg = <0x50>;
compatible = "google,cr50";
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
u-boot,i2c-offset-len = <0>;
ready-gpios = <&gpio_n 28 GPIO_ACTIVE_LOW>;
interrupts-extended = <&acpi_gpe GPIO_28_IRQ
@ -1233,5 +1233,5 @@
&rtc {
#address-cells = <1>;
#size-cells = <0>;
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
};

View File

@ -1,6 +1,6 @@
/ {
reset: reset {
compatible = "x86,reset";
u-boot,dm-pre-reloc;
u-boot,dm-pre-proper;
};
};

View File

@ -38,20 +38,11 @@
};
#endif
spl {
type = "section";
type = "u-boot-spl";
offset = <CONFIG_X86_OFFSET_SPL>;
u-boot-spl {
};
u-boot-spl-dtb {
};
};
u-boot {
type = "section";
offset = <CONFIG_X86_OFFSET_U_BOOT>;
u-boot-nodtb {
};
u-boot-dtb {
};
};
#elif defined(CONFIG_SPL)
u-boot-spl-with-ucode-ptr {
@ -64,11 +55,7 @@
offset = <CONFIG_X86_OFFSET_U_BOOT>;
};
#else
# ifdef CONFIG_SPL
u-boot {
offset = <CONFIG_SYS_TEXT_BASE>;
};
# elif defined(CONFIG_HAVE_MICROCODE)
# ifdef CONFIG_HAVE_MICROCODE
/* If there is no SPL then we need to put microcode in U-Boot */
u-boot-with-ucode-ptr {
offset = <CONFIG_X86_OFFSET_U_BOOT>;

View File

@ -150,5 +150,6 @@ U_BOOT_DRIVER(pci_x86) = {
.name = "pci_x86",
.id = UCLASS_SIMPLE_BUS,
.of_match = of_match_ptr(tpl_fake_pci_ids),
DM_PHASE(tpl)
};
#endif

View File

@ -20,6 +20,13 @@ F: board/sandbox/
F: include/configs/sandbox_spl.h
F: configs/sandbox_spl_defconfig
SANDBOX NOINST BOARD
M: Simon Glass <sjg@chromium.org>
S: Maintained
F: board/sandbox/
F: include/configs/sandbox_spl.h
F: configs/sandbox_noinst_defconfig
SANDBOX FLAT TREE BOARD
M: Simon Glass <sjg@chromium.org>
S: Maintained

View File

@ -48,6 +48,7 @@ unsigned long timer_read_counter(void)
static enum env_location env_locations[] = {
ENVL_NOWHERE,
ENVL_EXT4,
ENVL_FAT,
};
enum env_location env_get_location(enum env_operation op, int prio)

View File

@ -276,6 +276,19 @@ config SPL_SEPARATE_BSS
location is used. Normally we put the device tree at the end of BSS
but with this option enabled, it goes at _image_binary_end.
config SPL_READ_ONLY
bool
depends on SPL_OF_PLATDATA
# Bind cannot be supported because the udevice structs are in read-only
# memory so we cannot update the linked lists.
select SPL_OF_PLATDATA_NO_BIND
select SPL_OF_PLATDATA_RT
help
Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only
section of memory. This means that of-platdata must make a copy (in
writeable memory) of anything it wants to modify, such as
device-private data.
config SPL_BANNER_PRINT
bool "Enable output of the SPL banner 'U-Boot SPL ...'"
default y
@ -1440,6 +1453,17 @@ config TPL_STACK
The address of the initial stack-pointer for the TPL stage.
Usually this will be the (aligned) top-of-stack.
config TPL_READ_ONLY
bool
depends on TPL_OF_PLATDATA
select TPL_OF_PLATDATA_NO_BIND
select TPL_OF_PLATDATA_RT
help
Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only
section of memory. This means that of-platdata must make a copy (in
writeable memory) of anything it wants to modify, such as
device-private data.
config TPL_BOOTROM_SUPPORT
bool "Support returning to the BOOTROM (from TPL)"
help

View File

@ -73,6 +73,7 @@ CONFIG_MAC_PARTITION=y
CONFIG_ISO_PARTITION=y
CONFIG_EFI_PARTITION=y
# CONFIG_SPL_EFI_PARTITION is not set
CONFIG_OF_SPL_REMOVE_PROPS="clocks clock-names interrupt-parent interrupts linux-name acpi,name acpi,path u-boot,acpi-dsdt-order u-boot,acpi-ssdt-order"
CONFIG_ENV_OVERWRITE=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y

View File

@ -0,0 +1,231 @@
CONFIG_SYS_TEXT_BASE=0x200000
CONFIG_SPL_LIBCOMMON_SUPPORT=y
CONFIG_SPL_LIBGENERIC_SUPPORT=y
CONFIG_NR_DRAM_BANKS=1
CONFIG_SYS_MEMTEST_START=0x00100000
CONFIG_SYS_MEMTEST_END=0x00101000
CONFIG_ENV_SIZE=0x2000
CONFIG_SPL_SERIAL_SUPPORT=y
CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
CONFIG_SPL_SYS_MALLOC_F_LEN=0x8000
CONFIG_SPL=y
CONFIG_BOOTSTAGE_STASH_ADDR=0x0
CONFIG_DEFAULT_DEVICE_TREE="sandbox"
CONFIG_SANDBOX_SPL=y
CONFIG_DEBUG_UART=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_FIT=y
CONFIG_FIT_SIGNATURE=y
CONFIG_FIT_VERBOSE=y
CONFIG_SPL_LOAD_FIT=y
# CONFIG_USE_SPL_FIT_GENERATOR is not set
CONFIG_BOOTSTAGE=y
CONFIG_BOOTSTAGE_REPORT=y
CONFIG_BOOTSTAGE_FDT=y
CONFIG_BOOTSTAGE_STASH=y
CONFIG_BOOTSTAGE_STASH_SIZE=0x4096
CONFIG_CONSOLE_RECORD=y
CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000
CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_HANDOFF=y
CONFIG_SPL_BOARD_INIT=y
CONFIG_SPL_ENV_SUPPORT=y
CONFIG_SPL_I2C_SUPPORT=y
CONFIG_SPL_RTC_SUPPORT=y
CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y
CONFIG_CMD_BOOTEFI_HELLO=y
# CONFIG_CMD_ELF is not set
CONFIG_CMD_ASKENV=y
CONFIG_CMD_GREPENV=y
CONFIG_CMD_ERASEENV=y
CONFIG_CMD_ENV_CALLBACK=y
CONFIG_CMD_ENV_FLAGS=y
CONFIG_CMD_NVEDIT_INFO=y
CONFIG_CMD_NVEDIT_LOAD=y
CONFIG_CMD_NVEDIT_SELECT=y
CONFIG_LOOPW=y
CONFIG_CMD_MD5SUM=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_MX_CYCLIC=y
CONFIG_CMD_MEMTEST=y
CONFIG_CMD_DEMO=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_GPT=y
CONFIG_CMD_IDE=y
CONFIG_CMD_I2C=y
CONFIG_CMD_OSD=y
CONFIG_CMD_PCI=y
CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_BOOTP_DNS2=y
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TFTPSRV=y
CONFIG_CMD_RARP=y
CONFIG_CMD_CDP=y
CONFIG_CMD_SNTP=y
CONFIG_CMD_DNS=y
CONFIG_CMD_LINK_LOCAL=y
CONFIG_CMD_BMP=y
CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y
CONFIG_CMD_QFW=y
CONFIG_CMD_BOOTSTAGE=y
CONFIG_CMD_PMIC=y
CONFIG_CMD_REGULATOR=y
CONFIG_CMD_TPM=y
CONFIG_CMD_TPM_TEST=y
CONFIG_CMD_CBFS=y
CONFIG_CMD_CRAMFS=y
CONFIG_CMD_EXT4_WRITE=y
CONFIG_MAC_PARTITION=y
CONFIG_AMIGA_PARTITION=y
CONFIG_OF_CONTROL=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_HOSTFILE=y
CONFIG_SPL_OF_PLATDATA=y
CONFIG_ENV_IS_NOWHERE=y
CONFIG_ENV_IS_IN_EXT4=y
CONFIG_ENV_EXT4_INTERFACE="host"
CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
CONFIG_BOOTP_SEND_HOSTNAME=y
CONFIG_NETCONSOLE=y
CONFIG_IP_DEFRAG=y
CONFIG_SPL_DM=y
CONFIG_DM_DMA=y
CONFIG_REGMAP=y
CONFIG_SPL_REGMAP=y
CONFIG_SYSCON=y
CONFIG_SPL_SYSCON=y
CONFIG_DEVRES=y
CONFIG_DEBUG_DEVRES=y
# CONFIG_SPL_SIMPLE_BUS is not set
CONFIG_ADC=y
CONFIG_ADC_SANDBOX=y
CONFIG_AXI=y
CONFIG_AXI_SANDBOX=y
CONFIG_CLK=y
CONFIG_SPL_CLK=y
CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_SPL_FIRMWARE=y
CONFIG_GPIO_HOG=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_I2C_CROS_EC_TUNNEL=y
CONFIG_I2C_CROS_EC_LDO=y
CONFIG_DM_I2C_GPIO=y
CONFIG_SYS_I2C_SANDBOX=y
CONFIG_I2C_MUX=y
CONFIG_I2C_ARB_GPIO_CHALLENGE=y
CONFIG_CROS_EC_KEYB=y
CONFIG_I8042_KEYB=y
CONFIG_LED=y
CONFIG_LED_BLINK=y
CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
CONFIG_CROS_EC_SANDBOX=y
CONFIG_CROS_EC_SPI=y
CONFIG_IRQ=y
CONFIG_P2SB=y
CONFIG_PWRSEQ=y
CONFIG_SPL_PWRSEQ=y
CONFIG_MMC_SANDBOX=y
CONFIG_SPI_FLASH_SANDBOX=y
CONFIG_SPI_FLASH_ATMEL=y
CONFIG_SPI_FLASH_EON=y
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_SST=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_DM_ETH=y
CONFIG_NVME=y
CONFIG_PCI=y
CONFIG_DM_PCI=y
CONFIG_DM_PCI_COMPAT=y
CONFIG_PCI_SANDBOX=y
CONFIG_PHY=y
CONFIG_PHY_SANDBOX=y
CONFIG_PINCTRL=y
CONFIG_PINCONF=y
CONFIG_PINCTRL_SANDBOX=y
CONFIG_DM_PMIC=y
CONFIG_PMIC_ACT8846=y
CONFIG_DM_PMIC_PFUZE100=y
CONFIG_DM_PMIC_MAX77686=y
CONFIG_DM_PMIC_MC34708=y
CONFIG_PMIC_PM8916=y
CONFIG_PMIC_RK8XX=y
CONFIG_PMIC_S2MPS11=y
CONFIG_DM_PMIC_SANDBOX=y
CONFIG_PMIC_S5M8767=y
CONFIG_PMIC_TPS65090=y
CONFIG_DM_REGULATOR=y
CONFIG_REGULATOR_ACT8846=y
CONFIG_DM_REGULATOR_PFUZE100=y
CONFIG_DM_REGULATOR_MAX77686=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_REGULATOR_RK8XX=y
CONFIG_REGULATOR_S5M8767=y
CONFIG_DM_REGULATOR_SANDBOX=y
CONFIG_REGULATOR_TPS65090=y
CONFIG_DM_PWM=y
CONFIG_PWM_SANDBOX=y
CONFIG_RAM=y
CONFIG_REMOTEPROC_SANDBOX=y
CONFIG_DM_RESET=y
CONFIG_SANDBOX_RESET=y
CONFIG_DM_RTC=y
CONFIG_SPL_DM_RTC=y
CONFIG_SANDBOX_SERIAL=y
CONFIG_SOUND=y
CONFIG_SOUND_SANDBOX=y
CONFIG_SOC_DEVICE=y
CONFIG_SANDBOX_SPI=y
CONFIG_SPMI=y
CONFIG_SPMI_SANDBOX=y
CONFIG_SYSINFO=y
CONFIG_SYSINFO_SANDBOX=y
CONFIG_SYSRESET=y
CONFIG_SPL_SYSRESET=y
CONFIG_TIMER=y
CONFIG_TIMER_EARLY=y
CONFIG_SANDBOX_TIMER=y
CONFIG_USB=y
CONFIG_DM_USB=y
CONFIG_USB_EMUL=y
CONFIG_USB_KEYBOARD=y
CONFIG_DM_VIDEO=y
CONFIG_CONSOLE_ROTATION=y
CONFIG_CONSOLE_TRUETYPE=y
CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y
CONFIG_VIDEO_SANDBOX_SDL=y
CONFIG_OSD=y
CONFIG_SANDBOX_OSD=y
CONFIG_SPLASH_SCREEN_ALIGN=y
CONFIG_VIDEO_BMP_RLE8=y
CONFIG_FS_CBFS=y
CONFIG_FS_CRAMFS=y
# CONFIG_SPL_USE_TINY_PRINTF is not set
CONFIG_CMD_DHRYSTONE=y
CONFIG_RSA_VERIFY_WITH_PKEY=y
CONFIG_TPM=y
CONFIG_LZ4=y
CONFIG_ERRNO_STR=y
CONFIG_UNIT_TEST=y
CONFIG_SPL_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y

View File

@ -7,6 +7,7 @@ CONFIG_SYS_MEMTEST_END=0x00101000
CONFIG_ENV_SIZE=0x2000
CONFIG_SPL_SERIAL_SUPPORT=y
CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
CONFIG_SPL_SYS_MALLOC_F_LEN=0x8000
CONFIG_SPL=y
CONFIG_BOOTSTAGE_STASH_ADDR=0x0
CONFIG_DEFAULT_DEVICE_TREE="sandbox"
@ -87,6 +88,7 @@ CONFIG_OF_CONTROL=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_HOSTFILE=y
CONFIG_SPL_OF_PLATDATA=y
CONFIG_SPL_OF_PLATDATA_INST=y
CONFIG_ENV_IS_NOWHERE=y
CONFIG_ENV_IS_IN_EXT4=y
CONFIG_ENV_EXT4_INTERFACE="host"
@ -102,6 +104,7 @@ CONFIG_SYSCON=y
CONFIG_SPL_SYSCON=y
CONFIG_DEVRES=y
CONFIG_DEBUG_DEVRES=y
# CONFIG_SPL_SIMPLE_BUS is not set
CONFIG_ADC=y
CONFIG_ADC_SANDBOX=y
CONFIG_AXI=y

View File

@ -709,8 +709,8 @@ to load a 'u-boot-payload.efi', see below test logs on QEMU.
No controllers found
Hit any key to stop autoboot: 0
See :doc:`../uefi/u-boot_on_efi` and :doc:`../uefi/uefi` for details of
EFI support in U-Boot.
See :doc:`../develop/uefi/u-boot_on_efi` and :doc:`../develop/uefi/uefi` for
details of EFI support in U-Boot.
Chain-loading
-------------

View File

@ -3,6 +3,10 @@
Driver Model
============
The following holds information on the U-Boot device driver framework:
driver-model, including the design details of itself and several driver
subsystems
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,913 @@
.. SPDX-License-Identifier: GPL-2.0+
Compiled-in Device Tree / Platform Data
=======================================
Introduction
------------
Device tree is the standard configuration method in U-Boot. It is used to
define what devices are in the system and provide configuration information
to these devices.
The overhead of adding devicetree access to U-Boot is fairly modest,
approximately 3KB on Thumb 2 (plus the size of the DT itself). This means
that in most cases it is best to use devicetree for configuration.
However there are some very constrained environments where U-Boot needs to
work. These include SPL with severe memory limitations. For example, some
SoCs require a 16KB SPL image which must include a full MMC stack. In this
case the overhead of devicetree access may be too great.
It is possible to create platform data manually by defining C structures
for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This
bypasses the use of devicetree completely, effectively creating a parallel
configuration mechanism. But it is an available option for SPL.
As an alternative, the 'of-platdata' feature is provided. This converts the
devicetree contents into C code which can be compiled into the SPL binary.
This saves the 3KB of code overhead and perhaps a few hundred more bytes due
to more efficient storage of the data.
How it works
------------
The feature is enabled by CONFIG OF_PLATDATA. This is only available in
SPL/TPL and should be tested with:
.. code-block:: c
#if CONFIG_IS_ENABLED(OF_PLATDATA)
A tool called 'dtoc' converts a devicetree file either into a set of
struct declarations, one for each compatible node, and a set of
`U_BOOT_DRVINFO()` declarations along with the actual platform data for each
device. As an example, consider this MMC node:
.. code-block:: none
sdmmc: dwmmc@ff0c0000 {
compatible = "rockchip,rk3288-dw-mshc";
clock-freq-min-max = <400000 150000000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
fifo-depth = <0x100>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
reg = <0xff0c0000 0x4000>;
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
card-detect-delay = <200>;
disable-wp;
num-slots = <1>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
vmmc-supply = <&vcc_sd>;
status = "okay";
u-boot,dm-pre-reloc;
};
Some of these properties are dropped by U-Boot under control of the
CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce
the following C struct declaration:
.. code-block:: c
struct dtd_rockchip_rk3288_dw_mshc {
fdt32_t bus_width;
bool cap_mmc_highspeed;
bool cap_sd_highspeed;
fdt32_t card_detect_delay;
fdt32_t clock_freq_min_max[2];
struct phandle_1_arg clocks[4];
bool disable_wp;
fdt32_t fifo_depth;
fdt32_t interrupts[3];
fdt32_t num_slots;
fdt32_t reg[2];
fdt32_t vmmc_supply;
};
and the following device declarations:
.. code-block:: c
/* Node /clock-controller@ff760000 index 0 */
...
/* Node /dwmmc@ff0c0000 index 2 */
static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
.fifo_depth = 0x100,
.cap_sd_highspeed = true,
.interrupts = {0x0, 0x20, 0x4},
.clock_freq_min_max = {0x61a80, 0x8f0d180},
.vmmc_supply = 0xb,
.num_slots = 0x1,
.clocks = {{0, 456},
{0, 68},
{0, 114},
{0, 118}},
.cap_mmc_highspeed = true,
.disable_wp = true,
.bus_width = 0x4,
.u_boot_dm_pre_reloc = true,
.reg = {0xff0c0000, 0x4000},
.card_detect_delay = 0xc8,
};
U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = {
.name = "rockchip_rk3288_dw_mshc",
.plat = &dtv_dwmmc_at_ff0c0000,
.plat_size = sizeof(dtv_dwmmc_at_ff0c0000),
.parent_idx = -1,
};
The device is then instantiated at run-time and the platform data can be
accessed using:
.. code-block:: c
struct udevice *dev;
struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev);
This avoids the code overhead of converting the devicetree data to
platform data in the driver. The `of_to_plat()` method should
therefore do nothing in such a driver.
Note that for the platform data to be matched with a driver, the 'name'
property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared
via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a
'name' corresponding to the devicetree 'compatible' string (after converting
it to a valid name for C) is needed, so a dedicated driver is required for
each 'compatible' string.
In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be
used to declare an alias for a driver name, typically a 'compatible' string.
This macro produces no code, but is used by dtoc tool. It must be located in the
same file as its associated driver, ideally just after it.
The parent_idx is the index of the parent `driver_info` structure within its
linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to
support `dev_get_parent()`.
During the build process dtoc parses both `U_BOOT_DRIVER()` and
`DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases.
If the 'compatible' string used for a device does not not match a valid driver
name, it will be checked against the list of driver aliases in order to get the
right driver name to use. If in this step there is no match found a warning is
issued to avoid run-time failures.
Where a node has multiple compatible strings, dtoc generates a `#define` to
make them equivalent, e.g.:
.. code-block:: c
#define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc
Converting of-platdata to a useful form
---------------------------------------
Of course it would be possible to use the of-platdata directly in your driver
whenever configuration information is required. However this means that the
driver will not be able to support devicetree, since the of-platdata
structure is not available when devicetree is used. It would make no sense
to use this structure if devicetree were available, since the structure has
all the limitations metioned in caveats below.
Therefore it is recommended that the of-platdata structure should be used
only in the `probe()` method of your driver. It cannot be used in the
`of_to_plat()` method since this is not called when platform data is
already present.
How to structure your driver
----------------------------
Drivers should always support devicetree as an option. The of-platdata
feature is intended as a add-on to existing drivers.
Your driver should convert the plat struct in its `probe()` method. The
existing devicetree decoding logic should be kept in the
`of_to_plat()` method and wrapped with `#if`.
For example:
.. code-block:: c
#include <dt-structs.h>
struct mmc_plat {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
/* Put this first since driver model will copy the data here */
struct dtd_mmc dtplat;
#endif
/*
* Other fields can go here, to be filled in by decoding from
* the devicetree (or the C structures when of-platdata is used).
*/
int fifo_depth;
};
static int mmc_of_to_plat(struct udevice *dev)
{
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
/* Decode the devicetree data */
struct mmc_plat *plat = dev_get_plat(dev);
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0);
#endif
return 0;
}
static int mmc_probe(struct udevice *dev)
{
struct mmc_plat *plat = dev_get_plat(dev);
#if CONFIG_IS_ENABLED(OF_PLATDATA)
/* Decode the of-platdata from the C structures */
struct dtd_mmc *dtplat = &plat->dtplat;
plat->fifo_depth = dtplat->fifo_depth;
#endif
/* Set up the device from the plat data */
writel(plat->fifo_depth, ...)
}
static const struct udevice_id mmc_ids[] = {
{ .compatible = "vendor,mmc" },
{ }
};
U_BOOT_DRIVER(mmc_drv) = {
.name = "mmc_drv",
.id = UCLASS_MMC,
.of_match = mmc_ids,
.of_to_plat = mmc_of_to_plat,
.probe = mmc_probe,
.priv_auto = sizeof(struct mmc_priv),
.plat_auto = sizeof(struct mmc_plat),
};
DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */
Note that `struct mmc_plat` is defined in the C file, not in a header. This
is to avoid needing to include dt-structs.h in a header file. The idea is to
keep the use of each of-platdata struct to the smallest possible code area.
There is just one driver C file for each struct, that can convert from the
of-platdata struct to the standard one used by the driver.
In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is
still used to allocate space for the platform data. This is different from
the normal behaviour and is triggered by the use of of-platdata (strictly
speaking it is a non-zero `plat_size` which triggers this).
The of-platdata struct contents is copied from the C structure data to the
start of the newly allocated area. In the case where devicetree is used,
the platform data is allocated, and starts zeroed. In this case the
`of_to_plat()` method should still set up the platform data (and the
of-platdata struct will not be present).
SPL must use either of-platdata or devicetree. Drivers cannot use both at
the same time, but they must support devicetree. Supporting of-platdata is
optional.
The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled,
since the devicetree access code is not compiled in. A corollary is that
a board can only move to using of-platdata if all the drivers it uses support
it. There would be little point in having some drivers require the device
tree data, since then libfdt would still be needed for those drivers and
there would be no code-size benefit.
Build-time instantiation
------------------------
Even with of-platdata there is a fair amount of code required in driver model.
It is possible to have U-Boot handle the instantiation of devices at build-time,
so avoiding the need for the `device_bind()` code and some parts of
`device_probe()`.
The feature is enabled by CONFIG_OF_PLATDATA_INST.
Here is an example device, as generated by dtoc::
/*
* Node /serial index 6
* driver sandbox_serial parent root_driver
*/
#include <asm/serial.h>
struct sandbox_serial_plat __attribute__ ((section (".priv_data")))
_sandbox_serial_plat_serial = {
.dtplat = {
.sandbox_text_colour = "cyan",
},
};
#include <asm/serial.h>
u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)]
__attribute__ ((section (".priv_data")));
#include <serial.h>
u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)]
__attribute__ ((section (".priv_data")));
DM_DEVICE_INST(serial) = {
.driver = DM_DRIVER_REF(sandbox_serial),
.name = "sandbox_serial",
.plat_ = &_sandbox_serial_plat_serial,
.priv_ = _sandbox_serial_priv_serial,
.uclass = DM_UCLASS_REF(serial),
.uclass_priv_ = _sandbox_serial_uc_priv_serial,
.uclass_node = {
.prev = &DM_UCLASS_REF(serial)->dev_head,
.next = &DM_UCLASS_REF(serial)->dev_head,
},
.child_head = {
.prev = &DM_DEVICE_REF(serial)->child_head,
.next = &DM_DEVICE_REF(serial)->child_head,
},
.sibling_node = {
.prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node,
.next = &DM_DEVICE_REF(spl_test)->sibling_node,
},
.seq_ = 0,
};
Here is part of the driver, for reference::
static const struct udevice_id sandbox_serial_ids[] = {
{ .compatible = "sandbox,serial" },
{ }
};
U_BOOT_DRIVER(sandbox_serial) = {
.name = "sandbox_serial",
.id = UCLASS_SERIAL,
.of_match = sandbox_serial_ids,
.of_to_plat = sandbox_serial_of_to_plat,
.plat_auto = sizeof(struct sandbox_serial_plat),
.priv_auto = sizeof(struct sandbox_serial_priv),
.probe = sandbox_serial_probe,
.remove = sandbox_serial_remove,
.ops = &sandbox_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the
members are from that struct. The private data is declared immediately above,
as `_sandbox_serial_priv_serial`, so there is no need for run-time memory
allocation. The #include lines are generated as well, since dtoc searches the
U-Boot source code for the definition of `struct sandbox_serial_priv` and adds
the relevant header so that the code will compile without errors.
The `plat_` member is set to the dtv data which is declared immediately above
the device. This is similar to how it would look without of-platdata-inst, but
node that the `dtplat` member inside is part of the wider
`_sandbox_serial_plat_serial` struct. This is because the driver declares its
own platform data, and the part generated by dtoc can only be a portion of it.
The `dtplat` part is always first in the struct. If the device has no
`.plat_auto` field, then a simple dtv struct can be used as with this example::
static struct dtd_sandbox_clk dtv_clk_sbox = {
.assigned_clock_rates = 0x141,
.assigned_clocks = {0x7, 0x3},
};
#include <asm/clk.h>
u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)]
__attribute__ ((section (".priv_data")));
DM_DEVICE_INST(clk_sbox) = {
.driver = DM_DRIVER_REF(sandbox_clk),
.name = "sandbox_clk",
.plat_ = &dtv_clk_sbox,
Here is part of the driver, for reference::
static const struct udevice_id sandbox_clk_ids[] = {
{ .compatible = "sandbox,clk" },
{ }
};
U_BOOT_DRIVER(sandbox_clk) = {
.name = "sandbox_clk",
.id = UCLASS_CLK,
.of_match = sandbox_clk_ids,
.ops = &sandbox_clk_ops,
.probe = sandbox_clk_probe,
.priv_auto = sizeof(struct sandbox_clk_priv),
};
You can see that `dtv_clk_sbox` just has the devicetree contents and there is
no need for the `dtplat` separation, since the driver has no platform data of
its own, besides that provided by the devicetree (i.e. no `.plat_auto` field).
The doubly linked lists are handled by explicitly declaring the value of each
node, as you can see with the `.prev` and `.next` values in the example above.
Since dtoc knows the order of devices it can link them into the appropriate
lists correctly.
One of the features of driver model is the ability for a uclass to have a
small amount of private data for each device in that uclass. This is used to
provide a generic data structure that the uclass can use for all devices, thus
allowing generic features to be implemented in common code. An example is I2C,
which stores the bus speed there.
Similarly, parent devices can have data associated with each of their children.
This is used to provide information common to all children of a particular bus.
For an I2C bus, this is used to store the I2C address of each child on the bus.
This is all handled automatically by dtoc::
#include <asm/i2c.h>
u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)]
__attribute__ ((section (".priv_data")));
#include <i2c.h>
u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)]
__attribute__ ((section (".priv_data")));
DM_DEVICE_INST(i2c_at_0) = {
.driver = DM_DRIVER_REF(sandbox_i2c),
.name = "sandbox_i2c",
.plat_ = &dtv_i2c_at_0,
.priv_ = _sandbox_i2c_priv_i2c_at_0,
.uclass = DM_UCLASS_REF(i2c),
.uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0,
...
Part of driver, for reference::
static const struct udevice_id sandbox_i2c_ids[] = {
{ .compatible = "sandbox,i2c" },
{ }
};
U_BOOT_DRIVER(sandbox_i2c) = {
.name = "sandbox_i2c",
.id = UCLASS_I2C,
.of_match = sandbox_i2c_ids,
.ops = &sandbox_i2c_ops,
.priv_auto = sizeof(struct sandbox_i2c_priv),
};
Part of I2C uclass, for reference::
UCLASS_DRIVER(i2c) = {
.id = UCLASS_I2C,
.name = "i2c",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = i2c_post_bind,
.pre_probe = i2c_pre_probe,
.post_probe = i2c_post_probe,
.per_device_auto = sizeof(struct dm_i2c_bus),
.per_child_plat_auto = sizeof(struct dm_i2c_chip),
.child_post_bind = i2c_child_post_bind,
};
Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared
in the device, as required by driver model. The required header file is included
so that the code will compile without errors. A similar mechanism is used for
child devices, but is not shown by this example.
It would not be that useful to avoid binding devices but still need to allocate
uclasses at runtime. So dtoc generates uclass instances as well::
struct list_head uclass_head = {
.prev = &DM_UCLASS_REF(serial)->sibling_node,
.next = &DM_UCLASS_REF(clk)->sibling_node,
};
DM_UCLASS_INST(clk) = {
.uc_drv = DM_UCLASS_DRIVER_REF(clk),
.sibling_node = {
.prev = &uclass_head,
.next = &DM_UCLASS_REF(i2c)->sibling_node,
},
.dev_head = {
.prev = &DM_DEVICE_REF(clk_sbox)->uclass_node,
.next = &DM_DEVICE_REF(clk_fixed)->uclass_node,
},
};
At the top is the list head. Driver model uses this on start-up, instead of
creating its own.
Below that are a set of `DM_UCLASS_INST()` macros, each declaring a
`struct uclass`. The doubly linked lists work as for devices.
All private data is placed into a `.priv_data` section so that it is contiguous
in the resulting output binary.
Indexes
-------
U-Boot stores drivers, devices and many other things in linker_list structures.
These are sorted by name, so dtoc knows the order that they will appear when
the linker runs. Each driver_info / udevice is referenced by its index in the
linker_list array, called 'idx' in the code.
When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it
is the driver_info index. In either case, indexes are used to reference devices
using device_get_by_ofplat_idx(). This allows phandles to work as expected.
Phases
------
U-Boot operates in several phases, typically TPL, SPL and U-Boot proper.
The latter does not use dtoc.
In some rare cases different drivers are used for two phases. For example,
in TPL it may not be necessary to use the full PCI subsystem, so a simple
driver can be used instead.
This works in the build system simply by compiling in one driver or the
other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has
no way of knowing which code is compiled in for which phase, since it does
not inspect Makefiles or dependency graphs.
So to make this work for dtoc, we need to be able to explicitly mark
drivers with their phase. This is done by adding a macro to the driver::
/* code in tpl.c only compiled into TPL */
U_BOOT_DRIVER(pci_x86) = {
.name = "pci_x86",
.id = UCLASS_SIMPLE_BUS,
.of_match = of_match_ptr(tpl_fake_pci_ids),
DM_PHASE(tpl)
};
/* code in pci_x86.c compiled into SPL and U-Boot proper */
U_BOOT_DRIVER(pci_x86) = {
.name = "pci_x86",
.id = UCLASS_PCI,
.of_match = pci_x86_ids,
.ops = &pci_x86_ops,
};
Notice that the second driver has the same name but no DM_PHASE(), so it will be
used for SPL and U-Boot.
Note also that this only affects the code generated by dtoc. You still need to
make sure that only the required driver is build into each phase.
Header files
------------
With OF_PLATDATA_INST, dtoc must include the correct header file in the
generated code for any structs that are used, so that the code will compile.
For example, if `struct ns16550_plat` is used, the code must include the
`ns16550.h` header file.
Typically dtoc can detect the header file needed for a driver by looking
for the structs that it uses. For example, if a driver as a `.priv_auto`
that uses `struct ns16550_plat`, then dtoc can search header files for the
definition of that struct and use the file.
In some cases, enums are used in drivers, typically with the `.data` field
of `struct udevice_id`. Since dtoc does not support searching for these,
you must use the `DM_HDR()` macro to tell dtoc which header to use. This works
as a macro included in the driver definition::
static const struct udevice_id apl_syscon_ids[] = {
{ .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT },
{ }
};
U_BOOT_DRIVER(intel_apl_punit) = {
.name = "intel_apl_punit",
.id = UCLASS_SYSCON,
.of_match = apl_syscon_ids,
.probe = apl_punit_probe,
DM_HEADER(<asm/cpu.h>) /* for X86_SYSCON_PUNIT */
};
Caveats
-------
There are various complications with this feature which mean it should only
be used when strictly necessary, i.e. in SPL with limited memory. Notable
caveats include:
- Device tree does not describe data types. But the C code must define a
type for each property. These are guessed using heuristics which
are wrong in several fairly common cases. For example an 8-byte value
is considered to be a 2-item integer array, and is byte-swapped. A
boolean value that is not present means 'false', but cannot be
included in the structures since there is generally no mention of it
in the devicetree file.
- Naming of nodes and properties is automatic. This means that they follow
the naming in the devicetree, which may result in C identifiers that
look a bit strange.
- It is not possible to find a value given a property name. Code must use
the associated C member variable directly in the code. This makes
the code less robust in the face of devicetree changes. To avoid having
a second struct with similar members and names you need to explicitly
declare it as an alias with `DM_DRIVER_ALIAS()`.
- The platform data is provided to drivers as a C structure. The driver
must use the same structure to access the data. Since a driver
normally also supports devicetree it must use `#ifdef` to separate
out this code, since the structures are only available in SPL. This could
be fixed fairly easily by making the structs available outside SPL, so
that `IS_ENABLED()` could be used.
- With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning
that (by default) it is not possible to call `device_bind()` from C code.
This means that all devices must have an associated devicetree node and
compatible string. For example if a GPIO device currently creates child
devices in its `bind()` method, it will not work with
CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the
devicetree binding should be updated to declare compatible strings for
the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this
is not recommended since it increases code size.
Internals
---------
Generated files
```````````````
When enabled, dtoc generates the following five files:
include/generated/dt-decl.h (OF_PLATDATA_INST only)
Contains declarations for all drivers, devices and uclasses. This allows
any `struct udevice`, `struct driver` or `struct uclass` to be located by its
name
include/generated/dt-structs-gen.h
Contains the struct definitions for the devicetree nodes that are used. This
is the same as without OF_PLATDATA_INST
spl/dts/dt-plat.c (only with !OF_PLATDATA_INST)
Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices
at start-up. See above for an example
spl/dts/dt-device.c (only with OF_PLATDATA_INST)
Contains `DM_DEVICE_INST()` declarations for each device that can be used at
run-time. These are declared in the file along with any private/platform data
that they use. Every device has an idx, as above. Since each device must be
part of a double-linked list, the nodes are declared in the code as well.
spl/dts/dt-uclass.c (only with OF_PLATDATA_INST)
Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at
run-time. These are declared in the file along with any private data
associated with the uclass itself (the `.priv_auto` member). Since each
uclass must be part of a double-linked list, the nodes are declared in the
code as well.
The dt-structs.h file includes the generated file
`(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled.
Otherwise (such as in U-Boot proper) these structs are not available. This
prevents them being used inadvertently. All usage must be bracketed with
`#if CONFIG_IS_ENABLED(OF_PLATDATA)`.
The dt-plat.c file contains the device declarations and is is built in
spl/dt-plat.c.
CONFIG options
``````````````
Several CONFIG options are used to control the behaviour of of-platdata, all
available for both SPL and TPL:
OF_PLATDATA
This is the main option which enables the of-platdata feature
OF_PLATDATA_PARENT
This allows `device_get_parent()` to work. Without this, all devices exist as
direct children of the root node. This option is highly desirable (if not
always absolutely essential) for buses such as I2C.
OF_PLATDATA_INST
This controls the instantiation of devices at build time. With it disabled,
only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding
in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and
`DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at
runtime.
OF_PLATDATA_NO_BIND
This controls whether `device_bind()` is supported. It is enabled by default
with OF_PLATDATA_INST since code-size reduction is really the main point of
the feature. It can be disabled if needed but is not likely to be supported
in the long term.
OF_PLATDATA_DRIVER_RT
This controls whether the `struct driver_rt` records are used by U-Boot.
Normally when a device is bound, U-Boot stores the device pointer in one of
these records. There is one for every `struct driver_info` in the system,
i.e. one for every device that is bound from those records. It provides a
way to locate a device in the code and is used by
`device_get_by_ofplat_idx()`. This option is always enabled with of-platdata,
provided OF_PLATDATA_INST is not. In that case the records are useless since
we don't have any `struct driver_info` records.
OF_PLATDATA_RT
This controls whether the `struct udevice_rt` records are used by U-Boot.
It moves the updatable fields from `struct udevice` (currently only `flags`)
into a separate structure, allowing the records to be kept in read-only
memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option
also controls whether the private data is used in situ, or first copied into
an allocated region. Again this is to allow the private data declared by
dtoc-generated code to be in read-only memory. Note that access to private
data must be done via accessor functions, such as `dev_get_priv()`, so that
the relocation is handled.
READ_ONLY
This indicates that the data generated by dtoc should not be modified. Only
a few fields actually do get changed in U-Boot, such as device flags. This
option causes those to move into an allocated space (see OF_PLATDATA_RT).
Also, since updating doubly linked lists is generally impossible when some of
the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled.
Data structures
```````````````
A few extra data structures are used with of-platdata:
`struct udevice_rt`
Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds
the flags for each device, so that `struct udevice` can remain unchanged by
U-Boot, and potentially reside in read-only memory. Access to flags is then
via functions like `dev_get_flags()` and `dev_or_flags()`. This data
structure is allocated on start-up, where the private data is also copied.
All flags values start at 0 and any changes are handled by `dev_or_flags()`
and `dev_bic_flags()`. It would be more correct for the flags to be set to
`DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since
there is no code to bind/unbind devices and no code to allocate/free
private data / platform data, it doesn't matter.
`struct driver_rt`
Run-time information for `struct driver_info` records. When
OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device
created by each record. This is needed so that is it possible to locate a
device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to
get a reference to a particular `struct driver_info`, with `name` being the
name of the devicetree node. This is very convenient. It is also fast, since
no searching or string comparison is needed. This data structure is
allocated on start-up, filled out by `device_bind()` and used by
`device_get_by_ofplat_idx()`.
Other changes
`````````````
Some other changes are made with of-platdata:
Accessor functions
Accessing private / platform data via functions such as `dev_get_priv()` has
always been encouraged. With OF_PLATDATA_RT this is essential, since the
`priv_` and `plat_` (etc.) values point to the data generated by dtoc, not
the read-write copy that is sometimes made on start-up. Changing the
private / platform data pointers has always been discouraged (the API is
marked internal) but with OF_PLATDATA_RT this is not currently supported in
general, since it assumes that all such pointers point to the relocated data.
Note also that the renaming of struct members to have a trailing underscore
was partly done to make people aware that they should not be accessed
directly.
`gd->uclass_root_s`
Normally U-Boot sets up the head of the uclass list here and makes
`gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a
declaration of `uclass_head` in `dt-uclass.c` since it needs to link the
head node into the list. In that case, `gd->uclass_root_s` is not used and
U-Boot just makes `gd->uclass_root` point to `uclass_head`.
`gd->dm_driver_rt`
This holds a pointer to a list of `struct driver_rt` records, one for each
`struct driver_info`. The list is in alphabetical order by the name used
in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having
an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is
accessed via macros so that it can be used inside IS_ENABLED(), rather than
requiring #ifdefs in the C code when it is not present.
`gd->dm_udevice_rt`
This holds a pointer to a list of `struct udevice_rt` records, one for each
`struct udevice`. The list is in alphabetical order by the name used
in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by
idx, with the first record having an index of 0. It is only used if
OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be
used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when
it is not present.
`gd->dm_priv_base`
When OF_PLATDATA_RT is enabled, the private/platform data for each device is
copied into an allocated region by U-Boot on start-up. This points to that
region. All calls to accessor functions (e.g. `dev_get_priv()`) then
translate from the pointer provided by the caller (assumed to lie between
`__priv_data_start` and `__priv_data_end`) to the new allocated region. This
member is accessed via macros so that it can be used inside IS_ENABLED(),
rather than required #ifdefs in the C code when it is not present.
`struct udevice->flags_`
When OF_PLATDATA_RT is enabled, device flags are no-longer part of
`struct udevice`, but are instead kept in `struct udevice_rt`, as described
above. Flags are accessed via functions, such as `dev_get_flags()` and
`dev_or_flags()`.
`struct udevice->node_`
When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need
for this field. It is removed, just to save space.
`DM_PHASE`
This macro is used to indicate which phase of U-Boot a driver is intended
for. See above for details.
`DM_HDR`
This macro is used to indicate which header file dtoc should use to allow
a driver declaration to compile correctly. See above for details.
`device_get_by_ofplat_idx()`
There used to be a function called `device_get_by_driver_info()` which
looked up a `struct driver_info` pointer and returned the `struct udevice`
that was created from it. It was only available for use with of-platdata.
This has been removed in favour of `device_get_by_ofplat_idx()` which uses
`idx`, the index of the `struct driver_info` or `struct udevice` in the
linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been
updated to use this index instead of a pointer to `struct driver_info`.
`DM_DRVINFO_GET`
This has been removed since we now use indexes to obtain a driver from
`struct phandle_0_arg` and the like.
Two-pass binding
The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated
files so as to have parents declared ahead of children. This was convenient
as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does
not work as the idx value relies on using alphabetical order for everything,
so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are
then bound in order of idx, having no regard to parent/child relationships.
For this reason, device binding now hapens in multiple passes, with parents
being bound before their children. This is important so that children can
find their parents in the bind() method if needed.
Root device
The root device is generally bound by U-Boot but with OF_PLATDATA_INST it
cannot be, since binding needs to be done at build time. So in this case
dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and
U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot
generally ignores the root node and does not create a `U_BOOT_DRVINFO()`
record for it. This means that the idx numbers used by `struct driver_info`
(when OF_PLATDATA_INST is disabled) and the idx numbers used by
`struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a
root node and the other does not. This does not actually matter, since only
one of them is actually used for any particular build, but it is worth
keeping in mind if comparing index values and switching OF_PLATDATA_INST on
and off.
`__priv_data_start` and `__priv_data_end`
The private/platform data declared by dtoc is all collected together in
a linker section and these symbols mark the start and end of it. This allows
U-Boot to relocate the area to a new location if needed (with
OF_PLATDATA_RT)
`dm_priv_to_rw()`
This function converts a private- or platform-data pointer value generated by
dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT
is enabled, in which case it translates the address to the relocated
region. See above for more information.
The dm_populate_phandle_data() function that was previous needed has now been
removed, since dtoc can address the drivers directly from dt-plat.c and does
not need to fix up things at runtime.
The pylibfdt Python module is used to access the devicetree.
Credits
-------
This is an implementation of an idea by Tom Rini <trini@konsulko.com>.
Future work
-----------
- Consider programmatically reading binding files instead of devicetree
contents
- Allow IS_ENABLED() to be used in the C code instead of #if
.. Simon Glass <sjg@chromium.org>
.. Google, Inc
.. 6/6/16
.. Updated Independence Day 2016
.. Updated 1st October 2020
.. Updated 5th February 2021

View File

@ -125,6 +125,7 @@ emulator driver. For example::
compatible = "sandbox,pci-emul-parent";
emul_1f: emul@1f,0 {
compatible = "sandbox,swap-case";
#emul-cells = <0>;
};
};

View File

@ -10,9 +10,11 @@ Implementation
:maxdepth: 1
commands
driver-model/index
global_data
logging
menus
uefi/index
version
Debugging
@ -24,6 +26,14 @@ Debugging
crash_dumps
trace
Packaging
---------
.. toctree::
:maxdepth: 1
package/index
Testing
-------

View File

@ -0,0 +1 @@
../../../tools/binman/binman.rst

View File

@ -0,0 +1 @@
../../../tools/binman/entries.rst

View File

@ -0,0 +1,19 @@
.. SPDX-License-Identifier: GPL-2.0+
Package U-Boot
==============
U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging
images that it it reads and boots. Documentation about FIT is available at
doc/uImage.FIT
U-Boot also provides binman for cases not covered by FIT. Examples include
initial execution (since FIT itself does not have an executable header) and
dealing with device boundaries, such as the read-only/read-write separation in
SPI flash.
.. toctree::
:maxdepth: 2
binman

View File

@ -3,6 +3,10 @@
Unified Extensible Firmware (UEFI)
==================================
U-Boot provides an implementation of the UEFI API allowing to run UEFI
compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself
can be run an UEFI payload.
.. toctree::
:maxdepth: 2

View File

@ -1,359 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0+
Compiled-in Device Tree / Platform Data
=======================================
Introduction
------------
Device tree is the standard configuration method in U-Boot. It is used to
define what devices are in the system and provide configuration information
to these devices.
The overhead of adding device tree access to U-Boot is fairly modest,
approximately 3KB on Thumb 2 (plus the size of the DT itself). This means
that in most cases it is best to use device tree for configuration.
However there are some very constrained environments where U-Boot needs to
work. These include SPL with severe memory limitations. For example, some
SoCs require a 16KB SPL image which must include a full MMC stack. In this
case the overhead of device tree access may be too great.
It is possible to create platform data manually by defining C structures
for it, and reference that data in a U_BOOT_DRVINFO() declaration. This
bypasses the use of device tree completely, effectively creating a parallel
configuration mechanism. But it is an available option for SPL.
As an alternative, a new 'of-platdata' feature is provided. This converts the
device tree contents into C code which can be compiled into the SPL binary.
This saves the 3KB of code overhead and perhaps a few hundred more bytes due
to more efficient storage of the data.
Note: Quite a bit of thought has gone into the design of this feature.
However it still has many rough edges and comments and suggestions are
strongly encouraged! Quite possibly there is a much better approach.
Caveats
-------
There are many problems with this features. It should only be used when
strictly necessary. Notable problems include:
- Device tree does not describe data types. But the C code must define a
type for each property. These are guessed using heuristics which
are wrong in several fairly common cases. For example an 8-byte value
is considered to be a 2-item integer array, and is byte-swapped. A
boolean value that is not present means 'false', but cannot be
included in the structures since there is generally no mention of it
in the device tree file.
- Naming of nodes and properties is automatic. This means that they follow
the naming in the device tree, which may result in C identifiers that
look a bit strange.
- It is not possible to find a value given a property name. Code must use
the associated C member variable directly in the code. This makes
the code less robust in the face of device-tree changes. It also
makes it very unlikely that your driver code will be useful for more
than one SoC. Even if the code is common, each SoC will end up with
a different C struct name, and a likely a different format for the
platform data.
- The platform data is provided to drivers as a C structure. The driver
must use the same structure to access the data. Since a driver
normally also supports device tree it must use #ifdef to separate
out this code, since the structures are only available in SPL.
How it works
------------
The feature is enabled by CONFIG OF_PLATDATA. This is only available in
SPL/TPL and should be tested with:
.. code-block:: c
#if CONFIG_IS_ENABLED(OF_PLATDATA)
A new tool called 'dtoc' converts a device tree file either into a set of
struct declarations, one for each compatible node, and a set of
U_BOOT_DRVINFO() declarations along with the actual platform data for each
device. As an example, consider this MMC node:
.. code-block:: none
sdmmc: dwmmc@ff0c0000 {
compatible = "rockchip,rk3288-dw-mshc";
clock-freq-min-max = <400000 150000000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
fifo-depth = <0x100>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
reg = <0xff0c0000 0x4000>;
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
card-detect-delay = <200>;
disable-wp;
num-slots = <1>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
vmmc-supply = <&vcc_sd>;
status = "okay";
u-boot,dm-pre-reloc;
};
Some of these properties are dropped by U-Boot under control of the
CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce
the following C struct declaration:
.. code-block:: c
struct dtd_rockchip_rk3288_dw_mshc {
fdt32_t bus_width;
bool cap_mmc_highspeed;
bool cap_sd_highspeed;
fdt32_t card_detect_delay;
fdt32_t clock_freq_min_max[2];
struct phandle_1_arg clocks[4];
bool disable_wp;
fdt32_t fifo_depth;
fdt32_t interrupts[3];
fdt32_t num_slots;
fdt32_t reg[2];
fdt32_t vmmc_supply;
};
and the following device declarations:
.. code-block:: c
/* Node /clock-controller@ff760000 index 0 */
...
/* Node /dwmmc@ff0c0000 index 2 */
static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
.fifo_depth = 0x100,
.cap_sd_highspeed = true,
.interrupts = {0x0, 0x20, 0x4},
.clock_freq_min_max = {0x61a80, 0x8f0d180},
.vmmc_supply = 0xb,
.num_slots = 0x1,
.clocks = {{0, 456},
{0, 68},
{0, 114},
{0, 118}},
.cap_mmc_highspeed = true,
.disable_wp = true,
.bus_width = 0x4,
.u_boot_dm_pre_reloc = true,
.reg = {0xff0c0000, 0x4000},
.card_detect_delay = 0xc8,
};
U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = {
.name = "rockchip_rk3288_dw_mshc",
.plat = &dtv_dwmmc_at_ff0c0000,
.plat_size = sizeof(dtv_dwmmc_at_ff0c0000),
.parent_idx = -1,
};
The device is then instantiated at run-time and the platform data can be
accessed using:
.. code-block:: c
struct udevice *dev;
struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev);
This avoids the code overhead of converting the device tree data to
platform data in the driver. The of_to_plat() method should
therefore do nothing in such a driver.
Note that for the platform data to be matched with a driver, the 'name'
property of the U_BOOT_DRVINFO() declaration has to match a driver declared
via U_BOOT_DRIVER(). This effectively means that a U_BOOT_DRIVER() with a
'name' corresponding to the devicetree 'compatible' string (after converting
it to a valid name for C) is needed, so a dedicated driver is required for
each 'compatible' string.
In order to make this a bit more flexible DM_DRIVER_ALIAS macro can be
used to declare an alias for a driver name, typically a 'compatible' string.
This macro produces no code, but it is by dtoc tool.
The parent_idx is the index of the parent driver_info structure within its
linker list (instantiated by the U_BOOT_DRVINFO() macro). This is used to support
dev_get_parent().
During the build process dtoc parses both U_BOOT_DRIVER and DM_DRIVER_ALIAS
to build a list of valid driver names and driver aliases. If the 'compatible'
string used for a device does not not match a valid driver name, it will be
checked against the list of driver aliases in order to get the right driver
name to use. If in this step there is no match found a warning is issued to
avoid run-time failures.
Where a node has multiple compatible strings, a #define is used to make them
equivalent, e.g.:
.. code-block:: c
#define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc
Converting of-platdata to a useful form
---------------------------------------
Of course it would be possible to use the of-platdata directly in your driver
whenever configuration information is required. However this means that the
driver will not be able to support device tree, since the of-platdata
structure is not available when device tree is used. It would make no sense
to use this structure if device tree were available, since the structure has
all the limitations metioned in caveats above.
Therefore it is recommended that the of-platdata structure should be used
only in the probe() method of your driver. It cannot be used in the
of_to_plat() method since this is not called when platform data is
already present.
How to structure your driver
----------------------------
Drivers should always support device tree as an option. The of-platdata
feature is intended as a add-on to existing drivers.
Your driver should convert the plat struct in its probe() method. The
existing device tree decoding logic should be kept in the
of_to_plat() method and wrapped with #if.
For example:
.. code-block:: c
#include <dt-structs.h>
struct mmc_plat {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
/* Put this first since driver model will copy the data here */
struct dtd_mmc dtplat;
#endif
/*
* Other fields can go here, to be filled in by decoding from
* the device tree (or the C structures when of-platdata is used).
*/
int fifo_depth;
};
static int mmc_of_to_plat(struct udevice *dev)
{
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
/* Decode the device tree data */
struct mmc_plat *plat = dev_get_plat(dev);
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0);
#endif
return 0;
}
static int mmc_probe(struct udevice *dev)
{
struct mmc_plat *plat = dev_get_plat(dev);
#if CONFIG_IS_ENABLED(OF_PLATDATA)
/* Decode the of-platdata from the C structures */
struct dtd_mmc *dtplat = &plat->dtplat;
plat->fifo_depth = dtplat->fifo_depth;
#endif
/* Set up the device from the plat data */
writel(plat->fifo_depth, ...)
}
static const struct udevice_id mmc_ids[] = {
{ .compatible = "vendor,mmc" },
{ }
};
U_BOOT_DRIVER(mmc_drv) = {
.name = "mmc_drv",
.id = UCLASS_MMC,
.of_match = mmc_ids,
.of_to_plat = mmc_of_to_plat,
.probe = mmc_probe,
.priv_auto = sizeof(struct mmc_priv),
.plat_auto = sizeof(struct mmc_plat),
};
DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */
Note that struct mmc_plat is defined in the C file, not in a header. This
is to avoid needing to include dt-structs.h in a header file. The idea is to
keep the use of each of-platdata struct to the smallest possible code area.
There is just one driver C file for each struct, that can convert from the
of-platdata struct to the standard one used by the driver.
In the case where SPL_OF_PLATDATA is enabled, plat_auto is
still used to allocate space for the platform data. This is different from
the normal behaviour and is triggered by the use of of-platdata (strictly
speaking it is a non-zero plat_size which triggers this).
The of-platdata struct contents is copied from the C structure data to the
start of the newly allocated area. In the case where device tree is used,
the platform data is allocated, and starts zeroed. In this case the
of_to_plat() method should still set up the platform data (and the
of-platdata struct will not be present).
SPL must use either of-platdata or device tree. Drivers cannot use both at
the same time, but they must support device tree. Supporting of-platdata is
optional.
The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled,
since the device-tree access code is not compiled in. A corollary is that
a board can only move to using of-platdata if all the drivers it uses support
it. There would be little point in having some drivers require the device
tree data, since then libfdt would still be needed for those drivers and
there would be no code-size benefit.
Internals
---------
The dt-structs.h file includes the generated file
(include/generated//dt-structs.h) if CONFIG_SPL_OF_PLATDATA is enabled.
Otherwise (such as in U-Boot proper) these structs are not available. This
prevents them being used inadvertently. All usage must be bracketed with
#if CONFIG_IS_ENABLED(OF_PLATDATA).
The dt-plat.c file contains the device declarations and is is built in
spl/dt-plat.c.
The dm_populate_phandle_data() function that was previous needed has now been
removed, since dtoc can address the drivers directly from dt-plat.c and does
not need to fix up things at runtime.
The pylibfdt Python module is used to access the devicetree.
Credits
-------
This is an implementation of an idea by Tom Rini <trini@konsulko.com>.
Future work
-----------
- Consider programmatically reading binding files instead of device tree
contents
.. Simon Glass <sjg@chromium.org>
.. Google, Inc
.. 6/6/16
.. Updated Independence Day 2016
.. Updated 1st October 2020

View File

@ -38,29 +38,6 @@ want to contribute to U-Boot.
develop/index
Unified Extensible Firmware (UEFI)
----------------------------------
U-Boot provides an implementation of the UEFI API allowing to run UEFI
compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself
can be run an UEFI payload.
.. toctree::
:maxdepth: 2
uefi/index
Driver-Model documentation
--------------------------
The following holds information on the U-Boot device driver framework:
driver-model, including the design details of itself and several driver
subsystems.
.. toctree::
:maxdepth: 2
driver-model/index
U-Boot API documentation
------------------------

8
doc/usage/fit.rst Normal file
View File

@ -0,0 +1,8 @@
.. SPDX-License-Identifier: GPL-2.0+
Flat Image Tree (FIT)
=====================
U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging
images that it it reads and boots. Documentation about FIT is available at
doc/uImage.FIT

View File

@ -5,6 +5,7 @@ Use U-Boot
:maxdepth: 1
fdt_overlays
fit
netconsole
partitions

View File

@ -148,12 +148,6 @@ clocks_err:
return err;
}
UCLASS_DRIVER(ti_sysc) = {
.id = UCLASS_SIMPLE_BUS,
.name = "ti_sysc",
.post_bind = dm_scan_fdt_dev
};
U_BOOT_DRIVER(ti_sysc) = {
.name = "ti_sysc",
.id = UCLASS_SIMPLE_BUS,

View File

@ -39,7 +39,7 @@ int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells,
{
int ret;
ret = device_get_by_driver_info_idx(cells->idx, &clk->dev);
ret = device_get_by_ofplat_idx(cells->idx, &clk->dev);
if (ret)
return ret;
clk->id = cells->arg[0];

View File

@ -25,18 +25,24 @@ const struct clk_ops clk_fixed_rate_ops = {
.enable = dummy_enable,
};
static int clk_fixed_rate_of_to_plat(struct udevice *dev)
void clk_fixed_rate_ofdata_to_plat_(struct udevice *dev,
struct clk_fixed_rate *plat)
{
struct clk *clk = &to_clk_fixed_rate(dev)->clk;
struct clk *clk = &plat->clk;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
to_clk_fixed_rate(dev)->fixed_rate =
dev_read_u32_default(dev, "clock-frequency", 0);
plat->fixed_rate = dev_read_u32_default(dev, "clock-frequency", 0);
#endif
/* Make fixed rate clock accessible from higher level struct clk */
/* FIXME: This is not allowed */
dev_set_uclass_priv(dev, clk);
clk->dev = dev;
clk->enable_count = 0;
}
static int clk_fixed_rate_of_to_plat(struct udevice *dev)
{
clk_fixed_rate_ofdata_to_plat_(dev, to_clk_fixed_rate(dev));
return 0;
}

View File

@ -9,13 +9,7 @@
#include <errno.h>
#include <malloc.h>
#include <asm/clk.h>
struct sandbox_clk_priv {
bool probed;
ulong rate[SANDBOX_CLK_ID_COUNT];
bool enabled[SANDBOX_CLK_ID_COUNT];
bool requested[SANDBOX_CLK_ID_COUNT];
};
#include <linux/clk-provider.h>
static ulong sandbox_clk_get_rate(struct clk *clk)
{
@ -178,3 +172,35 @@ int sandbox_clk_query_requested(struct udevice *dev, int id)
return -EINVAL;
return priv->requested[id];
}
int clk_fixed_rate_of_to_plat(struct udevice *dev)
{
struct clk_fixed_rate *cplat;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct sandbox_clk_fixed_rate_plat *plat = dev_get_plat(dev);
cplat = &plat->fixed;
cplat->fixed_rate = plat->dtplat.clock_frequency;
#else
cplat = to_clk_fixed_rate(dev);
#endif
clk_fixed_rate_ofdata_to_plat_(dev, cplat);
return 0;
}
static const struct udevice_id sandbox_clk_fixed_rate_match[] = {
{ .compatible = "sandbox,fixed-clock" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(sandbox_fixed_clock) = {
.name = "sandbox_fixed_clock",
.id = UCLASS_CLK,
.of_match = sandbox_clk_fixed_rate_match,
.of_to_plat = clk_fixed_rate_of_to_plat,
.plat_auto = sizeof(struct sandbox_clk_fixed_rate_plat),
.ops = &clk_fixed_rate_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View File

@ -11,12 +11,6 @@
#include <dm/device_compat.h>
#include <linux/err.h>
struct sandbox_clk_test {
struct clk clks[SANDBOX_CLK_TEST_NON_DEVM_COUNT];
struct clk *clkps[SANDBOX_CLK_TEST_ID_COUNT];
struct clk_bulk bulk;
};
static const char * const sandbox_clk_test_names[] = {
[SANDBOX_CLK_TEST_ID_FIXED] = "fixed",
[SANDBOX_CLK_TEST_ID_SPI] = "spi",

View File

@ -45,6 +45,9 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
bool auto_seq = true;
void *ptr;
if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND))
return -ENOSYS;
if (devp)
*devp = NULL;
if (!name)
@ -395,26 +398,31 @@ int device_of_to_plat(struct udevice *dev)
if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
return 0;
/* Ensure all parents have ofdata */
if (dev->parent) {
ret = device_of_to_plat(dev->parent);
/*
* This is not needed if binding is disabled, since data is allocated
* at build time.
*/
if (!CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND)) {
/* Ensure all parents have ofdata */
if (dev->parent) {
ret = device_of_to_plat(dev->parent);
if (ret)
goto fail;
/*
* The device might have already been probed during
* the call to device_probe() on its parent device
* (e.g. PCI bridge devices). Test the flags again
* so that we don't mess up the device.
*/
if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
return 0;
}
ret = device_alloc_priv(dev);
if (ret)
goto fail;
/*
* The device might have already been probed during
* the call to device_probe() on its parent device
* (e.g. PCI bridge devices). Test the flags again
* so that we don't mess up the device.
*/
if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
return 0;
}
ret = device_alloc_priv(dev);
if (ret)
goto fail;
drv = dev->driver;
assert(drv);
@ -592,7 +600,7 @@ void *dev_get_plat(const struct udevice *dev)
return NULL;
}
return dev->plat_;
return dm_priv_to_rw(dev->plat_);
}
void *dev_get_parent_plat(const struct udevice *dev)
@ -602,7 +610,7 @@ void *dev_get_parent_plat(const struct udevice *dev)
return NULL;
}
return dev->parent_plat_;
return dm_priv_to_rw(dev->parent_plat_);
}
void *dev_get_uclass_plat(const struct udevice *dev)
@ -612,7 +620,7 @@ void *dev_get_uclass_plat(const struct udevice *dev)
return NULL;
}
return dev->uclass_plat_;
return dm_priv_to_rw(dev->uclass_plat_);
}
void *dev_get_priv(const struct udevice *dev)
@ -622,7 +630,7 @@ void *dev_get_priv(const struct udevice *dev)
return NULL;
}
return dev->priv_;
return dm_priv_to_rw(dev->priv_);
}
void *dev_get_uclass_priv(const struct udevice *dev)
@ -632,7 +640,7 @@ void *dev_get_uclass_priv(const struct udevice *dev)
return NULL;
}
return dev->uclass_priv_;
return dm_priv_to_rw(dev->uclass_priv_);
}
void *dev_get_parent_priv(const struct udevice *dev)
@ -642,7 +650,7 @@ void *dev_get_parent_priv(const struct udevice *dev)
return NULL;
}
return dev->parent_priv_;
return dm_priv_to_rw(dev->parent_priv_);
}
static int device_get_device_tail(struct udevice *dev, int ret,
@ -803,27 +811,19 @@ int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp)
}
#if CONFIG_IS_ENABLED(OF_PLATDATA)
int device_get_by_driver_info(const struct driver_info *info,
struct udevice **devp)
int device_get_by_ofplat_idx(uint idx, struct udevice **devp)
{
struct driver_info *info_base =
ll_entry_start(struct driver_info, driver_info);
int idx = info - info_base;
struct driver_rt *drt = gd_dm_driver_rt() + idx;
struct udevice *dev;
dev = drt->dev;
*devp = NULL;
if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
struct udevice *base = ll_entry_start(struct udevice, udevice);
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
}
dev = base + idx;
} else {
struct driver_rt *drt = gd_dm_driver_rt() + idx;
int device_get_by_driver_info_idx(uint idx, struct udevice **devp)
{
struct driver_rt *drt = gd_dm_driver_rt() + idx;
struct udevice *dev;
dev = drt->dev;
dev = drt->dev;
}
*devp = NULL;
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
@ -1136,3 +1136,36 @@ int dev_enable_by_path(const char *path)
return lists_bind_fdt(parent, node, NULL, false);
}
#endif
#if CONFIG_IS_ENABLED(OF_PLATDATA_RT)
static struct udevice_rt *dev_get_rt(const struct udevice *dev)
{
struct udevice *base = ll_entry_start(struct udevice, udevice);
int idx = dev - base;
struct udevice_rt *urt = gd_dm_udevice_rt() + idx;
return urt;
}
u32 dev_get_flags(const struct udevice *dev)
{
const struct udevice_rt *urt = dev_get_rt(dev);
return urt->flags_;
}
void dev_or_flags(const struct udevice *dev, u32 or)
{
struct udevice_rt *urt = dev_get_rt(dev);
urt->flags_ |= or;
}
void dev_bic_flags(const struct udevice *dev, u32 bic)
{
struct udevice_rt *urt = dev_get_rt(dev);
urt->flags_ &= ~bic;
}
#endif /* OF_PLATDATA_RT */

View File

@ -372,7 +372,7 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu,
bus_node->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
printf("Bad cell count for %s\n", of_node_full_name(dev));
return -EINVAL;
ret = -EINVAL;
goto out_parent;
}
@ -380,7 +380,7 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu,
bus_node->count_cells(parent, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
printf("Bad cell count for %s\n", of_node_full_name(parent));
return -EINVAL;
ret = -EINVAL;
goto out_parent;
}

View File

@ -11,6 +11,7 @@
#include <fdtdec.h>
#include <log.h>
#include <malloc.h>
#include <asm-generic/sections.h>
#include <asm/global_data.h>
#include <linux/libfdt.h>
#include <dm/acpi.h>
@ -129,6 +130,36 @@ void fix_devices(void)
}
}
static int dm_setup_inst(void)
{
DM_ROOT_NON_CONST = DM_DEVICE_GET(root);
if (CONFIG_IS_ENABLED(OF_PLATDATA_RT)) {
struct udevice_rt *urt;
void *base;
int n_ents;
uint size;
/* Allocate the udevice_rt table */
n_ents = ll_entry_count(struct udevice, udevice);
urt = calloc(n_ents, sizeof(struct udevice_rt));
if (!urt)
return log_msg_ret("urt", -ENOMEM);
gd_set_dm_udevice_rt(urt);
/* Now allocate space for the priv/plat data, and copy it in */
size = __priv_data_end - __priv_data_start;
base = calloc(1, size);
if (!base)
return log_msg_ret("priv", -ENOMEM);
memcpy(base, __priv_data_start, size);
gd_set_dm_priv_base(base);
}
return 0;
}
int dm_init(bool of_live)
{
int ret;
@ -140,8 +171,12 @@ int dm_init(bool of_live)
dm_warn("Virtual root driver already exists!\n");
return -EINVAL;
}
gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST;
INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST);
if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
gd->uclass_root = &uclass_head;
} else {
gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST;
INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST);
}
if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) {
fix_drivers();
@ -149,14 +184,23 @@ int dm_init(bool of_live)
fix_devices();
}
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(OF_CONTROL))
dev_set_ofnode(DM_ROOT_NON_CONST, ofnode_root());
ret = device_probe(DM_ROOT_NON_CONST);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
ret = dm_setup_inst();
if (ret) {
log_debug("dm_setup_inst() failed: %d\n", ret);
return ret;
}
} else {
ret = device_bind_by_name(NULL, false, &root_info,
&DM_ROOT_NON_CONST);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(OF_CONTROL))
dev_set_ofnode(DM_ROOT_NON_CONST, ofnode_root());
ret = device_probe(DM_ROOT_NON_CONST);
if (ret)
return ret;
}
return 0;
}
@ -185,7 +229,7 @@ int dm_scan_plat(bool pre_reloc_only)
{
int ret;
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
if (CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)) {
struct driver_rt *dyn;
int n_ents;
@ -303,6 +347,15 @@ __weak int dm_scan_other(bool pre_reloc_only)
return 0;
}
#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY)
void *dm_priv_to_rw(void *priv)
{
long offset = priv - (void *)__priv_data_start;
return gd_dm_priv_base() + offset;
}
#endif
/**
* dm_scan() - Scan tables to bind devices
*
@ -347,10 +400,12 @@ int dm_init_and_scan(bool pre_reloc_only)
debug("dm_init() failed: %d\n", ret);
return ret;
}
ret = dm_scan(pre_reloc_only);
if (ret) {
log_debug("dm_scan() failed: %d\n", ret);
return ret;
if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
ret = dm_scan(pre_reloc_only);
if (ret) {
log_debug("dm_scan() failed: %d\n", ret);
return ret;
}
}
return 0;

View File

@ -148,8 +148,11 @@ int uclass_get(enum uclass_id id, struct uclass **ucp)
*ucp = NULL;
uc = uclass_find(id);
if (!uc)
if (!uc) {
if (CONFIG_IS_ENABLED(OF_PLATDATA_INST))
return -ENOENT;
return uclass_add(id, ucp);
}
*ucp = uc;
return 0;
@ -391,7 +394,7 @@ done:
return ret;
}
#if CONFIG_IS_ENABLED(OF_CONTROL)
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent,
const char *name, struct udevice **devp)
{

View File

@ -39,9 +39,7 @@ obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
endif
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o

View File

@ -7,6 +7,7 @@
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <asm/i2c.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
@ -23,18 +24,6 @@
* uclass so avoid having strange devices on the I2C bus.
*/
/**
* struct i2c_emul_uc_plat - information about the emulator for this device
*
* This is used by devices in UCLASS_I2C_EMUL to record information about the
* device being emulated. It is accessible with dev_get_uclass_plat()
*
* @dev: Device being emulated
*/
struct i2c_emul_uc_plat {
struct udevice *dev;
};
struct udevice *i2c_emul_get_device(struct udevice *emul)
{
struct i2c_emul_uc_plat *uc_plat = dev_get_uclass_plat(emul);
@ -42,14 +31,27 @@ struct udevice *i2c_emul_get_device(struct udevice *emul)
return uc_plat->dev;
}
void i2c_emul_set_idx(struct udevice *dev, int emul_idx)
{
struct dm_i2c_chip *plat = dev_get_parent_plat(dev);
plat->emul_idx = emul_idx;
}
int i2c_emul_find(struct udevice *dev, struct udevice **emulp)
{
struct i2c_emul_uc_plat *uc_plat;
struct udevice *emul;
int ret;
ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev,
"sandbox,emul", &emul);
if (!CONFIG_IS_ENABLED(OF_PLATDATA)) {
ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev,
"sandbox,emul", &emul);
} else {
struct dm_i2c_chip *plat = dev_get_parent_plat(dev);
ret = device_get_by_ofplat_idx(plat->emul_idx, &emul);
}
if (ret) {
log_err("No emulators for device '%s'\n", dev->name);
return ret;
@ -85,8 +87,8 @@ static const struct udevice_id i2c_emul_parent_ids[] = {
{ }
};
U_BOOT_DRIVER(i2c_emul_parent_drv) = {
.name = "i2c_emul_parent_drv",
U_BOOT_DRIVER(sandbox_i2c_emul_parent) = {
.name = "sandbox_i2c_emul_parent",
.id = UCLASS_I2C_EMUL_PARENT,
.of_match = i2c_emul_parent_ids,
};

View File

@ -69,7 +69,7 @@ int irq_get_by_driver_info(struct udevice *dev,
{
int ret;
ret = device_get_by_driver_info_idx(cells->idx, &irq->dev);
ret = device_get_by_ofplat_idx(cells->idx, &irq->dev);
if (ret)
return ret;
irq->id = cells->arg[0];

View File

@ -86,7 +86,7 @@ static const struct udevice_id testbus_ids[] = {
{ }
};
U_BOOT_DRIVER(testbus_drv) = {
U_BOOT_DRIVER(denx_u_boot_test_bus) = {
.name = "testbus_drv",
.of_match = testbus_ids,
.id = UCLASS_TEST_BUS,
@ -98,6 +98,7 @@ U_BOOT_DRIVER(testbus_drv) = {
.per_child_plat_auto = sizeof(struct dm_test_parent_plat),
.child_pre_probe = testbus_child_pre_probe,
.child_post_remove = testbus_child_post_remove,
DM_HEADER(<test.h>)
};
UCLASS_DRIVER(testbus) = {
@ -106,6 +107,9 @@ UCLASS_DRIVER(testbus) = {
.flags = DM_UC_FLAG_SEQ_ALIAS,
.child_pre_probe = testbus_child_pre_probe_uclass,
.child_post_probe = testbus_child_post_probe_uclass,
/* This is for dtoc testing only */
.per_device_plat_auto = sizeof(struct dm_test_uclass_priv),
};
static int testfdt_drv_ping(struct udevice *dev, int pingval, int *pingret)
@ -160,7 +164,9 @@ static const struct udevice_id testfdt_ids[] = {
{ }
};
U_BOOT_DRIVER(testfdt_drv) = {
DM_DRIVER_ALIAS(denx_u_boot_fdt_test, google_another_fdt_test)
U_BOOT_DRIVER(denx_u_boot_fdt_test) = {
.name = "testfdt_drv",
.of_match = testfdt_ids,
.id = UCLASS_TEST_FDT,
@ -203,6 +209,7 @@ UCLASS_DRIVER(testfdt) = {
.name = "testfdt",
.id = UCLASS_TEST_FDT,
.flags = DM_UC_FLAG_SEQ_ALIAS,
.priv_auto = sizeof(struct dm_test_uc_priv),
};
static const struct udevice_id testfdtm_ids[] = {

View File

@ -311,6 +311,7 @@ config MMC_MXS
config MMC_PCI
bool "Support for MMC controllers on PCI"
depends on MMC_SDHCI
help
This selects PCI-based MMC controllers.
If you have an MMC controller on a PCI bus, say Y here.

View File

@ -1530,8 +1530,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
if (CONFIG_IS_ENABLED(DM_GPIO) && !priv->non_removable) {
struct udevice *gpiodev;
ret = device_get_by_driver_info_idx(dtplat->cd_gpios->idx,
&gpiodev);
ret = device_get_by_ofplat_idx(dtplat->cd_gpios->idx, &gpiodev);
if (ret)
return ret;

View File

@ -28,25 +28,6 @@
#define debug_buffer(x, ...)
#endif
/**
* struct sandbox_i2c_rtc_plat_data - platform data for the RTC
*
* @base_time: Base system time when RTC device was bound
* @offset: RTC offset from current system time
* @use_system_time: true to use system time, false to use @base_time
* @reg: Register values
*/
struct sandbox_i2c_rtc_plat_data {
long base_time;
long offset;
bool use_system_time;
u8 reg[REG_COUNT];
};
struct sandbox_i2c_rtc {
unsigned int offset_secs;
};
long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time,
int offset)
{
@ -223,7 +204,7 @@ static int sandbox_i2c_rtc_bind(struct udevice *dev)
}
static const struct udevice_id sandbox_i2c_rtc_ids[] = {
{ .compatible = "sandbox,i2c-rtc" },
{ .compatible = "sandbox,i2c-rtc-emul" },
{ }
};

View File

@ -79,6 +79,18 @@ struct acpi_ops sandbox_rtc_acpi_ops = {
};
#endif
static int sandbox_rtc_bind(struct udevice *dev)
{
#if CONFIG_IS_ENABLED(PLATDATA)
struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev);
/* Set up the emul_idx for i2c_emul_find() */
i2c_emul_set_idx(dev, plat->dtplat.sandbox_emul->idx);
#endif
return 0;
}
static const struct rtc_ops sandbox_rtc_ops = {
.get = sandbox_rtc_get,
.set = sandbox_rtc_set,
@ -97,5 +109,6 @@ U_BOOT_DRIVER(sandbox_rtc) = {
.id = UCLASS_RTC,
.of_match = sandbox_rtc_ids,
.ops = &sandbox_rtc_ops,
.bind = sandbox_rtc_bind,
ACPI_OPS_PTR(&sandbox_rtc_acpi_ops)
};

View File

@ -338,6 +338,7 @@ config SPL_OF_PLATDATA
bool "Generate platform data for use in SPL"
depends on SPL_OF_CONTROL
select DTOC
select SPL_OF_PLATDATA_DRIVER_RT if !SPL_OF_PLATDATA_INST
help
For very constrained SPL environments the overhead of decoding
device tree nodes and converting their contents into platform data
@ -355,19 +356,58 @@ config SPL_OF_PLATDATA
compatible string, then adding platform data and U_BOOT_DRVINFO
declarations for each node. See of-plat.txt for more information.
if SPL_OF_PLATDATA
config SPL_OF_PLATDATA_PARENT
bool "Support parent information in devices"
depends on SPL_OF_PLATDATA
default y
help
Generally it is useful to be able to access the parent of a device
with of-platdata. To save space this can be disabled, but in that
case dev_get_parent() will always return NULL;
config SPL_OF_PLATDATA_INST
bool "Declare devices at build time"
help
Declare devices as udevice instances so that they do not need to be
bound when U-Boot starts. This can save time and code space.
config SPL_OF_PLATDATA_NO_BIND
bool "Don't allow run-time binding of devices"
depends on SPL_OF_PLATDATA_INST
default y
help
This removes the ability to bind devices at run time, thus saving
some code space in U-Boot. This can be disabled if binding is needed,
at the code of some code size increase.
config SPL_OF_PLATDATA_RT
bool "Use a separate struct for device runtime data"
depends on SPL_OF_PLATDATA_INST
default y
help
For systems running SPL from read-only memory it is convenient to
separate out the runtime information, so that the devices don't need
to be copied before being used. This moves the read-write parts of
struct udevice (at present just the flags) into a separate struct,
which is allocated at runtime.
config SPL_OF_PLATDATA_DRIVER_RT
bool
help
Use a separate struct for driver runtime data.
This enables the driver_rt information, used with of-platdata when
of-platdata-inst is not used. It allows finding devices by their
driver data.
endif
config TPL_OF_PLATDATA
bool "Generate platform data for use in TPL"
depends on TPL_OF_CONTROL
select DTOC
select TPL_OF_PLATDATA_DRIVER_RT if !TPL_OF_PLATDATA_INST
help
For very constrained SPL environments the overhead of decoding
device tree nodes and converting their contents into platform data
@ -385,13 +425,52 @@ config TPL_OF_PLATDATA
compatible string, then adding platform data and U_BOOT_DRVINFO
declarations for each node. See of-plat.txt for more information.
if TPL_OF_PLATDATA
config TPL_OF_PLATDATA_PARENT
bool "Support parent information in devices"
depends on TPL_OF_PLATDATA
default y
help
Generally it is useful to be able to access the parent of a device
with of-platdata. To save space this can be disabled, but in that
case dev_get_parent() will always return NULL;
config TPL_OF_PLATDATA_INST
bool "Declare devices at build time"
help
Declare devices as udevice instances so that they do not need to be
bound when U-Boot starts. This can save time and code space.
config TPL_OF_PLATDATA_NO_BIND
bool "Don't allow run-time binding of devices"
depends on TPL_OF_PLATDATA_INST
default y
help
This removes the ability to bind devices at run time, thus saving
some code space in U-Boot. This can be disabled if binding is needed,
at the code of some code size increase.
config TPL_OF_PLATDATA_RT
bool "Use a separate struct for device runtime data"
depends on TPL_OF_PLATDATA_INST
default y
help
For systems running TPL from read-only memory it is convenient to
separate out the runtime information, so that the devices don't need
to be copied before being used. This moves the read-write parts of
struct udevice (at present just the flags) into a separate struct,
which is allocated at runtime.
config TPL_OF_PLATDATA_DRIVER_RT
bool
help
Use a separate struct for driver runtime data.
This enables the driver_rt information, used with of-platdata when
of-platdata-inst is not used. It allows finding devices by their
driver data.
endif
endmenu

View File

@ -215,10 +215,20 @@ struct global_data {
* @uclass_root_s.
*/
struct list_head *uclass_root;
# if CONFIG_IS_ENABLED(OF_PLATDATA)
# if CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)
/** @dm_driver_rt: Dynamic info about the driver */
struct driver_rt *dm_driver_rt;
# endif
#if CONFIG_IS_ENABLED(OF_PLATDATA_RT)
/** @dm_udevice_rt: Dynamic info about the udevice */
struct udevice_rt *dm_udevice_rt;
/**
* @dm_priv_base: Base address of the priv/plat region used when
* udevices and uclasses are in read-only memory. This is NULL if not
* used
*/
void *dm_priv_base;
# endif
#endif
#ifdef CONFIG_TIMER
/**
@ -483,7 +493,7 @@ struct global_data {
#define gd_set_of_root(_root)
#endif
#if CONFIG_IS_ENABLED(OF_PLATDATA)
#if CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)
#define gd_set_dm_driver_rt(dyn) gd->dm_driver_rt = dyn
#define gd_dm_driver_rt() gd->dm_driver_rt
#else
@ -491,6 +501,18 @@ struct global_data {
#define gd_dm_driver_rt() NULL
#endif
#if CONFIG_IS_ENABLED(OF_PLATDATA_RT)
#define gd_set_dm_udevice_rt(dyn) gd->dm_udevice_rt = dyn
#define gd_dm_udevice_rt() gd->dm_udevice_rt
#define gd_set_dm_priv_base(dyn) gd->dm_priv_base = dyn
#define gd_dm_priv_base() gd->dm_priv_base
#else
#define gd_set_dm_udevice_rt(dyn)
#define gd_dm_udevice_rt() NULL
#define gd_set_dm_priv_base(dyn)
#define gd_dm_priv_base() NULL
#endif
#ifdef CONFIG_GENERATE_ACPI_TABLE
#define gd_acpi_ctx() gd->acpi_ctx
#else

View File

@ -28,6 +28,9 @@ extern char __efi_helloworld_end[];
extern char __efi_var_file_begin[];
extern char __efi_var_file_end[];
/* Private data used by of-platdata devices/uclasses */
extern char __priv_data_start[], __priv_data_end[];
/* Start and end of .ctors section - used for constructor calls. */
extern char __ctors_start[], __ctors_end[];

View File

@ -10,11 +10,86 @@
#ifndef _DM_DEVICE_INTERNAL_H
#define _DM_DEVICE_INTERNAL_H
#include <linker_lists.h>
#include <dm/ofnode.h>
struct device_node;
struct udevice;
/*
* These two macros DM_DEVICE_INST and DM_DEVICE_REF are only allowed in code
* generated by dtoc, because the ordering is important and if other instances
* creep in then they may mess up the ordering expected by dtoc.
*
* It is OK to use them with 'extern' though, since that does not actually
* add a new record to the linker_list.
*/
/**
* DM_DEVICE_INST() - Declare a bound device ready for run-time use
*
* This adds an actual struct udevice to a list which is found by driver model
* on start-up.
*
* For example:
*
* extern U_BOOT_DRIVER(sandbox_fixed_clock);
* extern DM_UCLASS_INST(clk);
*
* DM_DEVICE_INST(clk_fixed) = {
* .driver = DM_DRIVER_REF(sandbox_fixed_clock),
* .name = "sandbox_fixed_clock",
* .plat_ = &_sandbox_fixed_clock_plat_clk_fixed,
* .uclass = DM_UCLASS_REF(clk),
* ...
* .seq_ = 0,
* };
*
* @_name: Name of the udevice. This must be a valid C identifier, used by the
* linker_list.
*/
#define DM_DEVICE_INST(_name) \
ll_entry_declare(struct udevice, _name, udevice)
/**
* DM_DEVICE_REF() - Get a reference to a device
*
* This is useful in data structures and code for referencing a udevice at
* build time. Before this is used, an extern DM_DEVICE_INST() must have been
* declared.
*
* For example:
*
* extern DM_DEVICE_INST(clk_fixed);
*
* struct udevice *devs[] = {
* DM_DEVICE_REF(clk_fixed),
* };
*
* @_name: Name of the udevice. This must be a valid C identifier, used by the
* linker_list
* @returns struct udevice * for the device
*/
#define DM_DEVICE_REF(_name) \
ll_entry_ref(struct udevice, _name, udevice)
/**
* DM_DEVICE_GET() - Get a pointer to a given device
*
* This is similar to DM_DEVICE_REF() except that it does not need the extern
* declaration before it. However it cannot be used in a data structures, only
* in code within a function.
*
* For example:
*
* void some_function() {
* struct udevice *dev = DM_DEVICE_GET(clk_fixed);
* ...
* }
*/
#define DM_DEVICE_GET(__name) \
ll_entry_get(struct udevice, __name, udevice)
/**
* device_bind() - Create a device and bind it to a driver
*
@ -209,6 +284,9 @@ static inline int device_chld_remove(struct udevice *dev, struct driver *drv,
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev Device to check
* @priv New private-data pointer
*/
@ -223,6 +301,9 @@ void dev_set_priv(struct udevice *dev, void *priv);
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev: Device to update
* @parent_priv: New parent-private data
*/
@ -237,6 +318,9 @@ void dev_set_parent_priv(struct udevice *dev, void *parent_priv);
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev: Device to update
* @uclass_priv: New uclass private data
*/
@ -251,6 +335,9 @@ void dev_set_uclass_priv(struct udevice *dev, void *uclass_priv);
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev Device to check
* @plat New platform-data pointer
*/
@ -265,6 +352,9 @@ void dev_set_plat(struct udevice *dev, void *priv);
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev: Device to update
* @parent_plat: New parent platform data
*/
@ -279,6 +369,9 @@ void dev_set_parent_plat(struct udevice *dev, void *parent_plat);
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @dev: Device to update
* @uclass_plat: New uclass platform data
*/

View File

@ -177,7 +177,9 @@ struct udevice {
struct list_head uclass_node;
struct list_head child_head;
struct list_head sibling_node;
#if !CONFIG_IS_ENABLED(OF_PLATDATA_RT)
u32 flags_;
#endif
int seq_;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
ofnode node_;
@ -190,12 +192,32 @@ struct udevice {
#endif
};
/**
* udevice_rt - runtime information set up by U-Boot
*
* This is only used with OF_PLATDATA_RT
*
* There is one of these for every udevice in the linker list, indexed by
* the udevice_info idx value.
*
* @flags_: Flags for this device DM_FLAG_... (do not access outside driver
* model)
*/
struct udevice_rt {
u32 flags_;
};
/* Maximum sequence number supported */
#define DM_MAX_SEQ 999
/* Returns the operations for a device */
#define device_get_ops(dev) (dev->driver->ops)
#if CONFIG_IS_ENABLED(OF_PLATDATA_RT)
u32 dev_get_flags(const struct udevice *dev);
void dev_or_flags(const struct udevice *dev, u32 or);
void dev_bic_flags(const struct udevice *dev, u32 bic);
#else
static inline u32 dev_get_flags(const struct udevice *dev)
{
return dev->flags_;
@ -210,6 +232,7 @@ static inline void dev_bic_flags(struct udevice *dev, u32 bic)
{
dev->flags_ &= ~bic;
}
#endif /* OF_PLATDATA_RT */
/**
* dev_ofnode() - get the DT node reference associated with a udevice
@ -362,6 +385,28 @@ struct driver {
#define DM_DRIVER_GET(__name) \
ll_entry_get(struct driver, __name, driver)
/**
* DM_DRIVER_REF() - Get a reference to a driver
*
* This is useful in data structures and code for referencing a driver at
* build time. Before this is used, an extern U_BOOT_DRIVER() must have been
* declared.
*
* For example:
*
* extern U_BOOT_DRIVER(sandbox_fixed_clock);
*
* struct driver *drvs[] = {
* DM_DRIVER_REF(sandbox_fixed_clock),
* };
*
* @_name: Name of the driver. This must be a valid C identifier, used by the
* linker_list
* @returns struct driver * for the driver
*/
#define DM_DRIVER_REF(_name) \
ll_entry_ref(struct driver, _name, driver)
/**
* Declare a macro to state a alias for a driver name. This macro will
* produce no code but its information will be parsed by tools like
@ -369,6 +414,40 @@ struct driver {
*/
#define DM_DRIVER_ALIAS(__name, __alias)
/**
* Declare a macro to indicate which phase of U-Boot this driver is fore.
*
*
* This macro produces no code but its information will be parsed by dtoc. The
* macro can be only be used once in a driver. Put it within the U_BOOT_DRIVER()
* declaration, e.g.:
*
* U_BOOT_DRIVER(cpu) = {
* .name = ...
* ...
* DM_PHASE(tpl)
* };
*/
#define DM_PHASE(_phase)
/**
* Declare a macro to declare a header needed for a driver. Often the correct
* header can be found automatically, but only for struct declarations. For
* enums and #defines used in the driver declaration and declared in a different
* header from the structs, this macro must be used.
*
* This macro produces no code but its information will be parsed by dtoc. The
* macro can be used multiple times with different headers, for the same driver.
* Put it within the U_BOOT_DRIVER() declaration, e.g.:
*
* U_BOOT_DRIVER(cpu) = {
* .name = ...
* ...
* DM_HEADER(<asm/cpu.h>)
* };
*/
#define DM_HEADER(_hdr)
/**
* dev_get_plat() - Get the platform data for a device
*
@ -611,33 +690,24 @@ int device_find_global_by_ofnode(ofnode node, struct udevice **devp);
int device_get_global_by_ofnode(ofnode node, struct udevice **devp);
/**
* device_get_by_driver_info() - Get a device based on driver_info
* device_get_by_ofplat_idx() - Get a device based on of-platdata index
*
* Locates a device by its struct driver_info, by using its reference which
* is updated during the bind process.
* Locates a device by either its struct driver_info index, or its
* struct udevice index. The latter is used with OF_PLATDATA_INST, since we have
* a list of build-time instantiated struct udevice records, The former is used
* with !OF_PLATDATA_INST since in that case we have a list of
* struct driver_info records.
*
* The index number is written into the idx field of struct phandle_1_arg, etc.
* It is the position of this driver_info/udevice in its linker list.
*
* The device is probed to activate it ready for use.
*
* @info: Struct driver_info
* @idx: Index number of the driver_info/udevice structure (0=first)
* @devp: Returns pointer to device if found, otherwise this is set to NULL
* @return 0 if OK, -ve on error
*/
int device_get_by_driver_info(const struct driver_info *info,
struct udevice **devp);
/**
* device_get_by_driver_info_idx() - Get a device based on driver_info index
*
* Locates a device by its struct driver_info, by using its index number which
* is written into the idx field of struct phandle_1_arg, etc.
*
* The device is probed to activate it ready for use.
*
* @idx: Index number of the driver_info structure (0=first)
* @devp: Returns pointer to device if found, otherwise this is set to NULL
* @return 0 if OK, -ve on error
*/
int device_get_by_driver_info_idx(uint idx, struct udevice **devp);
int device_get_by_ofplat_idx(uint idx, struct udevice **devp);
/**
* device_find_first_child() - Find the first child of a device

View File

@ -71,19 +71,4 @@ struct driver_rt {
#define U_BOOT_DRVINFOS(__name) \
ll_entry_declare_list(struct driver_info, __name, driver_info)
/**
* Get a pointer to a given device info given its name
*
* With the declaration U_BOOT_DRVINFO(name), DM_DRVINFO_GET(name) will return a
* pointer to the struct driver_info created by that declaration.
*
* if OF_PLATDATA is enabled, from this it is possible to use the @dev member of
* struct driver_info to find the device pointer itself.
*
* @__name: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000)
* @return struct driver_info * to the driver that created the device
*/
#define DM_DRVINFO_GET(__name) \
ll_entry_get(struct driver_info, __name, driver_info)
#endif

View File

@ -11,6 +11,9 @@
struct udevice;
/* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */
extern struct list_head uclass_head;
/**
* dm_root() - Return pointer to the top of the driver tree
*

View File

@ -71,6 +71,11 @@ struct dm_test_priv {
int uclass_postp;
};
/* struct dm_test_uc_priv - private data for the testdrv uclass */
struct dm_test_uc_priv {
int dummy;
};
/**
* struct dm_test_perdev_class_priv - private per-device data for test uclass
*/

View File

@ -11,6 +11,55 @@
#include <dm/ofnode.h>
/*
* These next two macros DM_UCLASS_INST() and DM_UCLASS_REF() are only allowed
* in code generated by dtoc, because the ordering is important and if other
* instances creep in then they may mess up the ordering expected by dtoc.
*
* It is OK to use them with 'extern' though, since that does not actually
* add a new record to the linker_list.
*/
/**
* DM_UCLASS_INST() - Declare a uclass ready for run-time use
*
* This adds an actual struct uclass to a list which is found by driver model
* on start-up.
*
* For example:
*
* DM_UCLASS_INST(clk) = {
* .uc_drv = DM_UCLASS_DRIVER_REF(clk),
* ...
* };
*
* @_name: Name of the uclass. This must be a valid C identifier, used by the
* linker_list.
*/
#define DM_UCLASS_INST(_name) \
ll_entry_declare(struct uclass, _name, uclass)
/**
* DM_UCLASS_REF() - Get a reference to a uclass
*
* This is useful for referencing a uclass at build time. Before this is used,
* an extern DM_UCLASS_INST() must have been declared.
*
* For example:
*
* extern DM_UCLASS_INST(clk);
*
* struct uclass *ucs[] = {
* DM_UCLASS_REF(clk),
* }
*
* @_name: Name of the uclass. This must be a valid C identifier, used by the
* linker_list
* @returns struct uclass * for the device
*/
#define DM_UCLASS_REF(_name) \
ll_entry_ref(struct uclass, _name, uclass)
/**
* uclass_set_priv() - Set the private data for a uclass
*
@ -20,6 +69,9 @@
* Use this function to override normal operation for special situations, such
* as needing to allocate a variable amount of data.
*
* If OF_PLATDATA_RT is enabled, this function cannot be used out of core driver
* model code, since the pointer must be within the gd->dm_priv_base region.
*
* @uc Uclass to update
* @priv New private-data pointer
*/

View File

@ -114,6 +114,37 @@ struct uclass_driver {
#define UCLASS_DRIVER(__name) \
ll_entry_declare(struct uclass_driver, __name, uclass_driver)
/*
* These two macros DM_UCLASS_DRIVER_REF and DM_UCLASS_DRIVER_REF are only
* allowed in code generated by dtoc, because the ordering is important and if
* other instances creep in then they may mess up the ordering expected by dtoc.
*
* It is OK to use them with 'extern' though, since that does not actually
* add a new record to the linker_list.
*/
/**
* DM_UCLASS_DRIVER_REF() - Get a reference to a uclass driver
*
* This is useful in data structures and code for referencing a uclass_driver at
* build time. Before this is used, an extern UCLASS_DRIVER() must have been
* declared.
*
* For example:
*
* extern UCLASS_DRIVER(clk);
*
* struct uclass_driver *drvs[] = {
* DM_UCLASS_DRIVER_REF(clk),
* };
*
* @_name: Name of the uclass_driver. This must be a valid C identifier, used by
* the linker_list.
* @returns struct uclass_driver * for the uclass driver
*/
#define DM_UCLASS_DRIVER_REF(_name) \
ll_entry_ref(struct uclass_driver, _name, uclass_driver)
/**
* uclass_get_priv() - Get the private data for a uclass
*

View File

@ -49,3 +49,12 @@ void dm_dump_driver_compat(void);
void dm_dump_static_driver_info(void);
#endif
#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY)
void *dm_priv_to_rw(void *priv);
#else
static inline void *dm_priv_to_rw(void *priv)
{
return priv;
}
#endif

View File

@ -24,7 +24,9 @@ struct phandle_2_arg {
uint idx;
int arg[2];
};
#include <generated/dt-structs-gen.h>
#include <generated/dt-decl.h>
#endif
#endif

View File

@ -93,6 +93,8 @@ struct udevice;
* datasheet explains it's usage of this addressing
* mode.
* @emul: Emulator for this chip address (only used for emulation)
* @emul_idx: Emulator index, used for of-platdata and set by each i2c chip's
* bind() method. This allows i2c_emul_find() to work with of-platdata.
*/
struct dm_i2c_chip {
uint chip_addr;
@ -102,6 +104,7 @@ struct dm_i2c_chip {
#ifdef CONFIG_SANDBOX
struct udevice *emul;
bool test_mode;
int emul_idx;
#endif
};
@ -554,6 +557,18 @@ void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs);
*/
int i2c_emul_find(struct udevice *dev, struct udevice **emulp);
/**
* i2c_emul_set_idx() - Set the emulator index for an i2c sandbox device
*
* With of-platdata we cannot find the emulator using the device tree, so rely
* on the bind() method of each i2c driver calling this function to tell us
* the of-platdata idx of the emulator
*
* @dev: i2c device to set the emulator for
* @emul_idx: of-platdata index for that emulator
*/
void i2c_emul_set_idx(struct udevice *dev, int emul_idx);
/**
* i2c_emul_get_device() - Find the device being emulated
*

View File

@ -211,6 +211,18 @@
_ll_result; \
})
/**
* ll_entry_ref() - Get a reference to a linker-generated array entry
*
* Once an extern ll_entry_declare() has been used to declare the reference,
* this macro allows the entry to be accessed.
*
* This is like ll_entry_get(), but without the extra code, so it is suitable
* for putting into data structures.
*/
#define ll_entry_ref(_type, _name, _list) \
((_type *)&_u_boot_list_2_##_list##_2_##_name)
/**
* ll_start() - Point to first entry of first linker-generated array
* @_type: Data type of the entry

View File

@ -192,6 +192,8 @@ struct clk_fixed_factor {
unsigned int div;
};
extern const struct clk_ops clk_fixed_rate_ops;
#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\
clk)
@ -202,6 +204,9 @@ struct clk_fixed_rate {
#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_plat(dev))
void clk_fixed_rate_ofdata_to_plat_(struct udevice *dev,
struct clk_fixed_rate *plat);
struct clk_composite {
struct clk clk;
struct clk_ops ops;

View File

@ -120,10 +120,25 @@ endif
u-boot-spl-init := $(head-y)
u-boot-spl-main := $(libs-y)
ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA
u-boot-spl-platdata := $(obj)/dts/dt-plat.o
u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata))
platdata-hdr := include/generated/dt-structs-gen.h include/generated/dt-decl.h
platdata-inst := $(obj)/dts/dt-uclass.o $(obj)/dts/dt-device.o
platdata-noinst := $(obj)/dts/dt-plat.o
ifdef CONFIG_$(SPL_TPL_)OF_PLATDATA_INST
u-boot-spl-platdata := $(platdata-inst)
u-boot-spl-old-platdata := $(platdata-noinst)
else
u-boot-spl-platdata := $(platdata-noinst)
u-boot-spl-old-platdata := $(platdata-inst)
endif
# Files we need to generate
u-boot-spl-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-platdata))
# Files we won't generate and should remove
u-boot-spl-old-platdata_c := $(patsubst %.o,%.c,$(u-boot-spl-old-platdata))
endif # OF_PLATDATA
# Linker Script
# First test whether there's a linker-script for the specific stage defined...
ifneq ($(CONFIG_$(SPL_TPL_)LDSCRIPT),)
@ -311,7 +326,11 @@ $(obj)/$(SPL_BIN).dtb: $(obj)/dts/dt-$(SPL_NAME).dtb FORCE
pythonpath = PYTHONPATH=scripts/dtc/pylibfdt
DTOC_ARGS := $(pythonpath) $(srctree)/tools/dtoc/dtoc \
-d $(obj)/$(SPL_BIN).dtb
-d $(obj)/$(SPL_BIN).dtb -p $(SPL_NAME)
ifneq ($(CONFIG_$(SPL_TPL_)OF_PLATDATA_INST),)
DTOC_ARGS += -i
endif
quiet_cmd_dtoc = DTOC $@
cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all
@ -319,18 +338,17 @@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all
quiet_cmd_plat = PLAT $@
cmd_plat = $(CC) $(c_flags) -c $< -o $(filter-out $(PHONY),$@)
targets += $(u-boot-spl-platdata)
$(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c \
include/generated/dt-structs-gen.h prepare FORCE
$(obj)/dts/dt-%.o: $(obj)/dts/dt-%.c $(platdata-hdr)
$(call if_changed,plat)
PHONY += dts_dir
dts_dir:
$(shell [ -d $(obj)/dts ] || mkdir -p $(obj)/dts)
include/generated/dt-structs-gen.h $(u-boot-spl-platdata_c) &: \
$(obj)/$(SPL_BIN).dtb dts_dir FORCE
# Don't use dts_dir here, since it forces running this expensive rule every time
$(platdata-hdr) $(u-boot-spl-platdata_c) &: $(obj)/$(SPL_BIN).dtb
@[ -d $(obj)/dts ] || mkdir -p $(obj)/dts
@# Remove old files since which ones we generate depends on the setting
@# of OF_PLATDATA_INST and this might change between builds. Leaving old
@# ones around is confusing and it is possible that switching the
@# setting again will use the old one instead of regenerating it.
@rm -f $(u-boot-spl-all-platdata_c) $(u-boot-spl-all-platdata)
$(call if_changed,dtoc)
ifdef CONFIG_SAMSUNG
@ -471,6 +489,10 @@ FORCE:
$(obj)/dts/dt-$(SPL_NAME).dtb: dts/dt.dtb
$(Q)$(MAKE) $(build)=$(obj)/dts spl_dtbs
PHONY += dts_dir
dts_dir:
$(shell [ -d $(obj)/dts ] || mkdir -p $(obj)/dts)
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)

View File

@ -23,12 +23,18 @@ quiet_cmd_pymod = PYMOD $@
SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \
$(PYTHON3) $< --quiet build_ext --inplace
$(obj)/_libfdt.so: $(src)/setup.py $(PYLIBFDT_srcs) FORCE
rebuild: $(src)/setup.py $(PYLIBFDT_srcs)
@# Remove the library since otherwise Python doesn't seem to regenerate
@# the libfdt.py file if it is missing.
rm -f $(obj)/_libfdt*.so
@rm -f $(obj)/_libfdt*.so
$(call if_changed,pymod)
@# Rename the file to _libfdt.so so this Makefile doesn't run every time
@if [ ! -e $(obj)/_libfdt.so ]; then \
mv $(obj)/_libfdt*.so $(obj)/_libfdt.so; \
fi
always += _libfdt.so
$(obj)/_libfdt.so $(obj)/libfdt.py &: rebuild
always += _libfdt.so libfdt.py
clean-files += libfdt.i _libfdt.so libfdt.py libfdt_wrap.c

View File

@ -15,7 +15,7 @@
#define BUF_SIZE 0x100
/* Declare a new mem test */
/* Declare a new setexpr test */
#define SETEXPR_TEST(_name, _flags) UNIT_TEST(_name, _flags, setexpr_test)
/* Test 'setexpr' command with simply setting integers */

View File

@ -142,12 +142,14 @@ static int find_driver_info(struct unit_test_state *uts, struct udevice *parent,
/* Check that every device is recorded in its driver_info struct */
static int dm_test_of_plat_dev(struct unit_test_state *uts)
{
const struct driver_info *info =
ll_entry_start(struct driver_info, driver_info);
const int n_ents = ll_entry_count(struct driver_info, driver_info);
bool found[n_ents];
uint i;
/* Skip this test if there is no platform data */
if (!CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT))
return 0;
/* Record the indexes that are found */
memset(found, '\0', sizeof(found));
ut_assertok(find_driver_info(uts, gd->dm_root, found));
@ -155,18 +157,16 @@ static int dm_test_of_plat_dev(struct unit_test_state *uts)
/* Make sure that the driver entries without devices have no ->dev */
for (i = 0; i < n_ents; i++) {
const struct driver_rt *drt = gd_dm_driver_rt() + i;
const struct driver_info *entry = info + i;
struct udevice *dev;
if (found[i]) {
/* Make sure we can find it */
ut_assertnonnull(drt->dev);
ut_assertok(device_get_by_driver_info(entry, &dev));
ut_assertok(device_get_by_ofplat_idx(i, &dev));
ut_asserteq_ptr(dev, drt->dev);
} else {
ut_assertnull(drt->dev);
ut_asserteq(-ENOENT,
device_get_by_driver_info(entry, &dev));
ut_asserteq(-ENOENT, device_get_by_ofplat_idx(i, &dev));
}
}
@ -184,22 +184,22 @@ static int dm_test_of_plat_phandle(struct unit_test_state *uts)
ut_asserteq_str("sandbox_clk_test", dev->name);
plat = dev_get_plat(dev);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[0].idx, &clk));
ut_asserteq_str("fixed_clock", clk->name);
ut_assertok(device_get_by_ofplat_idx(plat->clocks[0].idx, &clk));
ut_asserteq_str("sandbox_fixed_clock", clk->name);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[1].idx, &clk));
ut_assertok(device_get_by_ofplat_idx(plat->clocks[1].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(1, plat->clocks[1].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[2].idx, &clk));
ut_assertok(device_get_by_ofplat_idx(plat->clocks[2].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(0, plat->clocks[2].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[3].idx, &clk));
ut_assertok(device_get_by_ofplat_idx(plat->clocks[3].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(3, plat->clocks[3].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[4].idx, &clk));
ut_assertok(device_get_by_ofplat_idx(plat->clocks[4].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(2, plat->clocks[4].arg[0]);
@ -211,11 +211,11 @@ DM_TEST(dm_test_of_plat_phandle, UT_TESTF_SCAN_PDATA);
/* Test that device parents are correctly set up */
static int dm_test_of_plat_parent(struct unit_test_state *uts)
{
struct udevice *dev, *bus;
struct udevice *rtc, *i2c;
ut_assertok(uclass_first_device_err(UCLASS_SIMPLE_BUS, &bus));
ut_assertok(device_first_child_err(bus, &dev));
ut_asserteq_ptr(bus, dev_get_parent(dev));
ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc));
ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
ut_asserteq_ptr(i2c, dev_get_parent(rtc));
return 0;
}

View File

@ -330,7 +330,7 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts)
/* Check creating a device with an alias */
node = ofnode_path("/some-bus/c-test@1");
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv),
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
"c-test@1", NULL, node, &dev));
ut_asserteq(12, dev_seq(dev));
ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 12, &dev));
@ -350,11 +350,11 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts)
*
* So next available is 19
*/
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv),
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
"fred", NULL, ofnode_null(), &dev));
ut_asserteq(19, dev_seq(dev));
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv),
ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
"fred2", NULL, ofnode_null(), &dev));
ut_asserteq(20, dev_seq(dev));

View File

@ -6,8 +6,8 @@
#define DEBUG
#include <common.h>
#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
#include <command.h>
#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
#include <efi_api.h>
#endif
#include <display_options.h>

View File

@ -30,6 +30,10 @@ fi
run_test "sandbox_spl" ./test/py/test.py --bd sandbox_spl --build \
-k 'test_ofplatdata or test_handoff or test_spl'
# Run the sane tests with sandbox_noinst (i.e. without OF_PLATDATA_INST)
run_test "sandbox_spl" ./test/py/test.py --bd sandbox_noinst --build \
-k 'test_ofplatdata or test_handoff or test_spl'
if [ -z "$tools_only" ]; then
# Run tests for the flat-device-tree version of sandbox. This is a special
# build which does not enable CONFIG_OF_LIVE for the live device tree, so we can

View File

@ -62,17 +62,25 @@ static int dm_test_post_run(struct unit_test_state *uts)
{
int id;
for (id = 0; id < UCLASS_COUNT; id++) {
struct uclass *uc;
/*
* With of-platdata-inst the uclasses are created at build time. If we
* destroy them we cannot get them back since uclass_add() is not
* supported. So skip this.
*/
if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
for (id = 0; id < UCLASS_COUNT; id++) {
struct uclass *uc;
/*
* If the uclass doesn't exist we don't want to create it. So
* check that here before we call uclass_find_device().
*/
uc = uclass_find(id);
if (!uc)
continue;
ut_assertok(uclass_destroy(uc));
/*
* If the uclass doesn't exist we don't want to create
* it. So check that here before we call
* uclass_find_device().
*/
uc = uclass_find(id);
if (!uc)
continue;
ut_assertok(uclass_destroy(uc));
}
}
return 0;

1
tools/binman/README.rst Symbolic link
View File

@ -0,0 +1 @@
binman.rst

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,9 @@ controlled by a description in the board device tree.'''
default=False, help='Output a map file for each image')
build_parser.add_argument('-M', '--allow-missing', action='store_true',
default=False, help='Allow external blobs to be missing')
build_parser.add_argument('-n', '--no-expanded', action='store_true',
help="Don't use 'expanded' versions of entries where available; "
"normally 'u-boot' becomes 'u-boot-expanded', for example")
build_parser.add_argument('-O', '--outdir', type=str,
action='store', help='Path to directory to use for intermediate and '
'output files')
@ -66,7 +69,7 @@ controlled by a description in the board device tree.'''
default=False, help='Update the binman node with offset/size info')
entry_parser = subparsers.add_parser('entry-docs',
help='Write out entry documentation (see README.entries)')
help='Write out entry documentation (see entries.rst)')
list_parser = subparsers.add_parser('ls', help='List files in an image')
list_parser.add_argument('-i', '--image', type=str, required=True,

View File

@ -28,7 +28,7 @@ images = OrderedDict()
# value: Text for the help
missing_blob_help = {}
def _ReadImageDesc(binman_node):
def _ReadImageDesc(binman_node, use_expanded):
"""Read the image descriptions from the /binman node
This normally produces a single Image object called 'image'. But if
@ -36,15 +36,17 @@ def _ReadImageDesc(binman_node):
Args:
binman_node: Node object of the /binman node
use_expanded: True if the FDT will be updated with the entry information
Returns:
OrderedDict of Image objects, each of which describes an image
"""
images = OrderedDict()
if 'multiple-images' in binman_node.props:
for node in binman_node.subnodes:
images[node.name] = Image(node.name, node)
images[node.name] = Image(node.name, node,
use_expanded=use_expanded)
else:
images['image'] = Image('image', binman_node)
images['image'] = Image('image', binman_node, use_expanded=use_expanded)
return images
def _FindBinmanNode(dtb):
@ -241,7 +243,7 @@ def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
# If this entry has children, create a directory for it and put its
# data in a file called 'root' in that directory
if entry.GetEntries():
if not os.path.exists(fname):
if fname and not os.path.exists(fname):
os.makedirs(fname)
fname = os.path.join(fname, 'root')
tout.Notice("Write entry '%s' size %x to '%s'" %
@ -399,7 +401,7 @@ def ReplaceEntries(image_fname, input_fname, indir, entry_paths,
return image
def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt):
def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded):
"""Prepare the images to be processed and select the device tree
This function:
@ -413,6 +415,9 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt):
dtb_fname: Filename of the device tree file to use (.dts or .dtb)
selected_images: List of images to output, or None for all
update_fdt: True to update the FDT wth entry offsets, etc.
use_expanded: True to use expanded versions of entries, if available.
So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This
is needed if update_fdt is True (although tests may disable it)
Returns:
OrderedDict of images:
@ -438,7 +443,7 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt):
raise ValueError("Device tree '%s' does not have a 'binman' "
"node" % dtb_fname)
images = _ReadImageDesc(node)
images = _ReadImageDesc(node, use_expanded)
if select_images:
skip = []
@ -564,7 +569,7 @@ def Binman(args):
if not pager:
pager = 'more'
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
'README')
'README.rst')
command.Run(pager, fname)
return 0
@ -611,6 +616,13 @@ def Binman(args):
elf.debug = args.debug
cbfs_util.VERBOSE = args.verbosity > 2
state.use_fake_dtb = args.fake_dtb
# Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc.
# When running tests this can be disabled using this flag. When not
# updating the FDT in image, it is not needed by binman, but we use it
# for consistency, so that the images look the same to U-Boot at
# runtime.
use_expanded = not args.no_expanded
try:
tools.SetInputDirs(args.indir)
tools.PrepareOutputDir(args.outdir, args.preserve)
@ -618,7 +630,7 @@ def Binman(args):
state.SetEntryArgs(args.entry_arg)
images = PrepareImagesAndDtbs(dtb_fname, args.image,
args.update_fdt)
args.update_fdt, use_expanded)
missing = False
for image in images.values():
missing |= ProcessImage(image, args.update_fdt, args.map,

Some files were not shown because too many files have changed in this diff Show More