diff options
71 files changed, 2495 insertions, 130 deletions
@@ -802,7 +802,7 @@ quiet_cmd_pad_cat = CAT $@ cmd_pad_cat = $(cmd_objcopy) && $(append) || rm -f $@ all: $(ALL-y) -ifeq ($(CONFIG_DM_I2C_COMPAT),y) +ifeq ($(CONFIG_DM_I2C_COMPAT)$(CONFIG_SANDBOX),y) @echo "===================== WARNING ======================" @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove" @echo "(possibly in a subsequent patch in your series)" @@ -1319,7 +1319,8 @@ u-boot.lds: $(LDSCRIPT) prepare FORCE spl/u-boot-spl.bin: spl/u-boot-spl @: -spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb) +spl/u-boot-spl: tools prepare \ + $(if $(CONFIG_OF_SEPARATE)$(CONFIG_SPL_OF_PLATDATA),dts/dt.dtb) $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all spl/sunxi-spl.bin: spl/u-boot-spl @@ -3835,9 +3835,6 @@ Configuration Settings: The memory will be freed (or in fact just forgotten) when U-Boot relocates itself. - Pre-relocation malloc() is only supported on ARM and sandbox - at present but is fairly easy to enable for other archs. - - CONFIG_SYS_MALLOC_SIMPLE Provides a simple and small malloc() and calloc() for those boards which do not use the full malloc in SPL (which is diff --git a/arch/arm/dts/rk3288-firefly.dts b/arch/arm/dts/rk3288-firefly.dts index aed8d3a712..3176d5046b 100644 --- a/arch/arm/dts/rk3288-firefly.dts +++ b/arch/arm/dts/rk3288-firefly.dts @@ -30,7 +30,8 @@ 0x5 0x0>; rockchip,phy-timing = <0x48f9aab4 0xea0910 0x1002c200 0xa60 0x40 0x10 0x0>; - rockchip,sdram-channel = /bits/ 8 <0x1 0xa 0x3 0x2 0x1 0x0 0xf 0xf>; + /* Add a dummy value to cause of-platdata think this is bytes */ + rockchip,sdram-channel = /bits/ 8 <0x1 0xa 0x3 0x2 0x1 0x0 0xf 0xf 0xff>; rockchip,sdram-params = <0x30B25564 0x627 3 666000000 3 9 1>; }; diff --git a/arch/arm/include/asm/arch-rockchip/sdram.h b/arch/arm/include/asm/arch-rockchip/sdram.h index d3de42d297..e08e28f4f0 100644 --- a/arch/arm/include/asm/arch-rockchip/sdram.h +++ b/arch/arm/include/asm/arch-rockchip/sdram.h @@ -24,6 +24,12 @@ struct rk3288_sdram_channel { u8 row_3_4; u8 cs0_row; u8 cs1_row; + /* + * For of-platdata, which would otherwise convert this into two + * byte-swapped integers. With a size of 9 bytes, this struct will + * appear in of-platdata as a byte array. + */ + u8 dummy; }; struct rk3288_sdram_pctl_timing { @@ -81,12 +87,4 @@ struct rk3288_base_params { u32 odt; }; -struct rk3288_sdram_params { - struct rk3288_sdram_channel ch[2]; - struct rk3288_sdram_pctl_timing pctl_timing; - struct rk3288_sdram_phy_timing phy_timing; - struct rk3288_base_params base; - int num_channels; -}; - #endif diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c index 15f1266a7b..123f58b27f 100644 --- a/arch/arm/mach-rockchip/rk3288-board-spl.c +++ b/arch/arm/mach-rockchip/rk3288-board-spl.c @@ -29,6 +29,7 @@ DECLARE_GLOBAL_DATA_PTR; u32 spl_boot_device(void) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) const void *blob = gd->fdt_blob; struct udevice *dev; const char *bootdev; @@ -63,6 +64,7 @@ u32 spl_boot_device(void) } fallback: +#endif return BOOT_DEVICE_MMC1; } @@ -114,7 +116,6 @@ static void configure_l2ctlr(void) #ifdef CONFIG_SPL_MMC_SUPPORT static int configure_emmc(struct udevice *pinctrl) { -#if !defined(CONFIG_TARGET_ROCK2) && !defined(CONFIG_TARGET_FIREFLY_RK3288) struct gpio_desc desc; int ret; @@ -144,7 +145,6 @@ static int configure_emmc(struct udevice *pinctrl) debug("gpio value ret=%d\n", ret); return ret; } -#endif return 0; } @@ -247,15 +247,18 @@ void spl_board_init(void) goto err; } #ifdef CONFIG_SPL_MMC_SUPPORT - ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD); - if (ret) { - debug("%s: Failed to set up SD card\n", __func__); - goto err; - } - ret = configure_emmc(pinctrl); - if (ret) { - debug("%s: Failed to set up eMMC\n", __func__); - goto err; + if (!IS_ENABLED(CONFIG_TARGET_ROCK2) && + !IS_ENABLED(CONFIG_TARGET_FIREFLY_RK3288)) { + ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD); + if (ret) { + debug("%s: Failed to set up SD card\n", __func__); + goto err; + } + ret = configure_emmc(pinctrl); + if (ret) { + debug("%s: Failed to set up eMMC\n", __func__); + goto err; + } } #endif diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c index 55ac73e9d2..b36b6afcd9 100644 --- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c @@ -10,6 +10,7 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <dt-structs.h> #include <errno.h> #include <ram.h> #include <regmap.h> @@ -41,6 +42,19 @@ struct dram_info { struct rk3288_grf *grf; struct rk3288_sgrf *sgrf; struct rk3288_pmu *pmu; + bool is_veyron; +}; + +struct rk3288_sdram_params { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_dmc of_plat; +#endif + struct rk3288_sdram_channel ch[2]; + struct rk3288_sdram_pctl_timing pctl_timing; + struct rk3288_sdram_phy_timing phy_timing; + struct rk3288_base_params base; + int num_channels; + struct regmap *map; }; #ifdef CONFIG_SPL_BUILD @@ -703,7 +717,7 @@ static int sdram_init(struct dram_info *dram, return 0; } -#endif +#endif /* CONFIG_SPL_BUILD */ size_t sdram_size_mb(struct rk3288_pmu *pmu) { @@ -779,18 +793,36 @@ static int veyron_init(struct dram_info *priv) static int setup_sdram(struct udevice *dev) { struct dram_info *priv = dev_get_priv(dev); - struct rk3288_sdram_params params; + struct rk3288_sdram_params *params = dev_get_platdata(dev); + +# ifdef CONFIG_ROCKCHIP_FAST_SPL + if (priv->is_veyron) { + int ret; + + ret = veyron_init(priv); + if (ret) + return ret; + } +# endif + + return sdram_init(priv, params); +} + +static int rk3288_dmc_ofdata_to_platdata(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3288_sdram_params *params = dev_get_platdata(dev); const void *blob = gd->fdt_blob; int node = dev->of_offset; int i, ret; - params.num_channels = fdtdec_get_int(blob, node, - "rockchip,num-channels", 1); - for (i = 0; i < params.num_channels; i++) { + params->num_channels = fdtdec_get_int(blob, node, + "rockchip,num-channels", 1); + for (i = 0; i < params->num_channels; i++) { ret = fdtdec_get_byte_array(blob, node, "rockchip,sdram-channel", - (u8 *)¶ms.ch[i], - sizeof(params.ch[i])); + (u8 *)¶ms->ch[i], + sizeof(params->ch[i])); if (ret) { debug("%s: Cannot read rockchip,sdram-channel\n", __func__); @@ -798,46 +830,82 @@ static int setup_sdram(struct udevice *dev) } } ret = fdtdec_get_int_array(blob, node, "rockchip,pctl-timing", - (u32 *)¶ms.pctl_timing, - sizeof(params.pctl_timing) / sizeof(u32)); + (u32 *)¶ms->pctl_timing, + sizeof(params->pctl_timing) / sizeof(u32)); if (ret) { debug("%s: Cannot read rockchip,pctl-timing\n", __func__); return -EINVAL; } ret = fdtdec_get_int_array(blob, node, "rockchip,phy-timing", - (u32 *)¶ms.phy_timing, - sizeof(params.phy_timing) / sizeof(u32)); + (u32 *)¶ms->phy_timing, + sizeof(params->phy_timing) / sizeof(u32)); if (ret) { debug("%s: Cannot read rockchip,phy-timing\n", __func__); return -EINVAL; } ret = fdtdec_get_int_array(blob, node, "rockchip,sdram-params", - (u32 *)¶ms.base, - sizeof(params.base) / sizeof(u32)); + (u32 *)¶ms->base, + sizeof(params->base) / sizeof(u32)); if (ret) { debug("%s: Cannot read rockchip,sdram-params\n", __func__); return -EINVAL; } +#ifdef CONFIG_ROCKCHIP_FAST_SPL + struct dram_info *priv = dev_get_priv(dev); -# ifdef CONFIG_ROCKCHIP_FAST_SPL - if (!fdt_node_check_compatible(blob, 0, "google,veyron")) { - ret = veyron_init(priv); - if (ret) - return ret; + priv->is_veyron = !fdt_node_check_compatible(blob, 0, "google,veyron"); +#endif + ret = regmap_init_mem(dev, ¶ms->map); + if (ret) + return ret; +#endif + + return 0; +} +#endif /* CONFIG_SPL_BUILD */ + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +static int conv_of_platdata(struct udevice *dev) +{ + struct rk3288_sdram_params *plat = dev_get_platdata(dev); + struct dtd_rockchip_rk3288_dmc *of_plat = &plat->of_plat; + int i, ret; + + for (i = 0; i < 2; i++) { + memcpy(&plat->ch[i], of_plat->rockchip_sdram_channel, + sizeof(plat->ch[i])); } -# endif + memcpy(&plat->pctl_timing, of_plat->rockchip_pctl_timing, + sizeof(plat->pctl_timing)); + memcpy(&plat->phy_timing, of_plat->rockchip_phy_timing, + sizeof(plat->phy_timing)); + memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base)); + plat->num_channels = of_plat->rockchip_num_channels; + ret = regmap_init_mem_platdata(dev, of_plat->reg, + ARRAY_SIZE(of_plat->reg) / 2, + &plat->map); + if (ret) + return ret; - return sdram_init(priv, ¶ms); + return 0; } #endif static int rk3288_dmc_probe(struct udevice *dev) { +#ifdef CONFIG_SPL_BUILD + struct rk3288_sdram_params *plat = dev_get_platdata(dev); +#endif struct dram_info *priv = dev_get_priv(dev); struct regmap *map; int ret; struct udevice *dev_clk; +#if CONFIG_IS_ENABLED(OF_PLATDATA) + ret = conv_of_platdata(dev); + if (ret) + return ret; +#endif map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_NOC); if (IS_ERR(map)) return PTR_ERR(map); @@ -849,14 +917,12 @@ static int rk3288_dmc_probe(struct udevice *dev) priv->sgrf = syscon_get_first_range(ROCKCHIP_SYSCON_SGRF); priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); - ret = regmap_init_mem(dev, &map); - if (ret) - return ret; - priv->chan[0].pctl = regmap_get_range(map, 0); - priv->chan[0].publ = regmap_get_range(map, 1); - priv->chan[1].pctl = regmap_get_range(map, 2); - priv->chan[1].publ = regmap_get_range(map, 3); - +#ifdef CONFIG_SPL_BUILD + priv->chan[0].pctl = regmap_get_range(plat->map, 0); + priv->chan[0].publ = regmap_get_range(plat->map, 1); + priv->chan[1].pctl = regmap_get_range(plat->map, 2); + priv->chan[1].publ = regmap_get_range(plat->map, 3); +#endif ret = uclass_get_device(UCLASS_CLK, 0, &dev_clk); if (ret) return ret; @@ -898,10 +964,16 @@ static const struct udevice_id rk3288_dmc_ids[] = { }; U_BOOT_DRIVER(dmc_rk3288) = { - .name = "rk3288_dmc", + .name = "rockchip_rk3288_dmc", .id = UCLASS_RAM, .of_match = rk3288_dmc_ids, .ops = &rk3288_dmc_ops, +#ifdef CONFIG_SPL_BUILD + .ofdata_to_platdata = rk3288_dmc_ofdata_to_platdata, +#endif .probe = rk3288_dmc_probe, .priv_auto_alloc_size = sizeof(struct dram_info), +#ifdef CONFIG_SPL_BUILD + .platdata_auto_alloc_size = sizeof(struct rk3288_sdram_params), +#endif }; diff --git a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c index c9f7c4e32f..be4b2b00c3 100644 --- a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c @@ -23,3 +23,41 @@ U_BOOT_DRIVER(syscon_rk3288) = { .id = UCLASS_SYSCON, .of_match = rk3288_syscon_ids, }; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +static int rk3288_syscon_bind_of_platdata(struct udevice *dev) +{ + dev->driver_data = dev->driver->of_match->data; + debug("syscon: %s %d\n", dev->name, (uint)dev->driver_data); + + return 0; +} + +U_BOOT_DRIVER(rockchip_rk3288_noc) = { + .name = "rockchip_rk3288_noc", + .id = UCLASS_SYSCON, + .of_match = rk3288_syscon_ids, + .bind = rk3288_syscon_bind_of_platdata, +}; + +U_BOOT_DRIVER(rockchip_rk3288_grf) = { + .name = "rockchip_rk3288_grf", + .id = UCLASS_SYSCON, + .of_match = rk3288_syscon_ids + 1, + .bind = rk3288_syscon_bind_of_platdata, +}; + +U_BOOT_DRIVER(rockchip_rk3288_sgrf) = { + .name = "rockchip_rk3288_sgrf", + .id = UCLASS_SYSCON, + .of_match = rk3288_syscon_ids + 2, + .bind = rk3288_syscon_bind_of_platdata, +}; + +U_BOOT_DRIVER(rockchip_rk3288_pmu) = { + .name = "rockchip_rk3288_pmu", + .id = UCLASS_SYSCON, + .of_match = rk3288_syscon_ids + 3, + .bind = rk3288_syscon_bind_of_platdata, +}; +#endif diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index a8a90cb7a4..d4c1ee0662 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -10,8 +10,13 @@ config SYS_BOARD config SYS_CPU default "sandbox" +config SANDBOX_SPL + bool "Enable SPL for sandbox" + select SUPPORT_SPL + config SYS_CONFIG_NAME - default "sandbox" + default "sandbox_spl" if SANDBOX_SPL + default "sandbox" if !SANDBOX_SPL config PCI bool "PCI support" diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 16fd6d508a..6d62abb035 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -20,4 +20,9 @@ cmd_u-boot__ = $(CC) -o $@ -Wl,-T u-boot.lds \ -Wl,--start-group $(u-boot-main) -Wl,--end-group \ $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map +cmd_u-boot-spl = (cd $(obj) && $(CC) -o $(SPL_BIN) -Wl,-T u-boot-spl.lds \ + -Wl,--start-group $(patsubst $(obj)/%,%,$(u-boot-spl-main)) \ + $(patsubst $(obj)/%,%,$(u-boot-spl-platdata)) -Wl,--end-group \ + $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot-spl.map -Wl,--gc-sections) + CONFIG_ARCH_DEVICE_TREE := sandbox diff --git a/arch/sandbox/cpu/Makefile b/arch/sandbox/cpu/Makefile index 1b42fee141..db4363358a 100644 --- a/arch/sandbox/cpu/Makefile +++ b/arch/sandbox/cpu/Makefile @@ -8,6 +8,7 @@ # obj-y := cpu.o os.o start.o state.o +obj-$(CONFIG_SPL_BUILD) += spl.o obj-$(CONFIG_ETH_SANDBOX_RAW) += eth-raw-os.o obj-$(CONFIG_SANDBOX_SDL) += sdl.o diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index 196f3e1191..2def72212d 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -4,10 +4,12 @@ */ #define DEBUG #include <common.h> -#include <dm/root.h> +#include <errno.h> +#include <libfdt.h> #include <os.h> #include <asm/io.h> #include <asm/state.h> +#include <dm/root.h> DECLARE_GLOBAL_DATA_PTR; @@ -55,7 +57,7 @@ int cleanup_before_linux_select(int flags) void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) { -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(CONFIG_SPL_BUILD) unsigned long plen = len; void *ptr; diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 8a4d719835..2d63dd88f1 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -541,6 +541,57 @@ int os_jump_to_image(const void *dest, int size) return unlink(fname); } +int os_find_u_boot(char *fname, int maxlen) +{ + struct sandbox_state *state = state_get_current(); + const char *progname = state->argv[0]; + int len = strlen(progname); + char *p; + int fd; + + if (len >= maxlen || len < 4) + return -ENOSPC; + + /* Look for 'u-boot' in the same directory as 'u-boot-spl' */ + strcpy(fname, progname); + if (!strcmp(fname + len - 4, "-spl")) { + fname[len - 4] = '\0'; + fd = os_open(fname, O_RDONLY); + if (fd >= 0) { + close(fd); + return 0; + } + } + + /* Look for 'u-boot' in the parent directory of spl/ */ + p = strstr(fname, "/spl/"); + if (p) { + strcpy(p, p + 4); + fd = os_open(fname, O_RDONLY); + if (fd >= 0) { + close(fd); + return 0; + } + } + + return -ENOENT; +} + +int os_spl_to_uboot(const char *fname) +{ + struct sandbox_state *state = state_get_current(); + char *argv[state->argc + 1]; + int ret; + + memcpy(argv, state->argv, sizeof(char *) * (state->argc + 1)); + argv[0] = (char *)fname; + ret = execv(fname, argv); + if (ret) + return ret; + + return unlink(fname); +} + void os_localtime(struct rtc_time *rt) { time_t t = time(NULL); diff --git a/arch/sandbox/cpu/spl.c b/arch/sandbox/cpu/spl.c new file mode 100644 index 0000000000..e8349c0b93 --- /dev/null +++ b/arch/sandbox/cpu/spl.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Google, Inc + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <os.h> +#include <spl.h> +#include <asm/spl.h> +#include <asm/state.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_init_f(ulong flag) +{ + struct sandbox_state *state = state_get_current(); + + gd->arch.ram_buf = state->ram_buf; + gd->ram_size = state->ram_size; +} + +u32 spl_boot_device(void) +{ + return BOOT_DEVICE_BOARD; +} + +void spl_board_announce_boot_device(void) +{ + char fname[256]; + int ret; + + ret = os_find_u_boot(fname, sizeof(fname)); + if (ret) { + printf("(%s not found, error %d)\n", fname, ret); + return; + } + printf("%s\n", fname); +} + +int spl_board_load_image(void) +{ + char fname[256]; + int ret; + + ret = os_find_u_boot(fname, sizeof(fname)); + if (ret) + return ret; + + /* Hopefully this will not return */ + return os_spl_to_uboot(fname); +} + +void spl_board_init(void) +{ + struct udevice *dev; + + preloader_console_init(); + + /* + * Scan all the devices so that we can output their platform data. See + * sandbox_spl_probe(). + */ + for (uclass_first_device(UCLASS_MISC, &dev); + dev; + uclass_next_device(&dev)) + ; +} diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 969618ef87..6e4ec017cc 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -73,6 +73,7 @@ static int sandbox_cmdline_cb_help(struct sandbox_state *state, const char *arg) } SANDBOX_CMDLINE_OPT_SHORT(help, 'h', 0, "Display help"); +#ifndef CONFIG_SPL_BUILD int sandbox_main_loop_init(void) { struct sandbox_state *state = state_get_current(); @@ -97,6 +98,7 @@ int sandbox_main_loop_init(void) return 0; } +#endif static int sandbox_cmdline_cb_boot(struct sandbox_state *state, const char *arg) diff --git a/arch/sandbox/cpu/u-boot-spl.lds b/arch/sandbox/cpu/u-boot-spl.lds new file mode 100644 index 0000000000..7e92b4ac66 --- /dev/null +++ b/arch/sandbox/cpu/u-boot-spl.lds @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011-2012 The Chromium OS Authors. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +SECTIONS +{ + + . = ALIGN(4); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + __u_boot_sandbox_option_start = .; + _u_boot_sandbox_getopt : { *(.u_boot_sandbox_getopt) } + __u_boot_sandbox_option_end = .; + + __bss_start = .; +} + +INSERT BEFORE .data; diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 2ae40148b0..e6d336f16a 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -172,6 +172,37 @@ }; }; + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + intarray = <2 3 4>; + byteval = [05]; + bytearray = [06]; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10 11]; + stringval = "message"; + stringarray = "multi-word", "message"; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intval = <3>; + intarray = <5>; + byteval = [08]; + bytearray = [01 23 34]; + longbytearray = [09 0a 0b 0c]; + stringval = "message2"; + stringarray = "another", "multi-word", "message"; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + }; + square { compatible = "demo-shape"; colour = "blue"; diff --git a/arch/sandbox/include/asm/spl.h b/arch/sandbox/include/asm/spl.h new file mode 100644 index 0000000000..59f2401170 --- /dev/null +++ b/arch/sandbox/include/asm/spl.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016 Google, Inc + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __asm_spl_h +#define __asm_spl_h + +#define CONFIG_SPL_BOARD_LOAD_IMAGE + +/** + * Board-specific load method for boards that have a special way of loading + * U-Boot, which does not fit with the existing SPL code. + * + * @return 0 on success, negative errno value on failure. + */ +int spl_board_load_image(void); + +enum { + BOOT_DEVICE_BOARD, +}; + +#endif diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile index 96761e27f7..7820c55c85 100644 --- a/arch/sandbox/lib/Makefile +++ b/arch/sandbox/lib/Makefile @@ -8,5 +8,7 @@ # obj-y += interrupts.o +ifndef CONFIG_SPL_BUILD obj-$(CONFIG_PCI) += pci_io.o +endif obj-$(CONFIG_CMD_BOOTM) += bootm.o diff --git a/board/sandbox/MAINTAINERS b/board/sandbox/MAINTAINERS index f5db773a47..4dcbf4ba03 100644 --- a/board/sandbox/MAINTAINERS +++ b/board/sandbox/MAINTAINERS @@ -11,3 +11,10 @@ S: Maintained F: board/sandbox/ F: include/configs/sandbox.h F: configs/sandbox_noblk_defconfig + +SANDBOX SPL BOARD +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: board/sandbox/ +F: include/configs/sandbox_spl.h +F: configs/sandbox_spl_defconfig diff --git a/common/spl/spl.c b/common/spl/spl.c index 840910a684..12aed02935 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -13,7 +13,6 @@ #include <nand.h> #include <fat.h> #include <version.h> -#include <i2c.h> #include <image.h> #include <malloc.h> #include <dm/root.h> @@ -203,7 +202,7 @@ int spl_init(void) gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN; gd->malloc_ptr = 0; #endif - if (CONFIG_IS_ENABLED(OF_CONTROL)) { + if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { ret = fdtdec_setup(); if (ret) { debug("fdtdec_setup() returned error %d\n", ret); @@ -211,7 +210,8 @@ int spl_init(void) } } if (IS_ENABLED(CONFIG_SPL_DM)) { - ret = dm_init_and_scan(true); + /* With CONFIG_OF_PLATDATA, bring in all devices */ + ret = dm_init_and_scan(!CONFIG_IS_ENABLED(OF_PLATDATA)); if (ret) { debug("dm_init_and_scan() returned error %d\n", ret); return ret; diff --git a/configs/firefly-rk3288_defconfig b/configs/firefly-rk3288_defconfig index 4af91206dc..bdafc716aa 100644 --- a/configs/firefly-rk3288_defconfig +++ b/configs/firefly-rk3288_defconfig @@ -69,3 +69,6 @@ CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_USE_TINY_PRINTF=y CONFIG_CMD_DHRYSTONE=y CONFIG_ERRNO_STR=y +CONFIG_SPL_OF_PLATDATA=y +# CONFIG_SPL_OF_LIBFDT is not set +CONFIG_ROCKCHIP_SERIAL=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig new file mode 100644 index 0000000000..0f6dda8c7e --- /dev/null +++ b/configs/sandbox_spl_defconfig @@ -0,0 +1,183 @@ +CONFIG_SYS_MALLOC_F_LEN=0x2000 +CONFIG_MMC=y +CONFIG_SANDBOX_SPL=y +CONFIG_PCI=y +CONFIG_DEFAULT_DEVICE_TREE="sandbox" +CONFIG_I8042_KEYB=y +CONFIG_SPL=y +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_SIGNATURE=y +CONFIG_SPL_LOAD_FIT=y +CONFIG_BOOTSTAGE=y +CONFIG_BOOTSTAGE_REPORT=y +CONFIG_BOOTSTAGE_USER_COUNT=0x20 +CONFIG_BOOTSTAGE_FDT=y +CONFIG_BOOTSTAGE_STASH=y +CONFIG_BOOTSTAGE_STASH_ADDR=0x0 +CONFIG_BOOTSTAGE_STASH_SIZE=0x4096 +CONFIG_CONSOLE_RECORD=y +CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 +CONFIG_HUSH_PARSER=y +CONFIG_CMD_CPU=y +CONFIG_CMD_LICENSE=y +CONFIG_CMD_BOOTZ=y +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_ASKENV=y +CONFIG_CMD_GREPENV=y +CONFIG_LOOPW=y +CONFIG_CMD_MEMTEST=y +CONFIG_CMD_MX_CYCLIC=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_DEMO=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_CMD_I2C=y +CONFIG_CMD_USB=y +CONFIG_CMD_REMOTEPROC=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_TFTPPUT=y +CONFIG_CMD_TFTPSRV=y +CONFIG_CMD_RARP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_MII=y +CONFIG_CMD_PING=y +CONFIG_CMD_CDP=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=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_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_CONTROL=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_OF_HOSTFILE=y +CONFIG_SPL_OF_PLATDATA=y +CONFIG_NETCONSOLE=y +CONFIG_SPL_DM=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_BLK=y +CONFIG_CLK=y +CONFIG_CPU=y +CONFIG_DM_DEMO=y +CONFIG_DM_DEMO_SIMPLE=y +CONFIG_DM_DEMO_SHAPE=y +CONFIG_PM8916_GPIO=y +CONFIG_SANDBOX_GPIO=y +CONFIG_DM_I2C_COMPAT=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_SPL_I2C_MUX=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_CROS_EC_KEYB=y +CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_DM_MAILBOX=y +CONFIG_SANDBOX_MBOX=y +CONFIG_MISC=y +CONFIG_CMD_CROS_EC=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_PWRSEQ=y +CONFIG_SPL_PWRSEQ=y +CONFIG_SYSRESET=y +CONFIG_DM_MMC_OPS=y +CONFIG_SANDBOX_MMC=y +CONFIG_SPI_FLASH_SANDBOX=y +CONFIG_SPI_FLASH=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_DM_PCI=y +CONFIG_DM_PCI_COMPAT=y +CONFIG_PCI_SANDBOX=y +CONFIG_PINCTRL=y +CONFIG_PINCONF=y +CONFIG_ROCKCHIP_PINCTRL=y +CONFIG_ROCKCHIP_3036_PINCTRL=y +CONFIG_PINCTRL_SANDBOX=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_ACT8846=y +CONFIG_DM_PMIC_PFUZE100=y +CONFIG_DM_PMIC_MAX77686=y +CONFIG_PMIC_PM8916=y +CONFIG_PMIC_RK808=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_RK808=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_DM_REGULATOR_SANDBOX=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_RAM=y +CONFIG_REMOTEPROC_SANDBOX=y +CONFIG_DM_RESET=y +CONFIG_SANDBOX_RESET=y +CONFIG_DM_RTC=y +CONFIG_SANDBOX_SERIAL=y +CONFIG_SOUND=y +CONFIG_SOUND_SANDBOX=y +CONFIG_SANDBOX_SPI=y +CONFIG_SPMI=y +CONFIG_SPMI_SANDBOX=y +CONFIG_TIMER=y +CONFIG_TIMER_EARLY=y +CONFIG_SANDBOX_TIMER=y +CONFIG_TPM_TIS_SANDBOX=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EMUL=y +CONFIG_USB_STORAGE=y +CONFIG_USB_KEYBOARD=y +CONFIG_SYS_USB_EVENT_POLL=y +CONFIG_DM_VIDEO=y +CONFIG_CONSOLE_ROTATION=y +CONFIG_CONSOLE_TRUETYPE=y +CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y +CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_CMD_DHRYSTONE=y +CONFIG_TPM=y +CONFIG_LZ4=y +CONFIG_ERRNO_STR=y +CONFIG_UNIT_TEST=y +CONFIG_UT_TIME=y +CONFIG_UT_DM=y +CONFIG_UT_ENV=y diff --git a/doc/driver-model/of-plat.txt b/doc/driver-model/of-plat.txt new file mode 100644 index 0000000000..86e5e25300 --- /dev/null +++ b/doc/driver-model/of-plat.txt @@ -0,0 +1,310 @@ +Driver Model 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_DEVICE() 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 SPL_OF_PLATDATA. This is only available +in SPL and should be tested with: + + #if CONFIG_IS_ENABLED(SPL_OF_PLATDATA) + +A new tool called 'dtoc' converts a device tree file either into a set of +struct declarations, one for each compatible node, or a set of +U_BOOT_DEVICE() declarations along with the actual platform data for each +device. As an example, consider this MMC node: + + 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: + +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_2_cell 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 declaration: + +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 = {{&dtv_clock_controller_at_ff760000, 456}, + {&dtv_clock_controller_at_ff760000, 68}, + {&dtv_clock_controller_at_ff760000, 114}, + {&dtv_clock_controller_at_ff760000, 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_DEVICE(dwmmc_at_ff0c0000) = { + .name = "rockchip_rk3288_dw_mshc", + .platdata = &dtv_dwmmc_at_ff0c0000, + .platdata_size = sizeof(dtv_dwmmc_at_ff0c0000), +}; + +The device is then instantiated at run-time and the platform data can be +accessed using: + + struct udevice *dev; + struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_platdata(dev); + +This avoids the code overhead of converting the device tree data to +platform data in the driver. The ofdata_to_platdata() method should +therefore do nothing in such a driver. + + +Converting of-platdata to a useful form +--------------------------------------- + +Of course it would be possible use the of-platdata directly in your driver +whenever configuration information is required. However this meands 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 +ofdata_to_platdata() 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 platdata struct in its probe() method. The +existing device tree decoding logic should be kept in the +ofdata_to_platdata() method and wrapped with #if. + +For example: + + #include <dt-structs.h> + + struct mmc_platdata { + #if CONFIG_IS_ENABLED(SPL_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_ofdata_to_platdata(struct udevice *dev) + { + #if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA) + /* Decode the device tree data */ + struct mmc_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0); + #endif + + return 0; + } + + static int mmc_probe(struct udevice *dev) + { + struct mmc_platdata *plat = dev_get_platdata(dev); + + #if CONFIG_IS_ENABLED(SPL_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", + .id = UCLASS_MMC, + .of_match = mmc_ids, + .ofdata_to_platdata = mmc_ofdata_to_platdata, + .probe = mmc_probe, + .priv_auto_alloc_size = sizeof(struct mmc_priv), + .platdata_auto_alloc_size = sizeof(struct mmc_platdata), + }; + + +In the case where SPL_OF_PLATDATA is enabled, platdata_auto_alloc_size 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 platdata_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 +ofdata_to_platdata() 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(SPL_OF_PLATDATA). + +The dt-platdata.c file contains the device declarations and is is built in +spl/dt-platdata.c. + +Some phandles (thsoe that are recognised as such) are converted into +points to platform data. This pointer can potentially be used to access the +referenced device (by searching for the pointer value). This feature is not +yet implemented, however. + +The beginnings of a libfdt Python module are provided. So far this only +implements a subset of the features. + +The 'swig' tool is needed to build the libfdt Python module. If this is not +found then the Python model is not used and a fallback is used instead, which +makes use of fdtget. + + +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 +- Complete the phandle feature +- Move to using a full Python libfdt module + +-- +Simon Glass <sjg@chromium.org> +Google, Inc +6/6/16 +Updated Independence Day 2016 diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 6e4d67220a..e0f85677e3 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -10,6 +10,7 @@ #include <clk.h> #include <clk-uclass.h> #include <dm.h> +#include <dt-structs.h> #include <errno.h> DECLARE_GLOBAL_DATA_PTR; @@ -21,6 +22,22 @@ static inline struct clk_ops *clk_dev_ops(struct udevice *dev) #if CONFIG_IS_ENABLED(OF_CONTROL) #ifdef CONFIG_SPL_BUILD +# if CONFIG_IS_ENABLED(OF_PLATDATA) +int clk_get_by_index_platdata(struct udevice *dev, int index, + struct phandle_2_cell *cells, struct clk *clk) +{ + int ret; + + if (index != 0) + return -ENOSYS; + ret = uclass_get_device(UCLASS_CLK, 0, &clk->dev); + if (ret) + return ret; + clk->id = cells[0].id; + + return 0; +} +# else int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { int ret; @@ -39,6 +56,7 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) clk->id = cell[1]; return 0; } +# endif /* OF_PLATDATA */ int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) { @@ -117,8 +135,8 @@ int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) return clk_get_by_index(dev, index, clk); } -#endif -#endif +#endif /* CONFIG_SPL_BUILD */ +#endif /* OF_CONTROL */ int clk_request(struct udevice *dev, struct clk *clk) { diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 797e537907..9c4d2b322f 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -30,9 +30,11 @@ const struct clk_ops clk_fixed_rate_ops = { static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) to_clk_fixed_rate(dev)->fixed_rate = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "clock-frequency", 0); +#endif return 0; } diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index 2285453e8d..679f010bb7 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -7,7 +7,9 @@ #include <common.h> #include <clk-uclass.h> #include <dm.h> +#include <dt-structs.h> #include <errno.h> +#include <mapmem.h> #include <syscon.h> #include <asm/io.h> #include <asm/arch/clock.h> @@ -21,6 +23,12 @@ DECLARE_GLOBAL_DATA_PTR; +struct rk3288_clk_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_cru dtd; +#endif +}; + struct rk3288_clk_priv { struct rk3288_grf *grf; struct rk3288_cru *cru; @@ -783,13 +791,30 @@ static struct clk_ops rk3288_clk_ops = { .set_rate = rk3288_clk_set_rate, }; -static int rk3288_clk_probe(struct udevice *dev) +static int rk3288_clk_ofdata_to_platdata(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3288_clk_priv *priv = dev_get_priv(dev); priv->cru = (struct rk3288_cru *)dev_get_addr(dev); +#endif + + return 0; +} + +static int rk3288_clk_probe(struct udevice *dev) +{ + struct rk3288_clk_priv *priv = dev_get_priv(dev); + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(priv->grf)) + return PTR_ERR(priv->grf); #ifdef CONFIG_SPL_BUILD +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + + priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); +#endif rkclk_init(priv->cru, priv->grf); #endif @@ -813,12 +838,14 @@ static const struct udevice_id rk3288_clk_ids[] = { { } }; -U_BOOT_DRIVER(clk_rk3288) = { - .name = "clk_rk3288", +U_BOOT_DRIVER(rockchip_rk3288_cru) = { + .name = "rockchip_rk3288_cru", .id = UCLASS_CLK, .of_match = rk3288_clk_ids, .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv), + .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat), .ops = &rk3288_clk_ops, .bind = rk3288_clk_bind, + .ofdata_to_platdata = rk3288_clk_ofdata_to_platdata, .probe = rk3288_clk_probe, }; diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 0e56b23fbb..a7f77b4a21 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -112,7 +112,7 @@ int device_unbind(struct udevice *dev) devres_release_all(dev); - if (dev->flags & DM_NAME_ALLOCED) + if (dev->flags & DM_FLAG_NAME_ALLOCED) free((char *)dev->name); free(dev); diff --git a/drivers/core/device.c b/drivers/core/device.c index f7fb0cc0fa..5bb1d7793d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -30,7 +30,7 @@ DECLARE_GLOBAL_DATA_PTR; static int device_bind_common(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, ulong driver_data, int of_offset, - struct udevice **devp) + uint of_platdata_size, struct udevice **devp) { struct udevice *dev; struct uclass *uc; @@ -84,12 +84,29 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, } } - if (!dev->platdata && drv->platdata_auto_alloc_size) { - dev->flags |= DM_FLAG_ALLOC_PDATA; - dev->platdata = calloc(1, drv->platdata_auto_alloc_size); - if (!dev->platdata) { - ret = -ENOMEM; - goto fail_alloc1; + if (drv->platdata_auto_alloc_size) { + bool alloc = !platdata; + + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (of_platdata_size) { + dev->flags |= DM_FLAG_OF_PLATDATA; + if (of_platdata_size < + drv->platdata_auto_alloc_size) + alloc = true; + } + } + if (alloc) { + dev->flags |= DM_FLAG_ALLOC_PDATA; + dev->platdata = calloc(1, + drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail_alloc1; + } + if (CONFIG_IS_ENABLED(OF_PLATDATA) && platdata) { + memcpy(dev->platdata, platdata, + of_platdata_size); + } } } @@ -202,14 +219,14 @@ int device_bind_with_driver_data(struct udevice *parent, struct udevice **devp) { return device_bind_common(parent, drv, name, NULL, driver_data, - of_offset, devp); + of_offset, 0, devp); } int device_bind(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, int of_offset, struct udevice **devp) { - return device_bind_common(parent, drv, name, platdata, 0, of_offset, + return device_bind_common(parent, drv, name, platdata, 0, of_offset, 0, devp); } @@ -217,6 +234,7 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) { struct driver *drv; + uint platdata_size = 0; drv = lists_driver_lookup_name(info->name); if (!drv) @@ -224,8 +242,11 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return -EPERM; - return device_bind(parent, drv, info->name, (void *)info->platdata, - -1, devp); +#if CONFIG_IS_ENABLED(OF_PLATDATA) + platdata_size = info->platdata_size; +#endif + return device_bind_common(parent, drv, info->name, + (void *)info->platdata, 0, -1, platdata_size, devp); } static void *alloc_priv(int size, uint flags) @@ -608,7 +629,7 @@ const char *dev_get_uclass_name(struct udevice *dev) fdt_addr_t dev_get_addr_index(struct udevice *dev, int index) { -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) fdt_addr_t addr; if (CONFIG_IS_ENABLED(OF_TRANSLATE)) { @@ -738,7 +759,7 @@ bool device_is_last_sibling(struct udevice *dev) void device_set_name_alloced(struct udevice *dev) { - dev->flags |= DM_NAME_ALLOCED; + dev->flags |= DM_FLAG_NAME_ALLOCED; } int device_set_name(struct udevice *dev, const char *name) diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 0c27717790..6a634e6951 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -99,7 +99,7 @@ int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, return 0; } -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) /** * driver_check_compatible() - Check if a driver is compatible with this node * diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 519832f173..0299ff0879 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -15,6 +15,49 @@ DECLARE_GLOBAL_DATA_PTR; +static struct regmap *regmap_alloc_count(int count) +{ + struct regmap *map; + + map = malloc(sizeof(struct regmap)); + if (!map) + return NULL; + if (count <= 1) { + map->range = &map->base_range; + } else { + map->range = malloc(count * sizeof(struct regmap_range)); + if (!map->range) { + free(map); + return NULL; + } + } + map->range_count = count; + + return map; +} + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +int regmap_init_mem_platdata(struct udevice *dev, u32 *reg, int count, + struct regmap **mapp) +{ + struct regmap_range *range; + struct regmap *map; + + map = regmap_alloc_count(count); + if (!map) + return -ENOMEM; + + map->base = *reg; + for (range = map->range; count > 0; reg += 2, range++, count--) { + range->start = *reg; + range->size = reg[1]; + } + + *mapp = map; + + return 0; +} +#else int regmap_init_mem(struct udevice *dev, struct regmap **mapp) { const void *blob = gd->fdt_blob; @@ -37,22 +80,11 @@ int regmap_init_mem(struct udevice *dev, struct regmap **mapp) if (!cell || !count) return -EINVAL; - map = malloc(sizeof(struct regmap)); + map = regmap_alloc_count(count); if (!map) return -ENOMEM; - if (count <= 1) { - map->range = &map->base_range; - } else { - map->range = malloc(count * sizeof(struct regmap_range)); - if (!map->range) { - free(map); - return -ENOMEM; - } - } - map->base = fdtdec_get_number(cell, addr_len); - map->range_count = count; for (range = map->range; count > 0; count--, cell += both_len, range++) { @@ -64,6 +96,7 @@ int regmap_init_mem(struct udevice *dev, struct regmap **mapp) return 0; } +#endif void *regmap_get_range(struct regmap *map, unsigned int range_num) { diff --git a/drivers/core/root.c b/drivers/core/root.c index 95886add23..158702406e 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -188,7 +188,7 @@ int dm_scan_platdata(bool pre_reloc_only) return ret; } -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, bool pre_reloc_only) { @@ -244,7 +244,7 @@ int dm_init_and_scan(bool pre_reloc_only) return ret; } - if (CONFIG_IS_ENABLED(OF_CONTROL)) { + if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); if (ret) { debug("dm_scan_fdt() failed: %d\n", ret); diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index e03f46af57..01bd9683a7 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -29,7 +29,20 @@ static int syscon_pre_probe(struct udevice *dev) { struct syscon_uc_info *priv = dev_get_uclass_priv(dev); + /* + * With OF_PLATDATA we really have no way of knowing the format of + * the device-specific platform data. So we assume that it starts with + * a 'reg' member, and this holds a single address and size. Drivers + * using OF_PLATDATA will need to ensure that this is true. + */ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct syscon_base_platdata *plat = dev_get_platdata(dev); + + return regmap_init_mem_platdata(dev, plat->reg, ARRAY_SIZE(plat->reg), + &priv->regmap); +#else return regmap_init_mem(dev, &priv->regmap); +#endif } int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 066639ba1f..fff6f0cdf9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -29,12 +29,19 @@ obj-$(CONFIG_PDSP188x) += pdsp188x.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o ifdef CONFIG_DM_I2C +ifndef CONFIG_SPL_BUILD obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o endif +endif obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o obj-$(CONFIG_STATUS_LED) += status_led.o obj-$(CONFIG_SANDBOX) += swap_case.o +ifdef CONFIG_SPL_OF_PLATDATA +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SANDBOX) += spltest_sandbox.o +endif +endif obj-$(CONFIG_SANDBOX) += syscon_sandbox.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/drivers/misc/spltest_sandbox.c b/drivers/misc/spltest_sandbox.c new file mode 100644 index 0000000000..1fef8252ab --- /dev/null +++ b/drivers/misc/spltest_sandbox.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int sandbox_spl_probe(struct udevice *dev) +{ + struct dtd_sandbox_spl_test *plat = dev_get_platdata(dev); + int i; + + printf("of-platdata probe:\n"); + printf("bool %d\n", plat->boolval); + + printf("byte %02x\n", plat->byteval); + printf("bytearray"); + for (i = 0; i < sizeof(plat->bytearray); i++) + printf(" %02x", plat->bytearray[i]); + printf("\n"); + + printf("int %d\n", plat->intval); + printf("intarray"); + for (i = 0; i < ARRAY_SIZE(plat->intarray); i++) + printf(" %d", plat->intarray[i]); + printf("\n"); + + printf("longbytearray"); + for (i = 0; i < sizeof(plat->longbytearray); i++) + printf(" %02x", plat->longbytearray[i]); + printf("\n"); + + printf("string %s\n", plat->stringval); + printf("stringarray"); + for (i = 0; i < ARRAY_SIZE(plat->stringarray); i++) + printf(" \"%s\"", plat->stringarray[i]); + printf("\n"); + + return 0; +} + +U_BOOT_DRIVER(sandbox_spl_test) = { + .name = "sandbox_spl_test", + .id = UCLASS_MISC, + .flags = DM_FLAG_PRE_RELOC, + .probe = sandbox_spl_probe, +}; diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 691a51557d..020a59b921 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -7,8 +7,10 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <dt-structs.h> #include <dwmmc.h> #include <errno.h> +#include <mapmem.h> #include <pwrseq.h> #include <syscon.h> #include <asm/gpio.h> @@ -19,6 +21,9 @@ DECLARE_GLOBAL_DATA_PTR; struct rockchip_mmc_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_dw_mshc dtplat; +#endif struct mmc_config cfg; struct mmc mmc; }; @@ -26,6 +31,9 @@ struct rockchip_mmc_plat { struct rockchip_dwmmc_priv { struct clk clk; struct dwmci_host host; + int fifo_depth; + bool fifo_mode; + u32 minmax[2]; }; static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) @@ -45,6 +53,7 @@ static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; @@ -61,6 +70,16 @@ static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) else host->dev_index = 1; + priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "fifo-depth", 0); + if (priv->fifo_depth < 0) + return -EINVAL; + priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev->of_offset, + "fifo-mode"); + if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, + "clock-freq-min-max", priv->minmax, 2)) + return -EINVAL; +#endif return 0; } @@ -71,28 +90,34 @@ static int rockchip_dwmmc_probe(struct udevice *dev) struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; struct udevice *pwr_dev __maybe_unused; - u32 minmax[2]; int ret; - int fifo_depth; +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat; + + host->name = dev->name; + host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); + host->buswidth = dtplat->bus_width; + host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; + host->priv = dev; + host->dev_index = 0; + priv->fifo_depth = dtplat->fifo_depth; + priv->fifo_mode = 0; + memcpy(priv->minmax, dtplat->clock_freq_min_max, sizeof(priv->minmax)); + + ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk); + if (ret < 0) + return ret; +#else ret = clk_get_by_index(dev, 0, &priv->clk); if (ret < 0) return ret; - - if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, - "clock-freq-min-max", minmax, 2)) - return -EINVAL; - - fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, - "fifo-depth", 0); - if (fifo_depth < 0) - return -EINVAL; - +#endif host->fifoth_val = MSIZE(0x2) | - RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); + RX_WMARK(priv->fifo_depth / 2 - 1) | + TX_WMARK(priv->fifo_depth / 2); - if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "fifo-mode")) - host->fifo_mode = true; + host->fifo_mode = priv->fifo_mode; #ifdef CONFIG_PWRSEQ /* Enable power if needed */ @@ -105,7 +130,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev) } #endif dwmci_setup_cfg(&plat->cfg, dev->name, host->buswidth, host->caps, - minmax[1], minmax[0]); + priv->minmax[1], priv->minmax[0]); host->mmc = &plat->mmc; host->mmc->priv = &priv->host; host->mmc->dev = dev; @@ -132,7 +157,7 @@ static const struct udevice_id rockchip_dwmmc_ids[] = { }; U_BOOT_DRIVER(rockchip_dwmmc_drv) = { - .name = "rockchip_dwmmc", + .name = "rockchip_rk3288_dw_mshc", .id = UCLASS_MMC, .of_match = rockchip_dwmmc_ids, .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c index 1fa1daa939..8cb3b8228e 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3288.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c @@ -476,6 +476,7 @@ static int rk3288_pinctrl_request(struct udevice *dev, int func, int flags) static int rk3288_pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) u32 cell[3]; int ret; @@ -506,6 +507,7 @@ static int rk3288_pinctrl_get_periph_id(struct udevice *dev, case 103: return PERIPH_ID_HDMI; } +#endif return -ENOENT; } @@ -664,8 +666,12 @@ static struct pinctrl_ops rk3288_pinctrl_ops = { static int rk3288_pinctrl_bind(struct udevice *dev) { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + return 0; +#else /* scan child GPIO banks */ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +#endif } #ifndef CONFIG_SPL_BUILD @@ -719,7 +725,7 @@ static const struct udevice_id rk3288_pinctrl_ids[] = { }; U_BOOT_DRIVER(pinctrl_rk3288) = { - .name = "pinctrl_rk3288", + .name = "rockchip_rk3288_pinctrl", .id = UCLASS_PINCTRL, .of_match = rk3288_pinctrl_ids, .priv_auto_alloc_size = sizeof(struct rk3288_pinctrl_priv), diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 0e3890391b..9ff7234d61 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -312,6 +312,15 @@ config SYS_NS16550 be used. It can be a constant or a function to get clock, eg, get_serial_clock(). +config ROCKCHIP_SERIAL + bool "Rockchip on-chip UART support" + depends on DM_SERIAL && SPL_OF_PLATDATA + help + Select this to enable a debug UART for Rockchip devices when using + CONFIG_OF_PLATDATA (i.e. a compiled-in device tree replacemenmt). + This uses the ns16550 driver, converting the platdata from of-platdata + to the ns16550 format. + config SANDBOX_SERIAL bool "Sandbox UART support" depends on SANDBOX diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 92cbea5913..6986d659ab 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -28,6 +28,9 @@ obj-$(CONFIG_S5P) += serial_s5p.o obj-$(CONFIG_MXC_UART) += serial_mxc.o obj-$(CONFIG_PXA_SERIAL) += serial_pxa.o obj-$(CONFIG_MESON_SERIAL) += serial_meson.o +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_ROCKCHIP_SERIAL) += serial_rockchip.o +endif obj-$(CONFIG_S3C24X0_SERIAL) += serial_s3c24x0.o obj-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o obj-$(CONFIG_SANDBOX_SERIAL) += sandbox.o diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index c6cb3eb500..88fca15357 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -347,7 +347,7 @@ int ns16550_serial_probe(struct udevice *dev) return 0; } -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) int ns16550_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev->platdata; @@ -416,6 +416,7 @@ const struct dm_serial_ops ns16550_serial_ops = { .setbrg = ns16550_serial_setbrg, }; +#if !CONFIG_IS_ENABLED(OF_PLATDATA) #if CONFIG_IS_ENABLED(OF_CONTROL) /* * Please consider existing compatible strings before adding a new @@ -452,4 +453,5 @@ U_BOOT_DRIVER(ns16550_serial) = { .flags = DM_FLAG_PRE_RELOC, }; #endif +#endif /* !OF_PLATDATA */ #endif /* CONFIG_DM_SERIAL */ diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index 58f882b22a..bcc3465312 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -115,7 +115,9 @@ static int sandbox_serial_pending(struct udevice *dev, bool input) return 0; os_usleep(100); +#ifndef CONFIG_SPL_BUILD video_sync_all(); +#endif if (next_index == serial_buf_read) return 1; /* buffer full */ diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 0ce5c44f33..19f38e162e 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -33,7 +33,13 @@ static void serial_find_console_or_panic(void) struct udevice *dev; int node; - if (CONFIG_IS_ENABLED(OF_CONTROL) && blob) { + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + uclass_first_device(UCLASS_SERIAL, &dev); + if (dev) { + gd->cur_serial_dev = dev; + return; + } + } else if (CONFIG_IS_ENABLED(OF_CONTROL) && blob) { /* Check for a chosen console */ node = fdtdec_get_chosen_node(blob, "stdout-path"); if (node < 0) { diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c new file mode 100644 index 0000000000..6bac95a414 --- /dev/null +++ b/drivers/serial/serial_rockchip.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <debug_uart.h> +#include <dm.h> +#include <dt-structs.h> +#include <ns16550.h> +#include <serial.h> +#include <asm/arch/clock.h> + +struct rockchip_uart_platdata { + struct dtd_rockchip_rk3288_uart dtplat; + struct ns16550_platdata plat; +}; + +struct dtd_rockchip_rk3288_uart *dtplat, s_dtplat; + +static int rockchip_serial_probe(struct udevice *dev) +{ + struct rockchip_uart_platdata *plat = dev_get_platdata(dev); + + /* Create some new platform data for the standard driver */ + plat->plat.base = plat->dtplat.reg[0]; + plat->plat.reg_shift = plat->dtplat.reg_shift; + plat->plat.clock = plat->dtplat.clock_frequency; + dev->platdata = &plat->plat; + + return ns16550_serial_probe(dev); +} + +U_BOOT_DRIVER(rockchip_rk3288_uart) = { + .name = "rockchip_rk3288_uart", + .id = UCLASS_SERIAL, + .priv_auto_alloc_size = sizeof(struct NS16550), + .platdata_auto_alloc_size = sizeof(struct rockchip_uart_platdata), + .probe = rockchip_serial_probe, + .ops = &ns16550_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/dts/Kconfig b/dts/Kconfig index c56c1299c0..4b7d8b15cc 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -85,4 +85,25 @@ config OF_SPL_REMOVE_PROPS can be discarded. This option defines the list of properties to discard. +config SPL_OF_PLATDATA + bool "Generate platform data for use in SPL" + depends on SPL_OF_CONTROL + help + For very constrained SPL environments the overhead of decoding + device tree nodes and converting their contents into platform data + is too large. This overhead includes libfdt code as well as the + device tree contents itself. The latter is fairly compact, but the + former can add 3KB or more to a Thumb 2 Image. + + This option enables generation of platform data from the device + tree as C code. This code creates devices using U_BOOT_DEVICE() + declarations. The benefit is that it allows driver code to access + the platform data directly in C structures, avoidin the libfdt + overhead. + + This option works by generating C structure declarations for each + compatible string, then adding platform data and U_BOOT_DEVICE + declarations for each node. See README.platdata for more + information. + endmenu diff --git a/include/clk.h b/include/clk.h index 2f31cf70e3..161bc2825f 100644 --- a/include/clk.h +++ b/include/clk.h @@ -60,6 +60,10 @@ struct clk { }; #if CONFIG_IS_ENABLED(OF_CONTROL) +struct phandle_2_cell; +int clk_get_by_index_platdata(struct udevice *dev, int index, + struct phandle_2_cell *cells, struct clk *clk); + /** * clock_get_by_index - Get/request a clock by integer index. * diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 23a0c40ca5..4de89f8879 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -16,8 +16,10 @@ #endif +#ifndef CONFIG_SPL_BUILD #define CONFIG_IO_TRACE #define CONFIG_CMD_IOTRACE +#endif #ifndef CONFIG_TIMER #define CONFIG_SYS_TIMER_RATE 1000000 @@ -192,6 +194,7 @@ #define CONFIG_CMD_LZMADEC #define CONFIG_CMD_DATE +#ifndef CONFIG_SPL_BUILD #define CONFIG_CMD_IDE #define CONFIG_SYS_IDE_MAXBUS 1 #define CONFIG_SYS_ATA_IDE0_OFFSET 0 @@ -201,6 +204,7 @@ #define CONFIG_SYS_ATA_REG_OFFSET 1 #define CONFIG_SYS_ATA_ALT_OFFSET 2 #define CONFIG_SYS_ATA_STRIDE 4 +#endif #define CONFIG_SCSI #define CONFIG_SCSI_AHCI_PLAT diff --git a/include/configs/sandbox_spl.h b/include/configs/sandbox_spl.h new file mode 100644 index 0000000000..ffc309867a --- /dev/null +++ b/include/configs/sandbox_spl.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016 Google, Inc + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SANDBOX_SPL_CONFIG_H +#define __SANDBOX_SPL_CONFIG_H + +#include <configs/sandbox.h> + +#define CONFIG_SPL_BOARD_INIT + +#define CONFIG_SPL_DRIVERS_MISC_SUPPORT +#define CONFIG_SPL_ENV_SUPPORT +#define CONFIG_SPL_FRAMEWORK +#define CONFIG_SPL_LIBCOMMON_SUPPORT +#define CONFIG_SPL_LIBGENERIC_SUPPORT +#define CONFIG_SPL_SERIAL_SUPPORT + +#endif diff --git a/include/dm/device.h b/include/dm/device.h index 1bfcf3bcbc..c825d47236 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -42,7 +42,9 @@ struct driver_info; #define DM_FLAG_BOUND (1 << 6) /* Device name is allocated and should be freed on unbind() */ -#define DM_NAME_ALLOCED (1 << 7) +#define DM_FLAG_NAME_ALLOCED (1 << 7) + +#define DM_FLAG_OF_PLATDATA (1 << 8) /** * struct udevice - An instance of a driver @@ -553,7 +555,7 @@ int device_set_name(struct udevice *dev, const char *name); /** * device_set_name_alloced() - note that a device name is allocated * - * This sets the DM_NAME_ALLOCED flag for the device, so that when it is + * This sets the DM_FLAG_NAME_ALLOCED flag for the device, so that when it is * unbound the name will be freed. This avoids memory leaks. * * @dev: Device to update diff --git a/include/dm/platdata.h b/include/dm/platdata.h index 6f4f00140e..488b2ab0ae 100644 --- a/include/dm/platdata.h +++ b/include/dm/platdata.h @@ -22,10 +22,15 @@ * * @name: Driver name * @platdata: Driver-specific platform data + * @platdata_size: Size of platform data structure + * @flags: Platform data flags (DM_FLAG_...) */ struct driver_info { const char *name; const void *platdata; +#if CONFIG_IS_ENABLED(OF_PLATDATA) + uint platdata_size; +#endif }; /** diff --git a/include/dt-structs.h b/include/dt-structs.h new file mode 100644 index 0000000000..e13afa6608 --- /dev/null +++ b/include/dt-structs.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DT_STTUCTS +#define __DT_STTUCTS + +/* These structures may only be used in SPL */ +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct phandle_2_cell { + const void *node; + int id; +}; +#include <generated/dt-structs.h> +#endif + +#endif diff --git a/include/os.h b/include/os.h index 954a48c991..1782e50e77 100644 --- a/include/os.h +++ b/include/os.h @@ -287,6 +287,31 @@ int os_read_ram_buf(const char *fname); int os_jump_to_image(const void *dest, int size); /** + * os_find_u_boot() - Determine the path to U-Boot proper + * + * This function is intended to be called from within sandbox SPL. It uses + * a few heuristics to find U-Boot proper. Normally it is either in the same + * directory, or the directory above (since u-boot-spl is normally in an + * spl/ subdirectory when built). + * + * @fname: Place to put full path to U-Boot + * @maxlen: Maximum size of @fname + * @return 0 if OK, -NOSPC if the filename is too large, -ENOENT if not found + */ +int os_find_u_boot(char *fname, int maxlen); + +/** + * os_spl_to_uboot() - Run U-Boot proper + * + * When called from SPL, this runs U-Boot proper. The filename is obtained by + * calling os_find_u_boot(). + * + * @fname: Full pathname to U-Boot executable + * @return 0 if OK, -ve on error + */ +int os_spl_to_uboot(const char *fname); + +/** * Read the current system time * * This reads the current Local Time and places it into the provided diff --git a/include/regmap.h b/include/regmap.h index eccf7707f4..1eed94e47a 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -57,6 +57,22 @@ int regmap_read(struct regmap *map, uint offset, uint *valp); int regmap_init_mem(struct udevice *dev, struct regmap **mapp); /** + * regmap_init_mem_platdata() - Set up a new memory register map for of-platdata + * + * This creates a new regmap with a list of regions passed in, rather than + * using the device tree. It only supports 32-bit machines. + * + * Use regmap_uninit() to free it. + * + * @dev: Device that uses this map + * @reg: List of address, size pairs + * @count: Number of pairs (e.g. 1 if the regmap has a single entry) + * @mapp: Returns allocated map + */ +int regmap_init_mem_platdata(struct udevice *dev, u32 *reg, int count, + struct regmap **mapp); + +/** * regmap_get_range() - Obtain the base memory address of a regmap range * * @map: Regmap to query diff --git a/include/syscon.h b/include/syscon.h index 4593b6e3eb..34842aa470 100644 --- a/include/syscon.h +++ b/include/syscon.h @@ -23,6 +23,17 @@ struct syscon_ops { #define syscon_get_ops(dev) ((struct syscon_ops *)(dev)->driver->ops) +#if CONFIG_IS_ENABLED(OF_PLATDATA) +/* + * We don't support 64-bit machines. If they are so resource-contrained that + * they need to use OF_PLATDATA, something is horribly wrong with the + * education of our hardware engineers. + */ +struct syscon_base_platdata { + u32 reg[2]; +}; +#endif + /** * syscon_get_regmap() - Get access to a register map * diff --git a/lib/Makefile b/lib/Makefile index f48d90103d..f6a8ba1227 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -48,11 +48,10 @@ obj-$(CONFIG_$(SPL_)SHA1) += sha1.o obj-$(CONFIG_$(SPL_)SHA256) += sha256.o obj-$(CONFIG_$(SPL_)OF_LIBFDT) += libfdt/ -ifdef CONFIG_SPL_OF_CONTROL -obj-$(CONFIG_OF_LIBFDT) += libfdt/ -endif +ifneq ($(CONFIG_SPL_BUILD)$(CONFIG_SPL_OF_PLATDATA),yy) obj-$(CONFIG_$(SPL_)OF_CONTROL) += fdtdec_common.o obj-$(CONFIG_$(SPL_)OF_CONTROL) += fdtdec.o +endif ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16.o diff --git a/lib/libfdt/libfdt.swig b/lib/libfdt/libfdt.swig new file mode 100644 index 0000000000..14f583dfbe --- /dev/null +++ b/lib/libfdt/libfdt.swig @@ -0,0 +1,89 @@ +/* File: libfdt.i */ +%module libfdt + +%{ +#define SWIG_FILE_WITH_INIT +#include "libfdt.h" +%} + +%pythoncode %{ +def Raise(errnum): + raise ValueError('Error %s' % fdt_strerror(errnum)) + +def Name(fdt, offset): + name, len = fdt_get_name(fdt, offset) + return name + +def String(fdt, offset): + offset = fdt32_to_cpu(offset) + name = fdt_string(fdt, offset) + return name + +def swap32(x): + return (((x << 24) & 0xFF000000) | + ((x << 8) & 0x00FF0000) | + ((x >> 8) & 0x0000FF00) | + ((x >> 24) & 0x000000FF)) + +def fdt32_to_cpu(x): + return swap32(x) + +def Data(prop): + set_prop(prop) + return get_prop_data() +%} + +%include "typemaps.i" +%include "cstring.i" + +%typemap(in) void* = char*; + +typedef int fdt32_t; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[0]; +}; + +/* + * This is a work-around since I'm not sure of a better way to copy out the + * contents of a string. This is used in dtoc/GetProps(). The intent is to + * pass in a pointer to a property and access the data field at the end of + * it. Ideally the Data() function above would be able to do this directly, + * but I'm not sure how to do that. + */ +#pragma SWIG nowarn=454 +%inline %{ + static struct fdt_property *cur_prop; + + void set_prop(struct fdt_property *prop) { + cur_prop = prop; + } +%} + +%cstring_output_allocate_size(char **s, int *sz, free(*$1)); +%inline %{ + void get_prop_data(char **s, int *sz) { + *sz = fdt32_to_cpu(cur_prop->len); + *s = (char *)malloc(*sz); + if (!*s) + *sz = 0; + else + memcpy(*s, cur_prop + 1, *sz); + } +%} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +int fdt_path_offset(const void *fdt, const char *path); +int fdt_first_property_offset(const void *fdt, int nodeoffset); +int fdt_next_property_offset(const void *fdt, int offset); +const char *fdt_strerror(int errval); +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *OUTPUT); +const char *fdt_get_name(const void *fdt, int nodeoffset, int *OUTPUT); +const char *fdt_string(const void *fdt, int stroffset); +int fdt_first_subnode(const void *fdt, int offset); +int fdt_next_subnode(const void *fdt, int offset); diff --git a/lib/libfdt/setup.py b/lib/libfdt/setup.py new file mode 100644 index 0000000000..62e7bcc1ac --- /dev/null +++ b/lib/libfdt/setup.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +setup.py file for SWIG libfdt +""" + +from distutils.core import setup, Extension +import os +import sys + +# Don't cross-compile - always use the host compiler. +del os.environ['CROSS_COMPILE'] +del os.environ['CC'] + +progname = sys.argv[0] +cflags = sys.argv[1] +files = sys.argv[2:] + +if cflags: + cflags = [flag for flag in cflags.split(' ') if flag] +else: + cflags = None + +libfdt_module = Extension( + '_libfdt', + sources = files, + extra_compile_args = cflags +) + +sys.argv = [progname, '--quiet', 'build_ext', '--inplace'] + +setup (name = 'libfdt', + version = '0.1', + author = "SWIG Docs", + description = """Simple swig libfdt from docs""", + ext_modules = [libfdt_module], + py_modules = ["libfdt"], + ) diff --git a/lib/libfdt/test_libfdt.py b/lib/libfdt/test_libfdt.py new file mode 100644 index 0000000000..14d0da4fb3 --- /dev/null +++ b/lib/libfdt/test_libfdt.py @@ -0,0 +1,14 @@ +#!/usr/bin/python + +import os +import sys + +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(our_path, '../../b/sandbox_spl/tools')) + +import libfdt + +with open('b/sandbox_spl/u-boot.dtb') as fd: + fdt = fd.read() + +print libfdt.fdt_path_offset(fdt, "/aliases") diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index b334f053cc..1aa43aba44 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -185,3 +185,12 @@ int snprintf(char *buf, size_t size, const char *fmt, ...) return ret; } + +void __assert_fail(const char *assertion, const char *file, unsigned line, + const char *function) +{ + /* This will not return */ + printf("%s:%u: %s: Assertion `%s' failed.", file, line, function, + assertion); + hang(); +} diff --git a/scripts/Makefile.host b/scripts/Makefile.host index bff8b5bc61..763a699c4c 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -28,12 +28,16 @@ __hostprogs := $(sort $(hostprogs-y) $(hostprogs-m)) # C code # Executables compiled from a single .c file host-csingle := $(foreach m,$(__hostprogs), \ - $(if $($(m)-objs)$($(m)-cxxobjs),,$(m))) + $(if $($(m)-objs)$($(m)-cxxobjs)$($(m)-sharedobjs),,$(m))) # C executables linked based on several .o files host-cmulti := $(foreach m,$(__hostprogs),\ $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))) +# Shared object libraries +host-shared := $(foreach m,$(__hostprogs),\ + $(if $($(m)-sharedobjs),$(m)))) + # Object (.o) files compiled from .c files host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs))) @@ -59,6 +63,7 @@ host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) host-cxxmulti := $(addprefix $(obj)/,$(host-cxxmulti)) host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs)) +host-shared := $(addprefix $(obj)/,$(host-shared)) host-objdirs := $(addprefix $(obj)/,$(host-objdirs)) obj-dirs += $(host-objdirs) @@ -128,4 +133,4 @@ $(host-cxxobjs): $(obj)/%.o: $(src)/%.cc FORCE $(call if_changed_dep,host-cxxobjs) targets += $(host-csingle) $(host-cmulti) $(host-cobjs)\ - $(host-cxxmulti) $(host-cxxobjs) + $(host-cxxmulti) $(host-cxxobjs) $(host-shared) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 0997fd9fdd..3ba974226b 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -45,6 +45,7 @@ LDFLAGS_FINAL += --gc-sections # FIX ME cpp_flags := $(KBUILD_CPPFLAGS) $(PLATFORM_CPPFLAGS) $(UBOOTINCLUDE) \ $(NOSTDINC_FLAGS) +c_flags := $(KBUILD_CFLAGS) $(cpp_flags) HAVE_VENDOR_COMMON_LIB = $(if $(wildcard $(srctree)/board/$(VENDOR)/common/Makefile),y,n) @@ -76,6 +77,9 @@ endif u-boot-spl-init := $(head-y) u-boot-spl-main := $(libs-y) +ifdef CONFIG_SPL_OF_PLATDATA +u-boot-spl-platdata := $(obj)/dts/dt-platdata.o +endif # Linker Script ifdef CONFIG_SPL_LDSCRIPT @@ -169,7 +173,7 @@ cmd_cat = cat $(filter-out $(PHONY), $^) > $@ quiet_cmd_copy = COPY $@ cmd_copy = cp $< $@ -ifeq ($(CONFIG_SPL_OF_CONTROL)$(CONFIG_OF_SEPARATE),yy) +ifeq ($(CONFIG_SPL_OF_CONTROL)$(CONFIG_OF_SEPARATE)$(CONFIG_SPL_OF_PLATDATA),yy) $(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN)-nodtb.bin $(obj)/$(SPL_BIN)-pad.bin \ $(obj)/$(SPL_BIN).dtb FORCE $(call if_changed,cat) @@ -207,6 +211,32 @@ cmd_cpp_cfg = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \ $(obj)/$(SPL_BIN).cfg: include/config.h FORCE $(call if_changed,cpp_cfg) +pythonpath = PYTHONPATH=tools + +quiet_cmd_dtocc = DTOC C $@ +cmd_dtocc = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb -o $@ platdata + +quiet_cmd_dtoch = DTOC H $@ +cmd_dtoch = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb -o $@ struct + +quiet_cmd_plat = PLAT $@ +cmd_plat = $(CC) $(c_flags) -c $< -o $@ + +$(obj)/dts/dt-platdata.o: $(obj)/dts/dt-platdata.c include/generated/dt-structs.h + $(call if_changed,plat) + +PHONY += dts_dir +dts_dir: + $(shell [ -d $(obj)/dts ] || mkdir -p $(obj)/dts) + +include/generated/dt-structs.h: $(obj)/$(SPL_BIN).dtb dts_dir dtoc + $(call if_changed,dtoch) + +$(obj)/dts/dt-platdata.c: $(obj)/$(SPL_BIN).dtb dts_dir dtoc + $(call if_changed,dtocc) + +dtoc: #$(objtree)/tools/_libfdt.so + ifdef CONFIG_SAMSUNG ifdef CONFIG_VAR_SIZE_SPL VAR_SIZE_PARAM = --vs @@ -241,19 +271,24 @@ cmd_mksunxiboot = $(objtree)/tools/mksunxiboot $< $@ $(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE $(call if_changed,mksunxiboot) -quiet_cmd_u-boot-spl = LD $@ - cmd_u-boot-spl = (cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \ +# Rule to link u-boot-spl +# May be overridden by arch/$(ARCH)/config.mk +quiet_cmd_u-boot-spl ?= LD $@ + cmd_u-boot-spl ?= (cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \ $(patsubst $(obj)/%,%,$(u-boot-spl-init)) --start-group \ - $(patsubst $(obj)/%,%,$(u-boot-spl-main)) --end-group \ + $(patsubst $(obj)/%,%,$(u-boot-spl-main)) \ + $(patsubst $(obj)/%,%,$(u-boot-spl-platdata)) \ + --end-group \ $(PLATFORM_LIBS) -Map $(SPL_BIN).map -o $(SPL_BIN)) -$(obj)/$(SPL_BIN): $(u-boot-spl-init) $(u-boot-spl-main) $(obj)/u-boot-spl.lds FORCE +$(obj)/$(SPL_BIN): $(u-boot-spl-platdata) $(u-boot-spl-init) \ + $(u-boot-spl-main) $(obj)/u-boot-spl.lds FORCE $(call if_changed,u-boot-spl) $(sort $(u-boot-spl-init) $(u-boot-spl-main)): $(u-boot-spl-dirs) ; PHONY += $(u-boot-spl-dirs) -$(u-boot-spl-dirs): +$(u-boot-spl-dirs): $(u-boot-spl-platdata) $(Q)$(MAKE) $(build)=$@ quiet_cmd_cpp_lds = LDS $@ diff --git a/test/py/conftest.py b/test/py/conftest.py index 5b16456e6b..5b3a92316b 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -193,7 +193,7 @@ def pytest_configure(config): for v in env_vars: os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v) - if board_type == 'sandbox': + if board_type.startswith('sandbox'): import u_boot_console_sandbox console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig) else: diff --git a/test/py/tests/test_ofplatdata.py b/test/py/tests/test_ofplatdata.py new file mode 100644 index 0000000000..c8b309e3d2 --- /dev/null +++ b/test/py/tests/test_ofplatdata.py @@ -0,0 +1,42 @@ +# Copyright (c) 2016 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ + +import pytest + +OF_PLATDATA_OUTPUT = ''' +of-platdata probe: +bool 1 +byte 05 +bytearray 06 00 00 +int 1 +intarray 2 3 4 0 +longbytearray 09 0a 0b 0c 0d 0e 0f 10 11 +string message +stringarray "multi-word" "message" "" +of-platdata probe: +bool 0 +byte 08 +bytearray 01 23 34 +int 3 +intarray 5 0 0 0 +longbytearray 09 00 00 00 00 00 00 00 00 +string message2 +stringarray "another" "multi-word" "message" +of-platdata probe: +bool 0 +byte 00 +bytearray 00 00 00 +int 0 +intarray 0 0 0 0 +longbytearray 00 00 00 00 00 00 00 00 00 +string <NULL> +stringarray "one" "" "" +''' + +@pytest.mark.buildconfigspec('spl') +def test_ofplatdata(u_boot_console): + """Test that of-platdata can be generated and used in sandbox""" + cons = u_boot_console + output = cons.get_spawn_output().replace('\r', '') + assert OF_PLATDATA_OUTPUT in output diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py index b5aad7cb94..4606ad48bf 100644 --- a/test/py/u_boot_console_base.py +++ b/test/py/u_boot_console_base.py @@ -345,7 +345,7 @@ class ConsoleBase(object): m = self.p.expect([pattern_u_boot_spl_signon] + self.bad_patterns) if m != 0: - raise Exception('Bad pattern found on console: ' + + raise Exception('Bad pattern found on SPL console: ' + self.bad_pattern_ids[m - 1]) m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) if m != 0: @@ -393,6 +393,16 @@ class ConsoleBase(object): pass self.p = None + def get_spawn_output(self): + """Return the start-up output from U-Boot + + Returns: + The output produced by ensure_spawed(), as a string. + """ + if self.p: + return self.p.get_expect_output() + return None + def validate_version_string_in_text(self, text): """Assert that a command's output includes the U-Boot signon message. diff --git a/test/py/u_boot_console_sandbox.py b/test/py/u_boot_console_sandbox.py index b4404971c4..647e1f879f 100644 --- a/test/py/u_boot_console_sandbox.py +++ b/test/py/u_boot_console_sandbox.py @@ -39,11 +39,15 @@ class ConsoleSandbox(ConsoleBase): A u_boot_spawn.Spawn object that is attached to U-Boot. """ + bcfg = self.config.buildconfig + config_spl = bcfg.get('config_spl', 'n') == 'y' + fname = '/spl/u-boot-spl' if config_spl else '/u-boot' + print fname cmd = [] if self.config.gdbserver: cmd += ['gdbserver', self.config.gdbserver] cmd += [ - self.config.build_dir + '/u-boot', + self.config.build_dir + fname, '-v', '-d', self.config.dtb diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py index d15517389e..3a0fbfad90 100644 --- a/test/py/u_boot_spawn.py +++ b/test/py/u_boot_spawn.py @@ -18,6 +18,9 @@ class Timeout(Exception): class Spawn(object): """Represents the stdio of a freshly created sub-process. Commands may be sent to the process, and responses waited for. + + Members: + output: accumulated output from expect() """ def __init__(self, args, cwd=None): @@ -34,6 +37,7 @@ class Spawn(object): self.waited = False self.buf = '' + self.output = '' self.logfile_read = None self.before = '' self.after = '' @@ -154,6 +158,7 @@ class Spawn(object): posafter = earliest_m.end() self.before = self.buf[:pos] self.after = self.buf[pos:posafter] + self.output += self.buf[:posafter] self.buf = self.buf[posafter:] return earliest_pi tnow_s = time.time() @@ -198,3 +203,11 @@ class Spawn(object): if not self.isalive(): break time.sleep(0.1) + + def get_expect_output(self): + """Return the output read by expect() + + Returns: + The output processed by expect(), as a string. + """ + return self.output diff --git a/tools/Makefile b/tools/Makefile index f72294a98a..421414bc15 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -107,6 +107,20 @@ mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o +# Build a libfdt Python module if swig is available +# Use 'sudo apt-get install swig libpython-dev' to enable this +hostprogs-$(CONFIG_SPL_OF_PLATDATA) += \ + $(if $(shell which swig),_libfdt.so) +_libfdt.so-sharedobjs += $(LIBFDT_OBJS) +libfdt: + +tools/_libfdt.so: $(patsubst %.o,%.c,$(LIBFDT_OBJS)) tools/libfdt_wrap.c + python $(srctree)/lib/libfdt/setup.py "$(_hostc_flags)" $^ + mv _libfdt.so $@ + +tools/libfdt_wrap.c: $(srctree)/lib/libfdt/libfdt.swig + swig -python -o $@ $< + # TODO(sjg@chromium.org): Is this correct on Mac OS? ifneq ($(CONFIG_MX23)$(CONFIG_MX28),) diff --git a/tools/dtoc/.gitignore b/tools/dtoc/.gitignore new file mode 100644 index 0000000000..0d20b6487c --- /dev/null +++ b/tools/dtoc/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/tools/dtoc/dtoc b/tools/dtoc/dtoc new file mode 120000 index 0000000000..896ca44e62 --- /dev/null +++ b/tools/dtoc/dtoc @@ -0,0 +1 @@ +dtoc.py
\ No newline at end of file diff --git a/tools/dtoc/dtoc.py b/tools/dtoc/dtoc.py new file mode 100755 index 0000000000..ec80abe717 --- /dev/null +++ b/tools/dtoc/dtoc.py @@ -0,0 +1,394 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import copy +from optparse import OptionError, OptionParser +import os +import sys + +import fdt_util + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(our_path, '../patman')) + +# Bring in either the normal fdt library (which relies on libfdt) or the +# fallback one (which uses fdtget and is slower). Both provide the same +# interfface for this file to use. +try: + from fdt import Fdt + import fdt + have_libfdt = True +except ImportError: + have_libfdt = False + from fdt_fallback import Fdt + import fdt_fallback as fdt + +import struct + +# When we see these properties we ignore them - i.e. do not create a structure member +PROP_IGNORE_LIST = [ + '#address-cells', + '#gpio-cells', + '#size-cells', + 'compatible', + 'linux,phandle', + "status", + 'phandle', + 'u-boot,dm-pre-reloc', +] + +# C type declarations for the tyues we support +TYPE_NAMES = { + fdt_util.TYPE_INT: 'fdt32_t', + fdt_util.TYPE_BYTE: 'unsigned char', + fdt_util.TYPE_STRING: 'const char *', + fdt_util.TYPE_BOOL: 'bool', +}; + +STRUCT_PREFIX = 'dtd_' +VAL_PREFIX = 'dtv_' + +def Conv_name_to_c(name): + """Convert a device-tree name to a C identifier + + Args: + name: Name to convert + Return: + String containing the C version of this name + """ + str = name.replace('@', '_at_') + str = str.replace('-', '_') + str = str.replace(',', '_') + str = str.replace('/', '__') + return str + +def TabTo(num_tabs, str): + if len(str) >= num_tabs * 8: + return str + ' ' + return str + '\t' * (num_tabs - len(str) / 8) + +class DtbPlatdata: + """Provide a means to convert device tree binary data to platform data + + The output of this process is C structures which can be used in space- + constrained encvironments where the ~3KB code overhead of device tree + code is not affordable. + + Properties: + fdt: Fdt object, referencing the device tree + _dtb_fname: Filename of the input device tree binary file + _valid_nodes: A list of Node object with compatible strings + _options: Command-line options + _phandle_node: A dict of nodes indexed by phandle number (1, 2...) + _outfile: The current output file (sys.stdout or a real file) + _lines: Stashed list of output lines for outputting in the future + _phandle_node: A dict of Nodes indexed by phandle (an integer) + """ + def __init__(self, dtb_fname, options): + self._dtb_fname = dtb_fname + self._valid_nodes = None + self._options = options + self._phandle_node = {} + self._outfile = None + self._lines = [] + + def SetupOutput(self, fname): + """Set up the output destination + + Once this is done, future calls to self.Out() will output to this + file. + + Args: + fname: Filename to send output to, or '-' for stdout + """ + if fname == '-': + self._outfile = sys.stdout + else: + self._outfile = open(fname, 'w') + + def Out(self, str): + """Output a string to the output file + + Args: + str: String to output + """ + self._outfile.write(str) + + def Buf(self, str): + """Buffer up a string to send later + + Args: + str: String to add to our 'buffer' list + """ + self._lines.append(str) + + def GetBuf(self): + """Get the contents of the output buffer, and clear it + + Returns: + The output buffer, which is then cleared for future use + """ + lines = self._lines + self._lines = [] + return lines + + def GetValue(self, type, value): + """Get a value as a C expression + + For integers this returns a byte-swapped (little-endian) hex string + For bytes this returns a hex string, e.g. 0x12 + For strings this returns a literal string enclosed in quotes + For booleans this return 'true' + + Args: + type: Data type (fdt_util) + value: Data value, as a string of bytes + """ + if type == fdt_util.TYPE_INT: + return '%#x' % fdt_util.fdt32_to_cpu(value) + elif type == fdt_util.TYPE_BYTE: + return '%#x' % ord(value[0]) + elif type == fdt_util.TYPE_STRING: + return '"%s"' % value + elif type == fdt_util.TYPE_BOOL: + return 'true' + + def GetCompatName(self, node): + """Get a node's first compatible string as a C identifier + + Args: + node: Node object to check + Return: + C identifier for the first compatible string + """ + compat = node.props['compatible'].value + if type(compat) == list: + compat = compat[0] + return Conv_name_to_c(compat) + + def ScanDtb(self): + """Scan the device tree to obtain a tree of notes and properties + + Once this is done, self.fdt.GetRoot() can be called to obtain the + device tree root node, and progress from there. + """ + self.fdt = Fdt(self._dtb_fname) + self.fdt.Scan() + + def ScanTree(self): + """Scan the device tree for useful information + + This fills in the following properties: + _phandle_node: A dict of Nodes indexed by phandle (an integer) + _valid_nodes: A list of nodes we wish to consider include in the + platform data + """ + node_list = [] + self._phandle_node = {} + for node in self.fdt.GetRoot().subnodes: + if 'compatible' in node.props: + status = node.props.get('status') + if (not options.include_disabled and not status or + status.value != 'disabled'): + node_list.append(node) + phandle_prop = node.props.get('phandle') + if phandle_prop: + phandle = phandle_prop.GetPhandle() + self._phandle_node[phandle] = node + + self._valid_nodes = node_list + + def IsPhandle(self, prop): + """Check if a node contains phandles + + We have no reliable way of detecting whether a node uses a phandle + or not. As an interim measure, use a list of known property names. + + Args: + prop: Prop object to check + Return: + True if the object value contains phandles, else False + """ + if prop.name in ['clocks']: + return True + return False + + def ScanStructs(self): + """Scan the device tree building up the C structures we will use. + + Build a dict keyed by C struct name containing a dict of Prop + object for each struct field (keyed by property name). Where the + same struct appears multiple times, try to use the 'widest' + property, i.e. the one with a type which can express all others. + + Once the widest property is determined, all other properties are + updated to match that width. + """ + structs = {} + for node in self._valid_nodes: + node_name = self.GetCompatName(node) + fields = {} + + # Get a list of all the valid properties in this node. + for name, prop in node.props.iteritems(): + if name not in PROP_IGNORE_LIST and name[0] != '#': + fields[name] = copy.deepcopy(prop) + + # If we've seen this node_name before, update the existing struct. + if node_name in structs: + struct = structs[node_name] + for name, prop in fields.iteritems(): + oldprop = struct.get(name) + if oldprop: + oldprop.Widen(prop) + else: + struct[name] = prop + + # Otherwise store this as a new struct. + else: + structs[node_name] = fields + + upto = 0 + for node in self._valid_nodes: + node_name = self.GetCompatName(node) + struct = structs[node_name] + for name, prop in node.props.iteritems(): + if name not in PROP_IGNORE_LIST and name[0] != '#': + prop.Widen(struct[name]) + upto += 1 + return structs + + def GenerateStructs(self, structs): + """Generate struct defintions for the platform data + + This writes out the body of a header file consisting of structure + definitions for node in self._valid_nodes. See the documentation in + README.of-plat for more information. + """ + self.Out('#include <stdbool.h>\n') + self.Out('#include <libfdt.h>\n') + + # Output the struct definition + for name in sorted(structs): + self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name)); + for pname in sorted(structs[name]): + prop = structs[name][pname] + if self.IsPhandle(prop): + # For phandles, include a reference to the target + self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'), + Conv_name_to_c(prop.name), + len(prop.value) / 2)) + else: + ptype = TYPE_NAMES[prop.type] + self.Out('\t%s%s' % (TabTo(2, ptype), + Conv_name_to_c(prop.name))) + if type(prop.value) == list: + self.Out('[%d]' % len(prop.value)) + self.Out(';\n') + self.Out('};\n') + + def GenerateTables(self): + """Generate device defintions for the platform data + + This writes out C platform data initialisation data and + U_BOOT_DEVICE() declarations for each valid node. See the + documentation in README.of-plat for more information. + """ + self.Out('#include <common.h>\n') + self.Out('#include <dm.h>\n') + self.Out('#include <dt-structs.h>\n') + self.Out('\n') + node_txt_list = [] + for node in self._valid_nodes: + struct_name = self.GetCompatName(node) + var_name = Conv_name_to_c(node.name) + self.Buf('static struct %s%s %s%s = {\n' % + (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + for pname, prop in node.props.iteritems(): + if pname in PROP_IGNORE_LIST or pname[0] == '#': + continue + ptype = TYPE_NAMES[prop.type] + member_name = Conv_name_to_c(prop.name) + self.Buf('\t%s= ' % TabTo(3, '.' + member_name)) + + # Special handling for lists + if type(prop.value) == list: + self.Buf('{') + vals = [] + # For phandles, output a reference to the platform data + # of the target node. + if self.IsPhandle(prop): + # Process the list as pairs of (phandle, id) + it = iter(prop.value) + for phandle_cell, id_cell in zip(it, it): + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + id = fdt_util.fdt32_to_cpu(id_cell) + target_node = self._phandle_node[phandle] + name = Conv_name_to_c(target_node.name) + vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id)) + else: + for val in prop.value: + vals.append(self.GetValue(prop.type, val)) + self.Buf(', '.join(vals)) + self.Buf('}') + else: + self.Buf(self.GetValue(prop.type, prop.value)) + self.Buf(',\n') + self.Buf('};\n') + + # Add a device declaration + self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name) + self.Buf('\t.name\t\t= "%s",\n' % struct_name) + self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) + self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' % + (VAL_PREFIX, var_name)) + self.Buf('};\n') + self.Buf('\n') + + # Output phandle target nodes first, since they may be referenced + # by others + if 'phandle' in node.props: + self.Out(''.join(self.GetBuf())) + else: + node_txt_list.append(self.GetBuf()) + + # Output all the nodes which are not phandle targets themselves, but + # may reference them. This avoids the need for forward declarations. + for node_txt in node_txt_list: + self.Out(''.join(node_txt)) + + +if __name__ != "__main__": + pass + +parser = OptionParser() +parser.add_option('-d', '--dtb-file', action='store', + help='Specify the .dtb input file') +parser.add_option('--include-disabled', action='store_true', + help='Include disabled nodes') +parser.add_option('-o', '--output', action='store', default='-', + help='Select output filename') +(options, args) = parser.parse_args() + +if not args: + raise ValueError('Please specify a command: struct, platdata') + +plat = DtbPlatdata(options.dtb_file, options) +plat.ScanDtb() +plat.ScanTree() +plat.SetupOutput(options.output) +structs = plat.ScanStructs() + +for cmd in args[0].split(','): + if cmd == 'struct': + plat.GenerateStructs(structs) + elif cmd == 'platdata': + plat.GenerateTables() + else: + raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd) diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py new file mode 100644 index 0000000000..1d913a925e --- /dev/null +++ b/tools/dtoc/fdt.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import fdt_util +import libfdt +import sys + +# This deals with a device tree, presenting it as a list of Node and Prop +# objects, representing nodes and properties, respectively. +# +# This implementation uses a libfdt Python library to access the device tree, +# so it is fairly efficient. + +class Prop: + """A device tree property + + Properties: + name: Property name (as per the device tree) + value: Property value as a string of bytes, or a list of strings of + bytes + type: Value type + """ + def __init__(self, name, bytes): + self.name = name + self.value = None + if not bytes: + self.type = fdt_util.TYPE_BOOL + self.value = True + return + self.type, self.value = fdt_util.BytesToValue(bytes) + + def GetPhandle(self): + """Get a (single) phandle value from a property + + Gets the phandle valuie from a property and returns it as an integer + """ + return fdt_util.fdt32_to_cpu(self.value[:4]) + + def Widen(self, newprop): + """Figure out which property type is more general + + Given a current property and a new property, this function returns the + one that is less specific as to type. The less specific property will + be ble to represent the data in the more specific property. This is + used for things like: + + node1 { + compatible = "fred"; + value = <1>; + }; + node1 { + compatible = "fred"; + value = <1 2>; + }; + + He we want to use an int array for 'value'. The first property + suggests that a single int is enough, but the second one shows that + it is not. Calling this function with these two propertes would + update the current property to be like the second, since it is less + specific. + """ + if newprop.type < self.type: + self.type = newprop.type + + if type(newprop.value) == list and type(self.value) != list: + self.value = [self.value] + + if type(self.value) == list and len(newprop.value) > len(self.value): + val = fdt_util.GetEmpty(self.type) + while len(self.value) < len(newprop.value): + self.value.append(val) + + +class Node: + """A device tree node + + Properties: + offset: Integer offset in the device tree + name: Device tree node tname + path: Full path to node, along with the node name itself + _fdt: Device tree object + subnodes: A list of subnodes for this node, each a Node object + props: A dict of properties for this node, each a Prop object. + Keyed by property name + """ + def __init__(self, fdt, offset, name, path): + self.offset = offset + self.name = name + self.path = path + self._fdt = fdt + self.subnodes = [] + self.props = {} + + def Scan(self): + """Scan a node's properties and subnodes + + This fills in the props and subnodes properties, recursively + searching into subnodes so that the entire tree is built. + """ + self.props = self._fdt.GetProps(self.path) + + offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.offset) + while offset >= 0: + sep = '' if self.path[-1] == '/' else '/' + name = libfdt.Name(self._fdt.GetFdt(), offset) + path = self.path + sep + name + node = Node(self._fdt, offset, name, path) + self.subnodes.append(node) + + node.Scan() + offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset) + + +class Fdt: + """Provides simple access to a flat device tree blob. + + Properties: + fname: Filename of fdt + _root: Root of device tree (a Node object) + """ + + def __init__(self, fname): + self.fname = fname + with open(fname) as fd: + self._fdt = fd.read() + + def GetFdt(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a string of bytes + """ + return self._fdt + + def Scan(self): + """Scan a device tree, building up a tree of Node objects + + This fills in the self._root property + """ + self._root = Node(self, 0, '/', '/') + self._root.Scan() + + def GetRoot(self): + """Get the root Node of the device tree + + Returns: + The root Node object + """ + return self._root + + def GetProps(self, node): + """Get all properties from a node. + + Args: + node: Full path to node name to look in. + + Returns: + A dictionary containing all the properties, indexed by node name. + The entries are Prop objects. + + Raises: + ValueError: if the node does not exist. + """ + offset = libfdt.fdt_path_offset(self._fdt, node) + if offset < 0: + libfdt.Raise(offset) + props_dict = {} + poffset = libfdt.fdt_first_property_offset(self._fdt, offset) + while poffset >= 0: + dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset) + prop = Prop(libfdt.String(self._fdt, dprop.nameoff), libfdt.Data(dprop)) + props_dict[prop.name] = prop + + poffset = libfdt.fdt_next_property_offset(self._fdt, poffset) + return props_dict diff --git a/tools/dtoc/fdt_fallback.py b/tools/dtoc/fdt_fallback.py new file mode 100644 index 0000000000..14decf394d --- /dev/null +++ b/tools/dtoc/fdt_fallback.py @@ -0,0 +1,207 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import command +import fdt_util +import sys + +# This deals with a device tree, presenting it as a list of Node and Prop +# objects, representing nodes and properties, respectively. +# +# This implementation uses the fdtget tool to access the device tree, so it +# is not very efficient for larger trees. The tool is called once for each +# node and property in the tree. + +class Prop: + """A device tree property + + Properties: + name: Property name (as per the device tree) + value: Property value as a string of bytes, or a list of strings of + bytes + type: Value type + """ + def __init__(self, name, byte_list_str): + self.name = name + self.value = None + if not byte_list_str.strip(): + self.type = fdt_util.TYPE_BOOL + return + bytes = [chr(int(byte, 16)) for byte in byte_list_str.strip().split(' ')] + self.type, self.value = fdt_util.BytesToValue(''.join(bytes)) + + def GetPhandle(self): + """Get a (single) phandle value from a property + + Gets the phandle valuie from a property and returns it as an integer + """ + return fdt_util.fdt32_to_cpu(self.value[:4]) + + def Widen(self, newprop): + """Figure out which property type is more general + + Given a current property and a new property, this function returns the + one that is less specific as to type. The less specific property will + be ble to represent the data in the more specific property. This is + used for things like: + + node1 { + compatible = "fred"; + value = <1>; + }; + node1 { + compatible = "fred"; + value = <1 2>; + }; + + He we want to use an int array for 'value'. The first property + suggests that a single int is enough, but the second one shows that + it is not. Calling this function with these two propertes would + update the current property to be like the second, since it is less + specific. + """ + if newprop.type < self.type: + self.type = newprop.type + + if type(newprop.value) == list and type(self.value) != list: + self.value = newprop.value + +class Node: + """A device tree node + + Properties: + name: Device tree node tname + path: Full path to node, along with the node name itself + _fdt: Device tree object + subnodes: A list of subnodes for this node, each a Node object + props: A dict of properties for this node, each a Prop object. + Keyed by property name + """ + def __init__(self, fdt, name, path): + self.name = name + self.path = path + self._fdt = fdt + self.subnodes = [] + self.props = {} + + def Scan(self): + """Scan a node's properties and subnodes + + This fills in the props and subnodes properties, recursively + searching into subnodes so that the entire tree is built. + """ + for name, byte_list_str in self._fdt.GetProps(self.path).iteritems(): + prop = Prop(name, byte_list_str) + self.props[name] = prop + + for name in self._fdt.GetSubNodes(self.path): + sep = '' if self.path[-1] == '/' else '/' + path = self.path + sep + name + node = Node(self._fdt, name, path) + self.subnodes.append(node) + + node.Scan() + + +class Fdt: + """Provides simple access to a flat device tree blob. + + Properties: + fname: Filename of fdt + _root: Root of device tree (a Node object) + """ + + def __init__(self, fname): + self.fname = fname + + def Scan(self): + """Scan a device tree, building up a tree of Node objects + + This fills in the self._root property + """ + self._root = Node(self, '/', '/') + self._root.Scan() + + def GetRoot(self): + """Get the root Node of the device tree + + Returns: + The root Node object + """ + return self._root + + def GetSubNodes(self, node): + """Returns a list of sub-nodes of a given node + + Args: + node: Node name to return children from + + Returns: + List of children in the node (each a string node name) + + Raises: + CmdError: if the node does not exist. + """ + out = command.Output('fdtget', self.fname, '-l', node) + return out.strip().splitlines() + + def GetProps(self, node, convert_dashes=False): + """Get all properties from a node + + Args: + node: full path to node name to look in + convert_dashes: True to convert - to _ in node names + + Returns: + A dictionary containing all the properties, indexed by node name. + The entries are simply strings - no decoding of lists or numbers + is done. + + Raises: + CmdError: if the node does not exist. + """ + out = command.Output('fdtget', self.fname, node, '-p') + props = out.strip().splitlines() + props_dict = {} + for prop in props: + name = prop + if convert_dashes: + prop = re.sub('-', '_', prop) + props_dict[prop] = self.GetProp(node, name) + return props_dict + + def GetProp(self, node, prop, default=None, typespec=None): + """Get a property from a device tree. + + This looks up the given node and property, and returns the value as a + string, + + If the node or property does not exist, this will return the default + value. + + Args: + node: Full path to node to look up. + prop: Property name to look up. + default: Default value to return if nothing is present in the fdt, + or None to raise in this case. This will be converted to a + string. + typespec: Type character to use (None for default, 's' for string) + + Returns: + string containing the property value. + + Raises: + CmdError: if the property does not exist and no default is provided. + """ + args = [self.fname, node, prop, '-t', 'bx'] + if default is not None: + args += ['-d', str(default)] + if typespec is not None: + args += ['-t%s' % typespec] + out = command.Output('fdtget', *args) + return out.strip() diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py new file mode 100644 index 0000000000..929b524fcf --- /dev/null +++ b/tools/dtoc/fdt_util.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import struct + +# A list of types we support +(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4) + +def BytesToValue(bytes): + """Converts a string of bytes into a type and value + + Args: + A string containing bytes + + Return: + A tuple: + Type of data + Data, either a single element or a list of elements. Each element + is one of: + TYPE_STRING: string value from the property + TYPE_INT: a byte-swapped integer stored as a 4-byte string + TYPE_BYTE: a byte stored as a single-byte string + """ + size = len(bytes) + strings = bytes.split('\0') + is_string = True + count = len(strings) - 1 + if count > 0 and not strings[-1]: + for string in strings[:-1]: + if not string: + is_string = False + break + for ch in string: + if ch < ' ' or ch > '~': + is_string = False + break + else: + is_string = False + if is_string: + if count == 1: + return TYPE_STRING, strings[0] + else: + return TYPE_STRING, strings[:-1] + if size % 4: + if size == 1: + return TYPE_BYTE, bytes[0] + else: + return TYPE_BYTE, list(bytes) + val = [] + for i in range(0, size, 4): + val.append(bytes[i:i + 4]) + if size == 4: + return TYPE_INT, val[0] + else: + return TYPE_INT, val + +def GetEmpty(type): + """Get an empty / zero value of the given type + + Returns: + A single value of the given type + """ + if type == TYPE_BYTE: + return chr(0) + elif type == TYPE_INT: + return struct.pack('<I', 0); + elif type == TYPE_STRING: + return '' + else: + return True + +def fdt32_to_cpu(val): + """Convert a device tree cell to an integer + + Args: + Value to convert (4-character string representing the cell value) + + Return: + A native-endian integer value + """ + return struct.unpack(">I", val)[0] |