From patchwork Thu Jul 2 21:10:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 240666 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Thu, 2 Jul 2020 15:10:02 -0600 Subject: [RFC PATCH v2 1/3] dm: Driver and uclass changes for tiny-dm In-Reply-To: <20200702211004.1491489-1-sjg@chromium.org> References: <20200702211004.1491489-1-sjg@chromium.org> Message-ID: <20200702211004.1491489-2-sjg@chromium.org> This includes various changes to support tiny-dm in serial, ram, clock, spi, SPI flash, syscon and sysreset drivers. Signed-off-by: Simon Glass --- (no changes since v1) drivers/clk/Kconfig | 54 +++++ drivers/clk/Makefile | 4 +- drivers/clk/clk-uclass.c | 53 ++++- drivers/clk/rockchip/clk_rk3288.c | 106 +++++++--- drivers/mtd/spi/Kconfig | 18 ++ drivers/mtd/spi/sf-uclass.c | 76 +++++++ drivers/mtd/spi/sf_probe.c | 4 + drivers/mtd/spi/spi-nor-tiny.c | 166 +++++++++++++-- drivers/ram/Kconfig | 18 ++ drivers/ram/ram-uclass.c | 12 ++ drivers/ram/rockchip/sdram_rk3188.c | 2 +- drivers/ram/rockchip/sdram_rk322x.c | 2 +- drivers/ram/rockchip/sdram_rk3288.c | 231 ++++++++++++-------- drivers/ram/rockchip/sdram_rk3328.c | 2 +- drivers/ram/rockchip/sdram_rk3399.c | 2 +- drivers/reset/reset-rockchip.c | 4 +- drivers/serial/Kconfig | 38 ++++ drivers/serial/ns16550.c | 195 +++++++++++++++-- drivers/serial/sandbox.c | 59 ++++-- drivers/serial/serial-uclass.c | 77 +++++++ drivers/serial/serial_omap.c | 2 +- drivers/serial/serial_rockchip.c | 59 ++++++ drivers/spi/Kconfig | 18 ++ drivers/spi/Makefile | 2 + drivers/spi/rk_spi.c | 301 +++++++++++++++++++-------- drivers/spi/spi-uclass.c | 77 +++++++ drivers/sysreset/Kconfig | 18 ++ drivers/sysreset/sysreset-uclass.c | 124 ++++++----- drivers/sysreset/sysreset_rockchip.c | 61 +++++- include/asm-generic/global_data.h | 7 +- include/clk-uclass.h | 11 + include/clk.h | 32 ++- include/linux/mtd/mtd.h | 23 +- include/linux/mtd/spi-nor.h | 22 ++ include/log.h | 6 + include/malloc.h | 3 + include/ns16550.h | 7 +- include/ram.h | 25 +++ include/regmap.h | 4 +- include/serial.h | 45 +++- include/spi.h | 31 +++ include/spi_flash.h | 7 + include/spl.h | 8 +- include/syscon.h | 2 + include/sysreset.h | 9 + 45 files changed, 1682 insertions(+), 345 deletions(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 8b8b719999..4762505a5b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -10,6 +10,19 @@ config CLK feed into other clocks in a tree structure, with multiplexers to choose the source for each clock. +config CLK_FIXED_RATE + bool "Enable fixed-rate clock support" + depends on CLK + default y + help + This enables support for a simple fixed-rate clock. The rate + is provided by the device tree and is set up when the device is + probed. Obviously the clock rate cannot be changed after the device + is set up. + + This also enables a clock with a fixed factor (divider and + multipler) of its parent clock. + config SPL_CLK bool "Enable clock support in SPL" depends on CLK && SPL && SPL_DM @@ -20,6 +33,28 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +config SPL_CLK_FIXED_RATE + bool "Enable fixed-rate clock support in SPL" + depends on CLK_FIXED_RATE + default y + help + This enables support for a simple fixed-rate clock in SPL. The rate + is provided by the device tree and is set up when the device is + probed. Obviously the clock rate cannot be changed after the device + is set up. + + This also enables a clock with a fixed factor (divider and + multipler) of its parent clock. + +config SPL_TINY_CLK + bool "Support tiny clock drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config TPL_CLK bool "Enable clock support in TPL" depends on CLK && TPL_DM @@ -30,6 +65,25 @@ config TPL_CLK setting up clocks within TPL, and allows the same drivers to be used as U-Boot proper. +config TPL_CLK_FIXED_RATE + bool "Enable fixed-rate clock support in TPL" + depends on TPL_CLK + default y + help + This enables support for a simple fixed-rate clock in TPL. The rate + is provided by the device tree and is set up when the device is + probed. Obviously the clock rate cannot be changed after the device + is set up. + +config TPL_TINY_CLK + bool "Support tiny clock drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config CLK_BCM6345 bool "Clock controller driver for BCM6345" depends on CLK && ARCH_BMIPS diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e01783391d..8704fece92 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -5,8 +5,8 @@ # obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o -obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o -obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o +obj-$(CONFIG_$(SPL_TPL_)CLK_FIXED_RATE) += clk_fixed_rate.o +obj-$(CONFIG_$(SPL_TPL_)CLK_FIXED_RATE) += clk_fixed_factor.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index d2a381490e..e702209126 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -27,6 +27,7 @@ static inline const struct clk_ops *clk_dev_ops(struct udevice *dev) #if CONFIG_IS_ENABLED(OF_CONTROL) # if CONFIG_IS_ENABLED(OF_PLATDATA) +# if !CONFIG_IS_ENABLED(TINY_CLK) int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells, struct clk *clk) { @@ -40,6 +41,21 @@ int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells, return 0; } +# else /* TINY CLK */ +int tiny_clk_get_by_driver_info(struct phandle_1_arg *cells, + struct tiny_clk *tclk) +{ + struct tinydev *tdev; + + tdev = tiny_dev_get(UCLASS_CLK, 0); + if (!tdev) + return -ENODEV; + tclk->tdev = tdev; + tclk->id = cells->arg[0]; + + return 0; +} +# endif # else static int clk_of_xlate_default(struct clk *clk, struct ofnode_phandle_args *args) @@ -727,7 +743,8 @@ void devm_clk_put(struct udevice *dev, struct clk *clk) WARN_ON(rc); } -int clk_uclass_post_probe(struct udevice *dev) +#if !CONFIG_IS_ENABLED(TINY_CLK) +static int clk_uclass_post_probe(struct udevice *dev) { /* * when a clock provider is probed. Call clk_set_defaults() @@ -745,3 +762,37 @@ UCLASS_DRIVER(clk) = { .name = "clk", .post_probe = clk_uclass_post_probe, }; +#else /* TINY_CLK */ +static inline const struct tiny_clk_ops *tiny_clk_dev_ops(struct tinydev *tdev) +{ + return (const struct tiny_clk_ops *)tdev->drv->ops; +} + +ulong tiny_clk_set_rate(struct tiny_clk *tclk, ulong rate) +{ + const struct tiny_clk_ops *ops; + + debug("%s(tclk=%p, rate=%lu)\n", __func__, tclk, rate); + if (!tiny_clk_valid(tclk)) + return 0; + ops = tiny_clk_dev_ops(tclk->tdev); + + if (!ops->set_rate) + return -ENOSYS; + + return ops->set_rate(tclk, rate); +} + +int tiny_clk_request(struct tinydev *tdev, struct tiny_clk *tclk) +{ + const struct tiny_clk_ops *ops; + + debug("%s(tdev=%p, tclk=%p)\n", __func__, tdev, tclk); + if (!tclk) + return 0; + ops = tiny_clk_dev_ops(tdev); + tclk->tdev = tdev; + + return 0; +} +#endif diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index a1dd642eef..f3e3a21be9 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -746,7 +746,7 @@ static ulong rockchip_saradc_set_clk(struct rockchip_cru *cru, uint hz) return rockchip_saradc_get_clk(cru); } -static ulong rk3288_clk_get_rate(struct clk *clk) +static __maybe_unused ulong rk3288_clk_get_rate(struct clk *clk) { struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); ulong new_rate, gclk_rate; @@ -788,14 +788,14 @@ static ulong rk3288_clk_get_rate(struct clk *clk) return new_rate; } -static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) +static ulong rk3288_clk_set_rate_(ulong clk_id, struct rk3288_clk_priv *priv, + ulong rate) { - struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); struct rockchip_cru *cru = priv->cru; ulong new_rate, gclk_rate; gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); - switch (clk->id) { + switch (clk_id) { case PLL_APLL: /* We only support a fixed rate here */ if (rate != 1800000000) @@ -812,12 +812,12 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) case SCLK_EMMC: case SCLK_SDMMC: case SCLK_SDIO0: - new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate); + new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk_id, rate); break; case SCLK_SPI0: case SCLK_SPI1: case SCLK_SPI2: - new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate); + new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk_id, rate); break; #ifndef CONFIG_SPL_BUILD case SCLK_I2S0: @@ -828,7 +828,7 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) break; case DCLK_VOP0: case DCLK_VOP1: - new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate); + new_rate = rockchip_vop_set_clk(cru, priv->grf, clk_id, rate); break; case SCLK_EDP_24M: /* clk_edp_24M source: 24M */ @@ -848,7 +848,7 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) div = CPLL_HZ / rate; assert((div - 1 < 64) && (div * rate == CPLL_HZ)); - switch (clk->id) { + switch (clk_id) { case ACLK_VOP0: rk_clrsetreg(&cru->cru_clksel_con[31], 3 << 6 | 0x1f << 0, @@ -946,28 +946,9 @@ static int __maybe_unused rk3288_clk_set_parent(struct clk *clk, struct clk *par return -ENOENT; } -static struct clk_ops rk3288_clk_ops = { - .get_rate = rk3288_clk_get_rate, - .set_rate = rk3288_clk_set_rate, -#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) - .set_parent = rk3288_clk_set_parent, -#endif -}; - -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 = dev_read_addr_ptr(dev); -#endif - - return 0; -} - -static int rk3288_clk_probe(struct udevice *dev) +static int rk3288_clk_probe_(struct rk3288_clk_priv *priv, + struct rk3288_clk_plat *plat) { - struct rk3288_clk_priv *priv = dev_get_priv(dev); bool init_clocks = false; priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); @@ -975,8 +956,6 @@ static int rk3288_clk_probe(struct udevice *dev) 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 init_clocks = true; @@ -1001,8 +980,44 @@ static int rk3288_clk_probe(struct udevice *dev) return 0; } +#if !CONFIG_IS_ENABLED(TINY_CLK) +static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); + + return rk3288_clk_set_rate_(clk->id, priv, rate); +} + +static struct clk_ops rk3288_clk_ops = { + .get_rate = rk3288_clk_get_rate, + .set_rate = rk3288_clk_set_rate, +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .set_parent = rk3288_clk_set_parent, +#endif +}; + +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 = dev_read_addr_ptr(dev); +#endif + + return 0; +} + +static int rk3288_clk_probe(struct udevice *dev) +{ + struct rk3288_clk_priv *priv = dev_get_priv(dev); + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + + return rk3288_clk_probe_(priv, plat); +} + static int rk3288_clk_bind(struct udevice *dev) { +#if 0 int ret; struct udevice *sys_child; struct sysreset_reg *priv; @@ -1020,6 +1035,7 @@ static int rk3288_clk_bind(struct udevice *dev) cru_glb_srst_snd_value); sys_child->priv = priv; } +#endif #if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rockchip_cru, cru_softrst_con[0]); @@ -1047,3 +1063,31 @@ U_BOOT_DRIVER(rockchip_rk3288_cru) = { .ofdata_to_platdata = rk3288_clk_ofdata_to_platdata, .probe = rk3288_clk_probe, }; +#else +static int rockchip_clk_tiny_probe(struct tinydev *tdev) +{ + struct rk3288_clk_plat *plat = tdev->dtplat; + struct rk3288_clk_priv *priv = tdev->priv; + + return rk3288_clk_probe_(priv, plat); +} + +static ulong tiny_rk3288_clk_set_rate(struct tiny_clk *tclk, ulong rate) +{ + struct rk3288_clk_priv *priv = tclk->tdev->priv; + + return rk3288_clk_set_rate_(tclk->id, priv, rate); +} + +struct tiny_clk_ops rockchip_clk_tiny_ops = { + .set_rate = tiny_rk3288_clk_set_rate, +}; + +U_BOOT_TINY_DRIVER(rockchip_rk3288_cru) = { + .uclass_id = UCLASS_CLK, + .probe = rockchip_clk_tiny_probe, + .ops = &rockchip_clk_tiny_ops, + DM_TINY_PRIV(, \ + sizeof(struct rk3288_clk_priv)) +}; +#endif diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 018e8c597e..57c4b4cb7e 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -16,6 +16,24 @@ config DM_SPI_FLASH enabled together (it is not possible to use driver model for one and not the other). +config SPL_TINY_SPI_FLASH + bool "Support tiny SPI-flash drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + +config TPL_TINY_SPI_FLASH + bool "Support tiny SPI-flash drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config SPI_FLASH_SANDBOX bool "Support sandbox SPI flash device" depends on SANDBOX && DM_SPI_FLASH diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c index 09c11439b0..3929120787 100644 --- a/drivers/mtd/spi/sf-uclass.c +++ b/drivers/mtd/spi/sf-uclass.c @@ -3,8 +3,11 @@ * Copyright (c) 2014 Google, Inc */ +#define LOG_CATEGORY UCLASS_SPI_FLASH + #include #include +#include #include #include #include @@ -14,6 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; +#if !CONFIG_IS_ENABLED(TINY_SPI_FLASH) int spi_flash_read_dm(struct udevice *dev, u32 offset, size_t len, void *buf) { return log_ret(sf_get_ops(dev)->read(dev, offset, len, buf)); @@ -102,3 +106,75 @@ UCLASS_DRIVER(spi_flash) = { .post_bind = spi_flash_post_bind, .per_device_auto_alloc_size = sizeof(struct spi_flash), }; +#else /* TINY_SPI_FLASH */ +static int tiny_sf_probe(struct tinydev *tdev) +{ + const struct dtd_jedec_spi_nor *dtplat = tdev->dtplat; + struct tiny_spi_nor *nor = tinydev_get_priv(tdev); + struct dm_spi_slave_platdata *slave_plat; + struct spi_slave *slave; + bool exists; + int ret; + + slave = tinydev_ensure_data(tdev, DEVDATAT_PARENT_PRIV, sizeof(*slave), + &exists); + if (!slave) + return log_msg_ret("slave", -ENOMEM); + if (!exists) { + slave->tdev = tdev; + slave->max_hz = dtplat->spi_max_frequency; + slave->wordlen = SPI_DEFAULT_WORDLEN; + /* Leave mode as the default 0 */ + nor->spi = slave; + nor->tdev = tdev; + log_debug("slave->max_hz=%d\n", slave->max_hz); + } + slave_plat = tinydev_ensure_data(tdev, DEVDATAT_PARENT_PLAT, + sizeof(*slave_plat), &exists); + if (!slave_plat) + return log_msg_ret("plat", -ENOMEM); + if (!exists) { + slave_plat->cs = dtplat->reg[0]; + slave_plat->max_hz = dtplat->spi_max_frequency; + /* Leave mode as the default 0 */ + } + + log_debug("start spi_nor_scan\n"); + ret = spi_nor_scan(nor); + if (ret) + return ret; + + return 0; +} + +static int tiny_sf_read(struct tinydev *tdev, u32 offset, size_t len, void *buf) +{ + return log_ret(tiny_spi_flash_read(tdev, offset, len, buf)); +} + +struct tiny_spi_flash_ops tiny_sf_ops = { + .read = tiny_sf_read, +}; + +U_BOOT_TINY_DRIVER(jedec_spi_nor) = { + .uclass_id = UCLASS_SPI_FLASH, + .probe = tiny_sf_probe, + .ops = &tiny_sf_ops, + DM_TINY_PRIV(, sizeof(struct tiny_spi_nor)) +}; + +int tiny_spi_flash_read(struct tinydev *tdev, u32 offset, size_t len, + void *buf) +{ + struct tiny_spi_nor *nor = tinydev_get_priv(tdev); + struct tiny_mtd_info *mtd = &nor->mtd; + size_t retlen; + int ret; + + ret = tiny_spi_nor_read(mtd, offset, len, &retlen, buf); + if (ret) + return log_ret(ret); + + return 0; +} +#endif diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 475f6c31db..c5245e1131 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -17,6 +17,7 @@ #include "sf_internal.h" +#if !CONFIG_IS_ENABLED(TINY_SPI_FLASH) /** * spi_flash_probe_slave() - Probe for a SPI flash device on a bus * @@ -173,3 +174,6 @@ U_BOOT_DRIVER(jedec_spi_nor) = { U_BOOT_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16) #endif /* CONFIG_DM_SPI_FLASH */ +#else /* TINY_SPI_FLASH */ +#endif + diff --git a/drivers/mtd/spi/spi-nor-tiny.c b/drivers/mtd/spi/spi-nor-tiny.c index 9f676c649d..397fb39a9d 100644 --- a/drivers/mtd/spi/spi-nor-tiny.c +++ b/drivers/mtd/spi/spi-nor-tiny.c @@ -9,6 +9,9 @@ * Synced from Linux v4.19 */ +// #define LOG_DEBUG +#define LOG_CATEGORY UCLASS_SPI_FLASH + #include #include #include @@ -36,6 +39,138 @@ #define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ) +#if CONFIG_IS_ENABLED(TINY_SPI) + +#define spi_nor tiny_spi_nor +#define mtd_info tiny_mtd_info + +/** + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to + * match controller limitations + * @tdev: the SPI device (its parent being a bus) + * @op: the operation to adjust + * + * Some controllers have FIFO limitations and must split a data transfer + * operation into multiple ones, others require a specific alignment for + * optimized accesses. This function allows SPI mem drivers to split a single + * operation into multiple sub-operations when required. + * + * Return: a negative error code if the controller can't properly adjust @op, + * 0 otherwise. Note that @op->data.nbytes will be updated if @op + * can't be handled in a single step. + */ +static int spi_mem_adjust_op_size_(struct tinydev *tdev, struct spi_mem_op *op) +{ + struct tinydev *bus = tinydev_get_parent(tdev); + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + + if (ops->adjust_op_size) + return ops->adjust_op_size(tdev, op); + + return 0; +} + +static int spi_mem_exec_op_(struct tinydev *tdev, const struct spi_mem_op *op) +{ + struct tinydev *bus = tinydev_get_parent(tdev); + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + unsigned int pos = 0; + const u8 *tx_buf = NULL; + u8 *rx_buf = NULL; + int op_len; + u32 flag; + int ret; + int i; + + log_debug("bus=%s\n", bus->name); + ret = tiny_spi_claim_bus(tdev); + if (ret < 0) + return ret; + + if (ops->exec_op) { + ret = ops->exec_op(tdev, op); + + /* + * Some controllers only optimize specific paths (typically the + * read path) and expect the core to use the regular SPI + * interface in other cases. + */ + if (!ret || ret != -ENOTSUPP) { + tiny_spi_release_bus(tdev); + return ret; + } + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + rx_buf = op->data.buf.in; + else + tx_buf = op->data.buf.out; + } + + op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes; + + /* + * Avoid using malloc() here so that we can use this code in SPL where + * simple malloc may be used. That implementation does not allow free() + * so repeated calls to this code can exhaust the space. + * + * The value of op_len is small, since it does not include the actual + * data being sent, only the op-code and address. In fact, it should be + * possible to just use a small fixed value here instead of op_len. + */ + u8 op_buf[op_len]; + + op_buf[pos++] = op->cmd.opcode; + + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) + op_buf[pos + i] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + pos += op->addr.nbytes; + } + + if (op->dummy.nbytes) + memset(op_buf + pos, 0xff, op->dummy.nbytes); + + /* 1st transfer: opcode + address + dummy cycles */ + flag = SPI_XFER_BEGIN; + /* Make sure to set END bit if no tx or rx data messages follow */ + if (!tx_buf && !rx_buf) + flag |= SPI_XFER_END; + + ret = tiny_spi_xfer(tdev, op_len * 8, op_buf, NULL, flag); + if (ret) + return ret; + + /* 2nd transfer: rx or tx data path */ + if (tx_buf || rx_buf) { + ret = tiny_spi_xfer(tdev, op->data.nbytes * 8, tx_buf, rx_buf, + SPI_XFER_END); + if (ret) + return ret; + } + + tiny_spi_release_bus(tdev); + + for (i = 0; i < pos; i++) + debug("%02x ", op_buf[i]); + debug("| [%dB %s] ", + tx_buf || rx_buf ? op->data.nbytes : 0, + tx_buf || rx_buf ? (tx_buf ? "out" : "in") : "-"); + for (i = 0; i < op->data.nbytes; i++) + debug("%02x ", tx_buf ? tx_buf[i] : rx_buf[i]); + debug("[ret %d]\n", ret); + + if (ret < 0) + return ret; + + return 0; +} + +#endif /* TINY_SPI */ + static int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op *op, void *buf) { @@ -43,7 +178,8 @@ static int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op op->data.buf.in = buf; else op->data.buf.out = buf; - return spi_mem_exec_op(nor->spi, op); + + return spi_mem_exec_op_(nor->tdev, op); } static int spi_nor_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) @@ -94,11 +230,11 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, while (remaining) { op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; - ret = spi_mem_adjust_op_size(nor->spi, &op); + ret = spi_mem_adjust_op_size_(nor->tdev, &op); if (ret) return ret; - ret = spi_mem_exec_op(nor->spi, &op); + ret = spi_mem_exec_op_(nor->tdev, &op); if (ret) return ret; @@ -351,7 +487,8 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) * Erase an address range on the nor chip. The address range may extend * one or more erase sectors. Return an error is there is a problem erasing. */ -static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) +__maybe_unused static int spi_nor_erase(struct mtd_info *mtd, + struct erase_info *instr) { return -ENOTSUPP; } @@ -380,8 +517,8 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) return ERR_PTR(-ENODEV); } -static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +int tiny_spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; @@ -416,8 +553,8 @@ read_err: * FLASH_PAGESIZE chunks. The address range may be any size provided * it is within the physical boundaries. */ -static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +__maybe_unused static int spi_nor_write(struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, const u_char *buf) { return -ENOTSUPP; } @@ -723,10 +860,14 @@ int spi_nor_scan(struct spi_nor *nor) struct spi_slave *spi = nor->spi; int ret; + log_debug("start\n"); + /* Reset SPI protocol for all commands. */ - nor->reg_proto = SNOR_PROTO_1_1_1; nor->read_proto = SNOR_PROTO_1_1_1; +#if !CONFIG_IS_ENABLED(TINY_SPI) + nor->reg_proto = SNOR_PROTO_1_1_1; nor->write_proto = SNOR_PROTO_1_1_1; +#endif if (spi->mode & SPI_RX_QUAD) hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; @@ -739,16 +880,17 @@ int spi_nor_scan(struct spi_nor *nor) if (ret) return ret; - mtd->name = "spi-flash"; mtd->priv = nor; + mtd->size = info->sector_size * info->n_sectors; +#if !CONFIG_IS_ENABLED(TINY_SPI) + mtd->name = "spi-flash"; mtd->type = MTD_NORFLASH; mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; - mtd->size = info->sector_size * info->n_sectors; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; mtd->_write = spi_nor_write; - +#endif nor->size = mtd->size; if (info->flags & USE_FSR) diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 7e6e981897..0c6d811219 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -17,6 +17,15 @@ config SPL_RAM SPL, enable this option. It might provide a cleaner interface to setting up RAM (e.g. SDRAM / DDR) within SPL. +config SPL_TINY_RAM + bool "Support tiny RAM drivers in SPL" + depends on SPL_RAM + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config TPL_RAM bool "Enable RAM support in TPL" depends on RAM @@ -26,6 +35,15 @@ config TPL_RAM TPL, enable this option. It might provide a cleaner interface to setting up RAM (e.g. SDRAM / DDR) within TPL. +config TPL_TINY_RAM + bool "Support tiny RAM drivers in TPL" + depends on TPL_RAM + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config STM32_SDRAM bool "Enable STM32 SDRAM support" depends on RAM diff --git a/drivers/ram/ram-uclass.c b/drivers/ram/ram-uclass.c index f4d387fed1..608eab9cd4 100644 --- a/drivers/ram/ram-uclass.c +++ b/drivers/ram/ram-uclass.c @@ -11,6 +11,7 @@ #include #include +#if !CONFIG_IS_ENABLED(TINY_RAM) int ram_get_info(struct udevice *dev, struct ram_info *info) { struct ram_ops *ops = ram_get_ops(dev); @@ -25,3 +26,14 @@ UCLASS_DRIVER(ram) = { .id = UCLASS_RAM, .name = "ram", }; +#else /* TINY_RAM */ +int tiny_ram_get_info(struct tinydev *tdev, struct ram_info *info) +{ + struct tiny_ram_ops *ops = tiny_ram_get_ops(tdev); + + if (IS_ENABLED(CONFIG_TINY_CHECK) && !ops->get_info) + return -ENOSYS; + + return ops->get_info(tdev, info); +} +#endif diff --git a/drivers/ram/rockchip/sdram_rk3188.c b/drivers/ram/rockchip/sdram_rk3188.c index 06f9eba1a5..5d94862e28 100644 --- a/drivers/ram/rockchip/sdram_rk3188.c +++ b/drivers/ram/rockchip/sdram_rk3188.c @@ -866,7 +866,7 @@ static int conv_of_platdata(struct udevice *dev) memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base)); /* rk3188 supports dual-channel, set default channel num to 2 */ plat->num_channels = 1; - ret = regmap_init_mem_platdata(dev, of_plat->reg, + ret = regmap_init_mem_platdata(of_plat->reg, ARRAY_SIZE(of_plat->reg) / 2, &plat->map); if (ret) diff --git a/drivers/ram/rockchip/sdram_rk322x.c b/drivers/ram/rockchip/sdram_rk322x.c index 094693ce24..16f4eb0df0 100644 --- a/drivers/ram/rockchip/sdram_rk322x.c +++ b/drivers/ram/rockchip/sdram_rk322x.c @@ -767,7 +767,7 @@ static int conv_of_platdata(struct udevice *dev) memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base)); plat->num_channels = 1; - ret = regmap_init_mem_platdata(dev, of_plat->reg, + ret = regmap_init_mem_platdata(of_plat->reg, ARRAY_SIZE(of_plat->reg) / 2, &plat->map); if (ret) diff --git a/drivers/ram/rockchip/sdram_rk3288.c b/drivers/ram/rockchip/sdram_rk3288.c index 26e8d059b5..83bc62caf7 100644 --- a/drivers/ram/rockchip/sdram_rk3288.c +++ b/drivers/ram/rockchip/sdram_rk3288.c @@ -30,27 +30,10 @@ #include #include -struct chan_info { - struct rk3288_ddr_pctl *pctl; - struct rk3288_ddr_publ *publ; - struct rk3288_msch *msch; -}; - -struct dram_info { - struct chan_info chan[2]; - struct ram_info info; - struct clk ddr_clk; - struct rockchip_cru *cru; - struct rk3288_grf *grf; - struct rk3288_sgrf *sgrf; - struct rk3288_pmu *pmu; - bool is_veyron; -}; +struct dtd_rockchip_rk3288_dmc; struct rk3288_sdram_params { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct dtd_rockchip_rk3288_dmc of_plat; -#endif + IF_OF_PLATDATA(struct dtd_rockchip_rk3288_dmc of_plat;) struct rk3288_sdram_channel ch[2]; struct rk3288_sdram_pctl_timing pctl_timing; struct rk3288_sdram_phy_timing phy_timing; @@ -85,6 +68,11 @@ const int ddrconf_table[] = { #if defined(CONFIG_TPL_BUILD) || \ (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) +#define DO_SDRAM_INIT 1 +#else +#define DO_SDRAM_INIT 0 +#endif + static void copy_to_reg(u32 *dest, const u32 *src, u32 n) { int i; @@ -291,7 +279,7 @@ static void pctl_cfg(int channel, struct rk3288_ddr_pctl *pctl, setbits_le32(&pctl->scfg, 1); } -static void phy_cfg(const struct chan_info *chan, int channel, +static void phy_cfg(const struct rk_chan_info *chan, int channel, struct rk3288_sdram_params *sdram_params) { struct rk3288_ddr_publ *publ = chan->publ; @@ -436,7 +424,7 @@ static void move_to_config_state(struct rk3288_ddr_publ *publ, } } -static void set_bandwidth_ratio(const struct chan_info *chan, int channel, +static void set_bandwidth_ratio(const struct rk_chan_info *chan, int channel, u32 n, struct rk3288_grf *grf) { struct rk3288_ddr_pctl *pctl = chan->pctl; @@ -474,7 +462,7 @@ static void set_bandwidth_ratio(const struct chan_info *chan, int channel, setbits_le32(&pctl->dfistcfg0, 1 << 2); } -static int data_training(const struct chan_info *chan, int channel, +static int data_training(const struct rk_chan_info *chan, int channel, struct rk3288_sdram_params *sdram_params) { unsigned int j; @@ -537,7 +525,7 @@ static int data_training(const struct chan_info *chan, int channel, return ret; } -static void move_to_access_state(const struct chan_info *chan) +static void move_to_access_state(const struct rk_chan_info *chan) { struct rk3288_ddr_publ *publ = chan->publ; struct rk3288_ddr_pctl *pctl = chan->pctl; @@ -577,7 +565,7 @@ static void move_to_access_state(const struct chan_info *chan) } } -static void dram_cfg_rbc(const struct chan_info *chan, u32 chnum, +static void dram_cfg_rbc(const struct rk_chan_info *chan, u32 chnum, struct rk3288_sdram_params *sdram_params) { struct rk3288_ddr_publ *publ = chan->publ; @@ -591,7 +579,7 @@ static void dram_cfg_rbc(const struct chan_info *chan, u32 chnum, writel(sdram_params->base.ddrconfig, &chan->msch->ddrconf); } -static void dram_all_config(const struct dram_info *dram, +static void dram_all_config(const struct rk_dram_info *dram, struct rk3288_sdram_params *sdram_params) { unsigned int chan; @@ -619,12 +607,12 @@ static void dram_all_config(const struct dram_info *dram, rk_clrsetreg(&dram->sgrf->soc_con2, 0x1f, sdram_params->base.stride); } -static int sdram_rank_bw_detect(struct dram_info *dram, int channel, +static int sdram_rank_bw_detect(struct rk_dram_info *dram, int channel, struct rk3288_sdram_params *sdram_params) { int reg; int need_trainig = 0; - const struct chan_info *chan = &dram->chan[channel]; + const struct rk_chan_info *chan = &dram->chan[channel]; struct rk3288_ddr_publ *publ = chan->publ; if (data_training(chan, channel, sdram_params) < 0) { @@ -672,12 +660,12 @@ static int sdram_rank_bw_detect(struct dram_info *dram, int channel, return 0; } -static int sdram_col_row_detect(struct dram_info *dram, int channel, +static int sdram_col_row_detect(struct rk_dram_info *dram, int channel, struct rk3288_sdram_params *sdram_params) { int row, col; unsigned int addr; - const struct chan_info *chan = &dram->chan[channel]; + const struct rk_chan_info *chan = &dram->chan[channel]; struct rk3288_ddr_pctl *pctl = chan->pctl; struct rk3288_ddr_publ *publ = chan->publ; int ret = 0; @@ -782,7 +770,7 @@ static int sdram_get_stride(struct rk3288_sdram_params *sdram_params) return ret; } -static int sdram_init(struct dram_info *dram, +static int sdram_init(struct rk_dram_info *dram, struct rk3288_sdram_params *sdram_params) { int channel; @@ -799,7 +787,11 @@ static int sdram_init(struct dram_info *dram, } debug("ddr clk dpll\n"); - ret = clk_set_rate(&dram->ddr_clk, sdram_params->base.ddr_freq); + if (!CONFIG_IS_ENABLED(TINY_CLK)) + ret = clk_set_rate(&dram->ddr_clk, sdram_params->base.ddr_freq); + else + ret = tiny_clk_set_rate(&dram->tiny_ddr_clk, + sdram_params->base.ddr_freq); debug("ret=%d\n", ret); if (ret) { debug("Could not set DDR clock\n"); @@ -807,7 +799,7 @@ static int sdram_init(struct dram_info *dram, } for (channel = 0; channel < 2; channel++) { - const struct chan_info *chan = &dram->chan[channel]; + const struct rk_chan_info *chan = &dram->chan[channel]; struct rk3288_ddr_pctl *pctl = chan->pctl; struct rk3288_ddr_publ *publ = chan->publ; @@ -927,8 +919,7 @@ error: hang(); } -# ifdef CONFIG_ROCKCHIP_FAST_SPL -static int veyron_init(struct dram_info *priv) +static int veyron_init(struct rk_dram_info *priv) { struct udevice *pmic; int ret; @@ -951,32 +942,31 @@ static int veyron_init(struct dram_info *priv) return 0; } -# endif -static int setup_sdram(struct udevice *dev) +static int setup_sdram(struct rk_dram_info *priv, + struct rk3288_sdram_params *params) { - struct dram_info *priv = dev_get_priv(dev); - struct rk3288_sdram_params *params = dev_get_platdata(dev); - -# ifdef CONFIG_ROCKCHIP_FAST_SPL - if (priv->is_veyron) { + if (IS_ENABLED(CONFIG_ROCKCHIP_FAST_SPL) && priv->is_veyron) { int ret; ret = veyron_init(priv); if (ret) return ret; } -# endif return sdram_init(priv, params); } +#if !CONFIG_IS_ENABLED(TINY_RAM) 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); int ret; + if (!DO_SDRAM_INIT) + return 0; + /* Rk3288 supports dual-channel, set default channel num to 2 */ params->num_channels = 2; ret = dev_read_u32_array(dev, "rockchip,pctl-timing", @@ -1000,11 +990,12 @@ static int rk3288_dmc_ofdata_to_platdata(struct udevice *dev) debug("%s: Cannot read rockchip,sdram-params\n", __func__); return -EINVAL; } -#ifdef CONFIG_ROCKCHIP_FAST_SPL - struct dram_info *priv = dev_get_priv(dev); + if (IS_ENABLED(CONFIG_ROCKCHIP_FAST_SPL)) { + struct rk_dram_info *priv = dev_get_priv(dev); - priv->is_veyron = !fdt_node_check_compatible(blob, 0, "google,veyron"); -#endif + priv->is_veyron = !fdt_node_check_compatible(gd->fdt_blob, 0, + "google,veyron"); + } ret = regmap_init_mem(dev_ofnode(dev), ¶ms->map); if (ret) return ret; @@ -1012,13 +1003,12 @@ static int rk3288_dmc_ofdata_to_platdata(struct udevice *dev) return 0; } -#endif /* CONFIG_SPL_BUILD */ +#endif /* !TINY_RAM */ -#if CONFIG_IS_ENABLED(OF_PLATDATA) -static int conv_of_platdata(struct udevice *dev) +static int conv_of_platdata(struct rk3288_sdram_params *plat, + struct dtd_rockchip_rk3288_dmc *of_plat) { - struct rk3288_sdram_params *plat = dev_get_platdata(dev); - struct dtd_rockchip_rk3288_dmc *of_plat = &plat->of_plat; +#if CONFIG_IS_ENABLED(OF_PLATDATA) int ret; memcpy(&plat->pctl_timing, of_plat->rockchip_pctl_timing, @@ -1028,35 +1018,22 @@ static int conv_of_platdata(struct udevice *dev) memcpy(&plat->base, of_plat->rockchip_sdram_params, sizeof(plat->base)); /* Rk3288 supports dual-channel, set default channel num to 2 */ plat->num_channels = 2; - ret = regmap_init_mem_platdata(dev, of_plat->reg, + ret = regmap_init_mem_platdata(of_plat->reg, ARRAY_SIZE(of_plat->reg) / 2, &plat->map); if (ret) return ret; +#endif return 0; } -#endif -static int rk3288_dmc_probe(struct udevice *dev) +static int complete_probe(struct rk3288_sdram_params *plat, + struct rk_dram_info *priv) { -#if defined(CONFIG_TPL_BUILD) || \ - (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) - struct rk3288_sdram_params *plat = dev_get_platdata(dev); - struct udevice *dev_clk; struct regmap *map; int ret; -#endif - struct dram_info *priv = dev_get_priv(dev); - priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); -#if defined(CONFIG_TPL_BUILD) || \ - (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) -#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); @@ -1072,32 +1049,69 @@ static int rk3288_dmc_probe(struct udevice *dev) priv->chan[1].pctl = regmap_get_range(plat->map, 2); priv->chan[1].publ = regmap_get_range(plat->map, 3); - ret = rockchip_get_clk(&dev_clk); - if (ret) - return ret; - priv->ddr_clk.id = CLK_DDR; - ret = clk_request(dev_clk, &priv->ddr_clk); - if (ret) - return ret; + if (!CONFIG_IS_ENABLED(TINY_CLK)) { + struct udevice *dev_clk; + + ret = rockchip_get_clk(&dev_clk); + if (ret) + return ret; + priv->ddr_clk.id = CLK_DDR; + ret = clk_request(dev_clk, &priv->ddr_clk); + if (ret) + return ret; + } else { + struct tinydev *tdev_clk; + + tdev_clk = tiny_rockchip_get_clk(); + if (!tdev_clk) + return -ENODEV; + priv->tiny_ddr_clk.id = CLK_DDR; + ret = tiny_clk_request(tdev_clk, &priv->tiny_ddr_clk); + if (ret) + return ret; + } priv->cru = rockchip_get_cru(); if (IS_ERR(priv->cru)) return PTR_ERR(priv->cru); - ret = setup_sdram(dev); + ret = setup_sdram(priv, plat); if (ret) return ret; -#else - priv->info.base = CONFIG_SYS_SDRAM_BASE; - priv->info.size = rockchip_sdram_size( - (phys_addr_t)&priv->pmu->sys_reg[2]); -#endif + + return 0; +} + +#if !CONFIG_IS_ENABLED(TINY_RAM) +static int rk3288_dmc_probe(struct udevice *dev) +{ + struct rk3288_sdram_params *plat = dev_get_platdata(dev); + int ret; + struct rk_dram_info *priv = dev_get_priv(dev); + + priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); + if (DO_SDRAM_INIT) { + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + ret = conv_of_platdata(plat, + CONFIG_IS_ENABLED(OF_PLATDATA, (&plat->of_plat), + (NULL))); + if (ret) + return log_msg_ret("plat", ret); + } + ret = complete_probe(plat, priv); + if (ret) + return log_msg_ret("complete", ret); + } else { + priv->info.base = CONFIG_SYS_SDRAM_BASE; + priv->info.size = rockchip_sdram_size( + (phys_addr_t)&priv->pmu->sys_reg[2]); + } return 0; } static int rk3288_dmc_get_info(struct udevice *dev, struct ram_info *info) { - struct dram_info *priv = dev_get_priv(dev); + struct rk_dram_info *priv = dev_get_priv(dev); *info = priv->info; @@ -1118,14 +1132,55 @@ U_BOOT_DRIVER(rockchip_rk3288_dmc) = { .id = UCLASS_RAM, .of_match = rk3288_dmc_ids, .ops = &rk3288_dmc_ops, -#if defined(CONFIG_TPL_BUILD) || \ - (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) .ofdata_to_platdata = rk3288_dmc_ofdata_to_platdata, -#endif .probe = rk3288_dmc_probe, - .priv_auto_alloc_size = sizeof(struct dram_info), -#if defined(CONFIG_TPL_BUILD) || \ - (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) + .priv_auto_alloc_size = sizeof(struct rk_dram_info), +#if DO_SDRAM_INIT .platdata_auto_alloc_size = sizeof(struct rk3288_sdram_params), #endif }; +#else /* TINY_RAM */ +static int tiny_rk3288_dmc_probe(struct tinydev *tdev) +{ + struct rk_dram_info *priv = tinydev_get_priv(tdev); + int ret; + + priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); + if (DO_SDRAM_INIT) { + struct rk3288_sdram_params plat; + + ret = conv_of_platdata(&plat, tdev->dtplat); + if (ret) + return log_msg_ret("plat", ret); + ret = complete_probe(&plat, priv); + if (ret) + return log_msg_ret("complete", ret); + } else { + priv->info.base = CONFIG_SYS_SDRAM_BASE; + priv->info.size = rockchip_sdram_size( + (phys_addr_t)&priv->pmu->sys_reg[2]); + } + + return 0; +} +static int tiny_rk3288_dmc_get_info(struct tinydev *dev, struct ram_info *info) +{ + struct rk_dram_info *priv = tinydev_get_priv(dev); + + *info = priv->info; + + return 0; +} + +static struct tiny_ram_ops tiny_rk3288_dmc_ops = { + .get_info = tiny_rk3288_dmc_get_info, +}; + +U_BOOT_TINY_DRIVER(rockchip_rk3288_dmc) = { + .uclass_id = UCLASS_RAM, + .ops = &tiny_rk3288_dmc_ops, + .probe = tiny_rk3288_dmc_probe, + DM_TINY_PRIV(, \ + sizeof(struct rk_dram_info)) +}; +#endif diff --git a/drivers/ram/rockchip/sdram_rk3328.c b/drivers/ram/rockchip/sdram_rk3328.c index 98c7feb6cf..184af5c861 100644 --- a/drivers/ram/rockchip/sdram_rk3328.c +++ b/drivers/ram/rockchip/sdram_rk3328.c @@ -54,7 +54,7 @@ static int conv_of_platdata(struct udevice *dev) struct dtd_rockchip_rk3328_dmc *dtplat = &plat->dtplat; int ret; - ret = regmap_init_mem_platdata(dev, dtplat->reg, + ret = regmap_init_mem_platdata(dtplat->reg, ARRAY_SIZE(dtplat->reg) / 2, &plat->map); if (ret) diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 0fe2cedc52..111a8856f6 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -3061,7 +3061,7 @@ static int conv_of_platdata(struct udevice *dev) struct dtd_rockchip_rk3399_dmc *dtplat = &plat->dtplat; int ret; - ret = regmap_init_mem_platdata(dev, dtplat->reg, + ret = regmap_init_mem_platdata(dtplat->reg, ARRAY_SIZE(dtplat->reg) / 2, &plat->map); if (ret) diff --git a/drivers/reset/reset-rockchip.c b/drivers/reset/reset-rockchip.c index 8092555650..689f38405f 100644 --- a/drivers/reset/reset-rockchip.c +++ b/drivers/reset/reset-rockchip.c @@ -112,8 +112,8 @@ int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number) struct rockchip_reset_priv *priv; int ret; - ret = device_bind_driver_to_node(pdev, "rockchip_reset", "reset", - dev_ofnode(pdev), &rst_dev); + ret = device_bind_driver_to_node(pdev, "rockchip_reset", "reset", + dev_ofnode(pdev), &rst_dev); if (ret) { debug("Warning: No rockchip reset driver: ret=%d\n", ret); return ret; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index bc7f42bbf3..8d3591b235 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -108,6 +108,24 @@ config DM_SERIAL implements serial_putc() etc. The uclass interface is defined in include/serial.h. +config SPL_TINY_SERIAL + bool "Support tiny serial drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + +config TPL_TINY_SERIAL + bool "Support tiny serial drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config SERIAL_RX_BUFFER bool "Enable RX buffer for serial input" depends on DM_SERIAL @@ -641,7 +659,9 @@ config SYS_NS16550 config NS16550_DYNAMIC bool "Allow NS16550 to be configured at runtime" + depends on SYS_NS16550 default y if SYS_COREBOOT || SYS_SLIMBOOTLOADER + default y if SPL_TINY_SERIAL || TPL_TINY_SERIAL help Enable this option to allow device-tree control of the driver. @@ -660,6 +680,24 @@ config NS16550_DYNAMIC UARTs in a system. This option avoids this problem at the cost of a slightly increased code size. +config NS16550_SUPPORT_IO + bool "Support I/O access in the ns16550 driver" + default y if X86 + help + This enables I/O access in this driver, as wells the normal + memory-mapped access. This is used on some architectures, such as + x86. Note that I/O access is not supported on some more modern + architectures, such as ARM. + +config NS16550_SUPPORT_ENDIAN + bool "Support endian-aware access in the ns16550 driver" + default y if POWERPC + help + This enables in_be32() and the like in this driver, as wells the + normal memory-mapped access. This is used on some architectures, such + as PowerPC. Note that this access is not used on some architectures, + such as ARM. + config INTEL_MID_SERIAL bool "Intel MID platform UART support" depends on DM_SERIAL && OF_CONTROL diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 20340a83f8..eb8f1f7166 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -88,10 +88,12 @@ static inline int serial_in_shift(void *addr, int shift) static void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr, int value) { - if (plat->flags & NS16550_FLAG_IO) { + if (IS_ENABLED(CONFIG_NS16550_SUPPORT_IO) && + (plat->flags & NS16550_FLAG_IO)) { outb(value, addr); } else if (plat->reg_width == 4) { - if (plat->flags & NS16550_FLAG_ENDIAN) { + if (IS_ENABLED(CONFIG_NS16550_SUPPORT_ENDIAN) && + (plat->flags & NS16550_FLAG_ENDIAN)) { if (plat->flags & NS16550_FLAG_BE) out_be32(addr, value); else @@ -99,7 +101,8 @@ static void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr, } else { writel(value, addr); } - } else if (plat->flags & NS16550_FLAG_BE) { + } else if (IS_ENABLED(CONFIG_NS16550_SUPPORT_ENDIAN) && + (plat->flags & NS16550_FLAG_BE)) { writeb(value, addr + (1 << plat->reg_shift) - 1); } else { writeb(value, addr); @@ -108,10 +111,12 @@ static void serial_out_dynamic(struct ns16550_platdata *plat, u8 *addr, static int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr) { - if (plat->flags & NS16550_FLAG_IO) { + if (IS_ENABLED(CONFIG_NS16550_SUPPORT_IO) && + (plat->flags & NS16550_FLAG_IO)) { return inb(addr); } else if (plat->reg_width == 4) { - if (plat->flags & NS16550_FLAG_ENDIAN) { + if (IS_ENABLED(CONFIG_NS16550_SUPPORT_ENDIAN) && + (plat->flags & NS16550_FLAG_ENDIAN)) { if (plat->flags & NS16550_FLAG_BE) return in_be32(addr); else @@ -119,7 +124,8 @@ static int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr) } else { return readl(addr); } - } else if (plat->flags & NS16550_FLAG_BE) { + } else if (IS_ENABLED(CONFIG_NS16550_SUPPORT_ENDIAN) && + (plat->flags & NS16550_FLAG_BE)) { return readb(addr + (1 << plat->reg_shift) - 1); } else { return readb(addr); @@ -138,13 +144,27 @@ static inline int serial_in_dynamic(struct ns16550_platdata *plat, u8 *addr) #endif /* CONFIG_NS16550_DYNAMIC */ +static u8 *ns16550_get_addr(struct ns16550_platdata *plat, int offset) +{ + offset *= 1 << plat->reg_shift; + + return (u8 *)plat->base + offset + plat->reg_offset; +} + +int ns16550_calc_divisor(int clock, int baudrate) +{ + const unsigned int mode_x_div = 16; + + return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); +} + +#if !CONFIG_IS_ENABLED(TINY_SERIAL) static void ns16550_writeb(NS16550_t port, int offset, int value) { struct ns16550_platdata *plat = port->plat; unsigned char *addr; - offset *= 1 << plat->reg_shift; - addr = (unsigned char *)plat->base + offset + plat->reg_offset; + addr = ns16550_get_addr(plat, offset); if (IS_ENABLED(CONFIG_NS16550_DYNAMIC)) serial_out_dynamic(plat, addr, value); @@ -157,8 +177,7 @@ static int ns16550_readb(NS16550_t port, int offset) struct ns16550_platdata *plat = port->plat; unsigned char *addr; - offset *= 1 << plat->reg_shift; - addr = (unsigned char *)plat->base + offset + plat->reg_offset; + addr = ns16550_get_addr(plat, offset); if (IS_ENABLED(CONFIG_NS16550_DYNAMIC)) return serial_in_dynamic(plat, addr); @@ -181,13 +200,6 @@ static u32 ns16550_getfcr(NS16550_t port) ns16550_readb(com_port, \ (unsigned char *)addr - (unsigned char *)com_port) -int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) -{ - const unsigned int mode_x_div = 16; - - return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); -} - static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) { /* to keep serial format, read lcr before writing BKSE */ @@ -309,7 +321,7 @@ static inline void _debug_uart_init(void) * feasible. The better fix is to move all users of this driver to * driver model. */ - baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, + baud_divisor = ns16550_calc_divisor(CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); serial_dout(&com_port->mcr, UART_MCRVAL); @@ -396,7 +408,7 @@ static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) struct ns16550_platdata *plat = com_port->plat; int clock_divisor; - clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); + clock_divisor = ns16550_calc_divisor(plat->clock, baudrate); NS16550_setbrg(com_port, clock_divisor); @@ -601,3 +613,148 @@ U_BOOT_DRIVER_ALIAS(ns16550_serial, rockchip_rk3368_uart) U_BOOT_DRIVER_ALIAS(ns16550_serial, ti_da830_uart) #endif #endif /* SERIAL_PRESENT */ + +#else /* TINY_SERIAL */ + +static void serial_out_reg(struct ns16550_platdata *plat, int offset, int value) +{ + unsigned char *addr = ns16550_get_addr(plat, offset); + + serial_out_dynamic(plat, addr, value); +} + +static int serial_in_reg(struct ns16550_platdata *plat, int offset) +{ + unsigned char *addr = ns16550_get_addr(plat, offset); + + return serial_in_dynamic(plat, addr); +} + +#define ns16550_reg(field) offsetof(struct NS16550, field) + +int ns16550_tiny_probe_plat(struct ns16550_platdata *plat) +{ + while (!(serial_in_reg(plat, ns16550_reg(lsr)) & UART_LSR_TEMT)) + ; + + serial_out_reg(plat, ns16550_reg(ier), CONFIG_SYS_NS16550_IER); + serial_out_reg(plat, ns16550_reg(mcr), UART_MCRVAL); + serial_out_reg(plat, ns16550_reg(fcr), plat->fcr); + + /* initialise serial config to 8N1 before writing baudrate */ + serial_out_reg(plat, ns16550_reg(lcr), UART_LCRVAL); + + return 0; +} + +int ns16550_tiny_setbrg(struct ns16550_platdata *plat, int baud_rate) +{ + int baud_divisor; + + baud_divisor = ns16550_calc_divisor(plat->clock, baud_rate); + serial_out_reg(plat, ns16550_reg(lcr), UART_LCR_BKSE | UART_LCRVAL); + serial_out_reg(plat, ns16550_reg(dll), baud_divisor & 0xff); + serial_out_reg(plat, ns16550_reg(dlm), (baud_divisor >> 8) & 0xff); + serial_out_reg(plat, ns16550_reg(lcr), UART_LCRVAL); + + return 0; +} + +int ns16550_tiny_putc(struct ns16550_platdata *plat, const char ch) +{ + while (!(serial_in_reg(plat, ns16550_reg(lsr)) & UART_LSR_THRE)) + ; + serial_out_reg(plat, ns16550_reg(thr), ch); + + return 0; +} + +#ifdef CONFIG_DEBUG_UART_NS16550 + +#include + +static inline void _debug_uart_init(void) +{ + struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; + int baud_divisor; + + /* + * We copy the code from above because it is already horribly messy. + * Trying to refactor to nicely remove the duplication doesn't seem + * feasible. The better fix is to move all users of this driver to + * driver model. + */ + baud_divisor = ns16550_calc_divisor(CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE); + serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); + serial_dout(&com_port->mcr, UART_MCRVAL); + serial_dout(&com_port->fcr, UART_FCR_DEFVAL); + + serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); + serial_dout(&com_port->dll, baud_divisor & 0xff); + serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); + serial_dout(&com_port->lcr, UART_LCRVAL); +} + +static inline int NS16550_read_baud_divisor(struct NS16550 *com_port) +{ + int ret; + + serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); + ret = serial_din(&com_port->dll) & 0xff; + ret |= (serial_din(&com_port->dlm) & 0xff) << 8; + serial_dout(&com_port->lcr, UART_LCRVAL); + + return ret; +} + +static inline void _debug_uart_putc(int ch) +{ + struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; + + while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) { +#ifdef CONFIG_DEBUG_UART_NS16550_CHECK_ENABLED + if (!NS16550_read_baud_divisor(com_port)) + return; +#endif + } + serial_dout(&com_port->thr, ch); +} +#if 0 +static inline void _debug_uart_init(void) +{ + struct ns16550_platdata plat; + + plat.base = CONFIG_DEBUG_UART_BASE; + plat.reg_shift = 1 << CONFIG_DEBUG_UART_SHIFT; + plat.reg_width = 1; + plat.reg_offset = 0; + plat.clock = CONFIG_DEBUG_UART_CLOCK; + plat.fcr = UART_FCR_DEFVAL; + plat.flags = 0; + ns16550_tiny_probe_plat(&plat); + ns16550_tiny_setbrg(&plat, CONFIG_BAUDRATE); +} + +static inline void _debug_uart_putc(int ch) +{ + struct ns16550_platdata plat; + + plat.base = CONFIG_DEBUG_UART_BASE; + plat.reg_shift = 1 << CONFIG_DEBUG_UART_SHIFT; + plat.reg_width = 1; + plat.reg_offset = 0; + plat.clock = CONFIG_DEBUG_UART_CLOCK; + plat.fcr = UART_FCR_DEFVAL; + plat.flags = 0; + while (!(serial_in_reg(&plat, ns16550_reg(lsr)) & UART_LSR_THRE)) + ; + serial_out_reg(&plat, ns16550_reg(thr), ch); +} +#endif + +DEBUG_UART_FUNCS + +#endif + +#endif /* TINY_SERIAL */ diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index bae9ed5178..68d808c559 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -19,6 +19,8 @@ #include #include +#if !CONFIG_IS_ENABLED(TINY_SERIAL) + DECLARE_GLOBAL_DATA_PTR; struct sandbox_serial_platdata { @@ -133,23 +135,6 @@ static int sandbox_serial_getc(struct udevice *dev) return membuff_getbyte(&priv->buf); } -#ifdef CONFIG_DEBUG_UART_SANDBOX - -#include - -static inline void _debug_uart_init(void) -{ -} - -static inline void _debug_uart_putc(int ch) -{ - os_putc(ch); -} - -DEBUG_UART_FUNCS - -#endif /* CONFIG_DEBUG_UART_SANDBOX */ - static int sandbox_serial_getconfig(struct udevice *dev, uint *serial_config) { uint config = SERIAL_DEFAULT_CONFIG; @@ -258,3 +243,43 @@ U_BOOT_DEVICE(serial_sandbox_non_fdt) = { .name = "sandbox_serial", .platdata = &platdata_non_fdt, }; + +#else /* TINY_SERIAL */ + +static int sandbox_serial_tiny_putc(struct tinydev *tdev, const char ch) +{ + os_putc(ch); + + return 0; +} + +struct tiny_serial_ops sandbox_serial_tiny_ops = { + .probe = sandbox_serial_tiny_probe, + .setbrg = sandbox_serial_tiny_setbrg, + .putc = sandbox_serial_tiny_putc, +}; + +U_BOOT_TINY_DRIVER(sandbox_serial) = { + .uclass_id = UCLASS_SERIAL, + .probe = sandbox_serial_tiny_probe, + .ops = &sandbox_serial_tiny_ops, +}; + +#endif + +#ifdef CONFIG_DEBUG_UART_SANDBOX + +#include + +static inline void _debug_uart_init(void) +{ +} + +static inline void _debug_uart_putc(int ch) +{ + os_putc(ch); +} + +DEBUG_UART_FUNCS + +#endif /* CONFIG_DEBUG_UART_SANDBOX */ diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index ed25d5cec8..32be645775 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -4,9 +4,11 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -19,6 +21,8 @@ DECLARE_GLOBAL_DATA_PTR; +#if !CONFIG_IS_ENABLED(TINY_SERIAL) + /* * Table with supported baudrates (defined in config_xyz.h) */ @@ -507,3 +511,76 @@ UCLASS_DRIVER(serial) = { .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), }; #endif + +#else /* TINY_SERIAL */ + +int serial_init(void) +{ + struct tinydev *tdev; + int ret; + + tdev = tiny_dev_find(UCLASS_SERIAL, 0); + if (!tdev) { + if (IS_ENABLED(CONFIG_REQUIRE_SERIAL_CONSOLE)) + panic_str("No serial"); + return -ENODEV; + } + ret = tiny_dev_probe(tdev); + if (ret) + return log_msg_ret("probe", ret); + ret = tiny_serial_setbrg(tdev, gd->baudrate); + if (ret) + return log_msg_ret("brg", ret); + gd->tiny_serial = tdev; + gd->flags |= GD_FLG_SERIAL_READY; + + return 0; +} + +int serial_getc(void) +{ + return -ENOSYS; +} + +void serial_putc(const char ch) +{ + struct tinydev *tdev = gd->tiny_serial; + struct tiny_serial_ops *ops; + + /* + * If we don't have a serial port or the driver is broken, try the + * debug UART. This helps with debugging the serial driver in early + * bringup, and adds very little to code size. + */ + if (!tdev) + goto err; + ops = tiny_serial_get_ops(tdev); + if (IS_ENABLED(CONFIG_TINY_CHECK) && !ops->putc) + goto err; + if (ch == '\n') + ops->putc(tdev, '\r'); + ops->putc(tdev, ch); + + return; +err: + if (IS_ENABLED(DEBUG_UART)) + printch(ch); +} + +void serial_puts(const char *str) +{ + for (const char *s = str; *s; s++) + serial_putc(*s); +} + +int tiny_serial_setbrg(struct tinydev *tdev, int baudrate) +{ + const struct tiny_serial_ops *ops = tiny_serial_get_ops(tdev); + + if (IS_ENABLED(CONFIG_TINY_CHECK) && !ops) + return -ENOSYS; + + return ops->setbrg(tdev, baudrate); +} + +#endif diff --git a/drivers/serial/serial_omap.c b/drivers/serial/serial_omap.c index ee7a18e5c5..d98e52832e 100644 --- a/drivers/serial/serial_omap.c +++ b/drivers/serial/serial_omap.c @@ -69,7 +69,7 @@ static inline void _debug_uart_init(void) struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; int baud_divisor; - baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, + baud_divisor = ns16550_calc_divisor(CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); serial_dout(&com_port->mdr1, 0x7); diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c index b1718f72d1..b47ff7142c 100644 --- a/drivers/serial/serial_rockchip.c +++ b/drivers/serial/serial_rockchip.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ struct rockchip_uart_platdata { struct dtd_rockchip_rk3288_uart *dtplat, s_dtplat; #endif +#if !CONFIG_IS_ENABLED(TINY_SERIAL) static int rockchip_serial_probe(struct udevice *dev) { struct rockchip_uart_platdata *plat = dev_get_platdata(dev); @@ -49,12 +51,69 @@ U_BOOT_DRIVER(rockchip_rk3188_uart) = { .flags = DM_FLAG_PRE_RELOC, }; +static const struct udevice_id rockchip_serial_ids[] = { + { .compatible = "rockchip,rk3288-uart" }, + { }, +}; + U_BOOT_DRIVER(rockchip_rk3288_uart) = { .name = "rockchip_rk3288_uart", .id = UCLASS_SERIAL, + .of_match = rockchip_serial_ids, .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, }; +#else /* TINY_SERIAL */ + +static int rockchip_serial_tiny_probe(struct tinydev *tdev) +{ + struct dtd_rockchip_rk3288_uart *dtplat = tdev->dtplat; + struct ns16550_platdata *plat = tdev->priv; + int ret; + + /* Create some new platform data for the standard driver */ + plat->base = dtplat->reg[0]; + plat->reg_shift = dtplat->reg_shift; + plat->reg_width = dtplat->reg_io_width; + plat->clock = dtplat->clock_frequency; + plat->fcr = UART_FCR_DEFVAL; + + log_debug("plat=%p, base=%lx, offset=%x, width=%x, shift=%x, clock=%d, flags=%x\n", + plat, plat->base, plat->reg_offset, plat->reg_width, + plat->reg_shift, plat->clock, plat->flags); + ret = ns16550_tiny_probe_plat(plat); + if (ret) + return log_ret(ret); + + return 0; +} + +static int rockchip_serial_tiny_setbrg(struct tinydev *tdev, int baudrate) +{ + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_setbrg(plat, baudrate); +} + +static int rockchip_serial_tiny_putc(struct tinydev *tdev, const char ch) +{ + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_putc(plat, ch); +} + +struct tiny_serial_ops rockchip_serial_tiny_ops = { + .setbrg = rockchip_serial_tiny_setbrg, + .putc = rockchip_serial_tiny_putc, +}; + +U_BOOT_TINY_DRIVER(rockchip_rk3288_uart) = { + .uclass_id = UCLASS_SERIAL, + .probe = rockchip_serial_tiny_probe, + .ops = &rockchip_serial_tiny_ops, + DM_TINY_PRIV(, sizeof(struct ns16550_platdata)) +}; +#endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 09b9cb17d8..6fffbde710 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -42,6 +42,24 @@ config SPI_MEM if DM_SPI +config SPL_TINY_SPI + bool "Support tiny SPI drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + +config TPL_TINY_SPI + bool "Support tiny SPI drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config ALTERA_SPI bool "Altera SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4e7461771f..332c6f3d5c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -9,7 +9,9 @@ obj-y += spi-uclass.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o obj-$(CONFIG_SOFT_SPI) += soft_spi.o +ifeq ($(CONFIG_$(SPL_TPL_)TINY_SPI),) obj-$(CONFIG_SPI_MEM) += spi-mem.o +endif obj-$(CONFIG_TI_QSPI) += ti_qspi.o else obj-y += spi.o diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c index 6cadeac56a..cf5b9ecfc4 100644 --- a/drivers/spi/rk_spi.c +++ b/drivers/spi/rk_spi.c @@ -10,6 +10,8 @@ * Peter, Software Engineering, . */ +#define LOG_CATEGORY UCLASS_SPI + #include #include #include @@ -97,7 +99,7 @@ static void rkspi_set_clk(struct rockchip_spi_priv *priv, uint speed) /* Round up to the next even 16bit number */ clk_div = (clk_div + 1) & 0xfffe; - debug("spi speed %u, div %u\n", speed, clk_div); + log_debug("spi speed %u, div %u\n", speed, clk_div); clrsetbits_le32(&priv->regs->baudr, 0xffff, clk_div); priv->last_speed_hz = speed; @@ -118,10 +120,8 @@ static int rkspi_wait_till_not_busy(struct rockchip_spi *regs) return 0; } -static void spi_cs_activate(struct udevice *dev, uint cs) +static void spi_cs_activate_bus(struct rockchip_spi_priv *priv, uint cs) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; /* If it's too soon to do another transaction, wait */ @@ -143,10 +143,8 @@ static void spi_cs_activate(struct udevice *dev, uint cs) udelay(priv->activate_delay_us); } -static void spi_cs_deactivate(struct udevice *dev, uint cs) +static void spi_cs_deactivate_bus(struct rockchip_spi_priv *priv, uint cs) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; debug("deactivate cs%u\n", cs); @@ -157,54 +155,6 @@ static void spi_cs_deactivate(struct udevice *dev, uint cs) priv->last_transaction_us = timer_get_us(); } -#if CONFIG_IS_ENABLED(OF_PLATDATA) -static int conv_of_platdata(struct udevice *dev) -{ - struct rockchip_spi_platdata *plat = dev->platdata; - struct dtd_rockchip_rk3288_spi *dtplat = &plat->of_plat; - struct rockchip_spi_priv *priv = dev_get_priv(dev); - int ret; - - priv->base = dtplat->reg[0]; - priv->frequency = 20000000; - ret = clk_get_by_driver_info(dev, dtplat->clocks, &priv->clk); - if (ret < 0) - return ret; - dev->req_seq = 0; - - return 0; -} -#endif - -static int rockchip_spi_ofdata_to_platdata(struct udevice *bus) -{ -#if !CONFIG_IS_ENABLED(OF_PLATDATA) - struct rockchip_spi_priv *priv = dev_get_priv(bus); - int ret; - - priv->base = dev_read_addr(bus); - - ret = clk_get_by_index(bus, 0, &priv->clk); - if (ret < 0) { - debug("%s: Could not get clock for %s: %d\n", __func__, - bus->name, ret); - return ret; - } - - priv->frequency = - dev_read_u32_default(bus, "spi-max-frequency", 50000000); - priv->deactivate_delay_us = - dev_read_u32_default(bus, "spi-deactivate-delay", 0); - priv->activate_delay_us = - dev_read_u32_default(bus, "spi-activate-delay", 0); - - debug("%s: base=%x, max-frequency=%d, deactivate_delay=%d\n", - __func__, (uint)priv->base, priv->frequency, - priv->deactivate_delay_us); -#endif - - return 0; -} static int rockchip_spi_calc_modclk(ulong max_freq) { @@ -234,17 +184,10 @@ static int rockchip_spi_calc_modclk(ulong max_freq) return gpll_hz / div; } -static int rockchip_spi_probe(struct udevice *bus) +static int rockchip_spi_probe_(struct rockchip_spi_priv *priv) { - struct rockchip_spi_priv *priv = dev_get_priv(bus); - int ret; + int ret, rate; - debug("%s: probe\n", __func__); -#if CONFIG_IS_ENABLED(OF_PLATDATA) - ret = conv_of_platdata(bus); - if (ret) - return ret; -#endif priv->regs = (struct rockchip_spi *)priv->base; priv->last_transaction_us = timer_get_us(); @@ -255,24 +198,30 @@ static int rockchip_spi_probe(struct udevice *bus) priv->max_freq = ROCKCHIP_SPI_MAX_RATE; /* Find a module-input clock that fits with the max_freq setting */ - ret = clk_set_rate(&priv->clk, - rockchip_spi_calc_modclk(priv->max_freq)); + log_debug("priv->max_freq=%d, modclk=%d\n", priv->max_freq, + rockchip_spi_calc_modclk(priv->max_freq)); + rate = rockchip_spi_calc_modclk(priv->max_freq); + if (!CONFIG_IS_ENABLED(TINY_SPI)) { + log_debug("clk=%s, id=%ld\n", priv->clk.dev->name, + priv->clk.id); + ret = clk_set_rate(&priv->clk, rate); + } else { + log_debug("clk=%s, id=%ld\n", priv->tiny_clk.tdev->name, + priv->tiny_clk.id); + ret = tiny_clk_set_rate(&priv->tiny_clk, rate); + } if (ret < 0) { debug("%s: Failed to set clock: %d\n", __func__, ret); - return ret; + return log_ret(ret); } priv->input_rate = ret; - if (dev_get_driver_data(bus) == RK_SPI_RK33XX) - priv->master_manages_fifo = true; debug("%s: rate = %u\n", __func__, priv->input_rate); return 0; } -static int rockchip_spi_claim_bus(struct udevice *dev) +static int rockchip_spi_claim_bus_(struct rockchip_spi_priv *priv) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; uint ctrlr0; @@ -323,21 +272,16 @@ static int rockchip_spi_claim_bus(struct udevice *dev) return 0; } -static int rockchip_spi_release_bus(struct udevice *dev) +static int rockchip_spi_release_bus_(struct rockchip_spi_priv *priv) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); - rkspi_enable_chip(priv->regs, false); return 0; } -static inline int rockchip_spi_16bit_reader(struct udevice *dev, - u8 **din, int *len) +static int rockchip_spi_16bit_reader(struct rockchip_spi_priv *priv, u8 **din, + int *len) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; const u32 saved_ctrlr0 = readl(®s->ctrlr0); #if defined(DEBUG) @@ -359,7 +303,6 @@ static inline int rockchip_spi_16bit_reader(struct udevice *dev, if (priv->master_manages_fifo) max_chunk_size = ROCKCHIP_SPI_MAX_TRANLEN; - // rockchip_spi_configure(dev, mode, size) rkspi_enable_chip(regs, false); clrsetbits_le32(®s->ctrlr0, TMOD_MASK << TMOD_SHIFT, @@ -376,6 +319,7 @@ static inline int rockchip_spi_16bit_reader(struct udevice *dev, while (frames) { u32 chunk_size = min(frames, max_chunk_size); + log_debug("frames=%u\n", frames); frames -= chunk_size; writew(chunk_size - 1, ®s->ctrlr1); @@ -392,6 +336,7 @@ static inline int rockchip_spi_16bit_reader(struct udevice *dev, *in++ = val & 0xff; *in++ = val >> 8; } + log_debug("chunk_size=%u\n", chunk_size); } while (chunk_size); rkspi_enable_chip(regs, false); @@ -405,16 +350,14 @@ static inline int rockchip_spi_16bit_reader(struct udevice *dev, #endif /* Restore the original transfer setup and return error-free. */ writel(saved_ctrlr0, ®s->ctrlr0); + return 0; } -static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) +static int rockchip_spi_xfer_(struct rockchip_spi_priv *priv, uint bitlen, + const void *dout, void *din, ulong flags, uint cs) { - struct udevice *bus = dev->parent; - struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; - struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); int len = bitlen >> 3; const u8 *out = dout; u8 *in = din; @@ -428,7 +371,7 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Assert CS before transfer */ if (flags & SPI_XFER_BEGIN) - spi_cs_activate(dev, slave_plat->cs); + spi_cs_activate_bus(priv, cs); /* * To ensure fast loading of firmware images (e.g. full U-Boot @@ -437,12 +380,13 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, * FIFO element. */ if (!out) - ret = rockchip_spi_16bit_reader(dev, &in, &len); + ret = rockchip_spi_16bit_reader(priv, &in, &len); /* This is the original 8bit reader/writer code */ while (len > 0) { int todo = min(len, ROCKCHIP_SPI_MAX_TRANLEN); + log_debug("todo=%d\n", todo); rkspi_enable_chip(regs, false); writel(todo - 1, ®s->ctrlr1); rkspi_enable_chip(regs, true); @@ -481,13 +425,43 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Deassert CS after transfer */ if (flags & SPI_XFER_END) - spi_cs_deactivate(dev, slave_plat->cs); + spi_cs_deactivate_bus(priv, cs); rkspi_enable_chip(regs, false); return ret; } +#if !CONFIG_IS_ENABLED(TINY_SPI) +static int rockchip_spi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + + return rockchip_spi_claim_bus_(priv); +} + +static int rockchip_spi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + + rockchip_spi_release_bus_(priv); + + return 0; +} + +static int rockchip_spi_xfer(struct udevice *dev, uint bitlen, + const void *dout, void *din, ulong flags) +{ + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + + return rockchip_spi_xfer_(priv, bitlen, dout, din, flags, + slave_plat->cs); +} + static int rockchip_spi_set_speed(struct udevice *bus, uint speed) { struct rockchip_spi_priv *priv = dev_get_priv(bus); @@ -510,6 +484,72 @@ static int rockchip_spi_set_mode(struct udevice *bus, uint mode) return 0; } +static int conv_of_platdata(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct rockchip_spi_platdata *plat = dev->platdata; + struct dtd_rockchip_rk3288_spi *dtplat = &plat->of_plat; + struct rockchip_spi_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dtplat->reg[0]; + priv->frequency = 20000000; + ret = clk_get_by_driver_info(dev, dtplat->clocks, &priv->clk); + if (ret < 0) + return log_ret(ret); + dev->req_seq = 0; +#endif + + return 0; +} + +static int rockchip_spi_probe(struct udevice *bus) +{ + struct rockchip_spi_priv *priv = dev_get_priv(bus); + int ret; + + debug("%s: probe\n", __func__); + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + ret = conv_of_platdata(bus); + if (ret) + return log_ret(ret); + } + if (dev_get_driver_data(bus) == RK_SPI_RK33XX) + priv->master_manages_fifo = true; + + return rockchip_spi_probe_(priv); +} + +static int rockchip_spi_ofdata_to_platdata(struct udevice *bus) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rockchip_spi_priv *priv = dev_get_priv(bus); + int ret; + + priv->base = dev_read_addr(bus); + + ret = clk_get_by_index(bus, 0, &priv->clk); + if (ret < 0) { + debug("%s: Could not get clock for %s: %d\n", __func__, + bus->name, ret); + return ret; + } + + priv->frequency = + dev_read_u32_default(bus, "spi-max-frequency", 50000000); + priv->deactivate_delay_us = + dev_read_u32_default(bus, "spi-deactivate-delay", 0); + priv->activate_delay_us = + dev_read_u32_default(bus, "spi-activate-delay", 0); + + debug("%s: base=%x, max-frequency=%d, deactivate_delay=%d\n", + __func__, (uint)priv->base, priv->frequency, + priv->deactivate_delay_us); +#endif + + return 0; +} + static const struct dm_spi_ops rockchip_spi_ops = { .claim_bus = rockchip_spi_claim_bus, .release_bus = rockchip_spi_release_bus, @@ -542,4 +582,87 @@ U_BOOT_DRIVER(rockchip_rk3288_spi) = { .probe = rockchip_spi_probe, }; +#else /* TINY_SPI */ +static int rockchip_tiny_spi_claim_bus(struct tinydev *tdev) +{ + struct tinydev *tbus = tinydev_get_parent(tdev); + struct rockchip_spi_priv *priv = tinydev_get_priv(tbus); + + return rockchip_spi_claim_bus_(priv); +} + +static int rockchip_tiny_spi_release_bus(struct tinydev *tdev) +{ + struct tinydev *tbus = tinydev_get_parent(tdev); + struct rockchip_spi_priv *priv = tinydev_get_priv(tbus); + + rockchip_spi_release_bus_(priv); + + return 0; +} + +static int rockchip_tiny_set_speed_mode(struct tinydev *tbus, uint speed_hz, + uint mode) +{ + struct rockchip_spi_priv *priv = tinydev_get_priv(tbus); + + /* Clamp to the maximum frequency specified in the DTS */ + if (speed_hz > priv->max_freq) + speed_hz = priv->max_freq; + + priv->speed_hz = speed_hz; + priv->mode = mode; + + return 0; +} + +static int rockchip_tiny_spi_xfer(struct tinydev *tdev, uint bitlen, + const void *dout, void *din, ulong flags) +{ + log_debug("xfer\n"); + struct tinydev *tbus = tinydev_get_parent(tdev); + struct rockchip_spi_priv *priv = tinydev_get_priv(tbus); + struct dm_spi_slave_platdata *slave_plat; + + slave_plat = tinydev_get_data(tdev, DEVDATAT_PARENT_PLAT); + log_debug("priv=%p, slave_plat=%p, cs=%d\n", priv, slave_plat, + slave_plat->cs); + + return rockchip_spi_xfer_(priv, bitlen, dout, din, flags, + slave_plat->cs); +} + +static int rockchip_spi_tiny_probe(struct tinydev *tdev) +{ + log_debug("start\n"); + struct rockchip_spi_priv *priv = tinydev_get_priv(tdev); + struct dtd_rockchip_rk3288_spi *dtplat = tdev->dtplat; + int ret; + + priv->base = dtplat->reg[0]; + priv->frequency = 20000000; + ret = tiny_clk_get_by_driver_info(dtplat->clocks, &priv->tiny_clk); + if (ret < 0) + return log_ret(ret); + log_debug("priv->base=%lx\n", priv->base); + + return rockchip_spi_probe_(priv); +} + +static struct tiny_spi_ops rockchip_spi_tiny_ops = { + .claim_bus = rockchip_tiny_spi_claim_bus, + .release_bus = rockchip_tiny_spi_release_bus, + .set_speed_mode = rockchip_tiny_set_speed_mode, + .xfer = rockchip_tiny_spi_xfer, +}; + +U_BOOT_TINY_DRIVER(rockchip_rk3288_spi) = { + .uclass_id = UCLASS_SPI, + .probe = rockchip_spi_tiny_probe, + .ops = &rockchip_spi_tiny_ops, + DM_TINY_PRIV(, \ + sizeof(struct rockchip_spi_priv)) +}; +#endif + U_BOOT_DRIVER_ALIAS(rockchip_rk3288_spi, rockchip_rk3368_spi) diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index cffd9cf0b0..1d143a417d 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -3,6 +3,8 @@ * Copyright (c) 2014 Google, Inc */ +#define LOG_CATEGORY UCLASS_SPI + #include #include #include @@ -18,6 +20,8 @@ DECLARE_GLOBAL_DATA_PTR; #define SPI_DEFAULT_SPEED_HZ 100000 +#if !CONFIG_IS_ENABLED(TINY_SPI) + static int spi_set_speed_mode(struct udevice *bus, int speed, int mode) { struct dm_spi_ops *ops; @@ -520,3 +524,76 @@ U_BOOT_DRIVER(spi_generic_drv) = { .name = "spi_generic_drv", .id = UCLASS_SPI_GENERIC, }; +#else /* TINY_SPI */ +int tiny_spi_claim_bus(struct tinydev *tdev) +{ + log_debug("claim\n"); + struct tinydev *bus = tinydev_get_parent(tdev); + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + struct spi_slave *slave = tinydev_get_data(tdev, DEVDATAT_PARENT_PRIV); + int speed = 0; + int ret; + + log_debug("bus=%s\n", bus->name); + log_debug("slave=%p\n", slave); + speed = slave->max_hz; + if (!speed) + speed = SPI_DEFAULT_SPEED_HZ; + log_debug("speed=%d\n", speed); + if (speed != slave->speed) { + int ret = tiny_spi_set_speed_mode(bus, speed, slave->mode); + + if (ret) + return log_msg_ret("speed", ret); + slave->speed = speed; + } + + if (ops->claim_bus) { + ret = ops->claim_bus(tdev); + if (ret) + return log_msg_ret("claim", ret); + } + + return 0; +} + +int tiny_spi_release_bus(struct tinydev *tdev) +{ + log_debug("release\n"); + struct tinydev *bus = tinydev_get_parent(tdev); + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + int ret; + + if (ops->release_bus) { + ret = ops->release_bus(tdev); + if (ret) + return log_ret(ret); + } + + return 0; +} + +int tiny_spi_xfer(struct tinydev *tdev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + log_debug("xfer\n"); + struct tinydev *bus = tinydev_get_parent(tdev); + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + + if (!ops->xfer) + return -ENOSYS; + + return ops->xfer(tdev, bitlen, dout, din, flags); +} + +int tiny_spi_set_speed_mode(struct tinydev *bus, uint hz, uint mode) +{ + struct tiny_spi_ops *ops = tiny_spi_get_ops(bus); + + if (!ops->set_speed_mode) + return -ENOSYS; + + return ops->set_speed_mode(bus, hz, mode); +} + +#endif /* TINY_SPI */ diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 4be7433404..7b566dc2b4 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -22,6 +22,15 @@ config SPL_SYSRESET to effect a reset. The uclass will try all available drivers when reset_walk() is called. +config SPL_TINY_SYSRESET + bool "Support tiny sysreset drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config TPL_SYSRESET bool "Enable support for system reset drivers in TPL mode" depends on SYSRESET && TPL_DM @@ -31,6 +40,15 @@ config TPL_SYSRESET to effect a reset. The uclass will try all available drivers when reset_walk() is called. +config TPL_TINY_SYSRESET + bool "Support tiny sysreset drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + if SYSRESET if CMD_POWEROFF diff --git a/drivers/sysreset/sysreset-uclass.c b/drivers/sysreset/sysreset-uclass.c index 995240f0cb..73561dce3e 100644 --- a/drivers/sysreset/sysreset-uclass.c +++ b/drivers/sysreset/sysreset-uclass.c @@ -21,6 +21,7 @@ #include #include +#if !CONFIG_IS_ENABLED(TINY_SYSRESET) int sysreset_request(struct udevice *dev, enum sysreset_t type) { struct sysreset_ops *ops = sysreset_get_ops(dev); @@ -51,25 +52,6 @@ int sysreset_get_last(struct udevice *dev) return ops->get_last(dev); } -int sysreset_walk(enum sysreset_t type) -{ - struct udevice *dev; - int ret = -ENOSYS; - - while (ret != -EINPROGRESS && type < SYSRESET_COUNT) { - for (uclass_first_device(UCLASS_SYSRESET, &dev); - dev; - uclass_next_device(&dev)) { - ret = sysreset_request(dev, type); - if (ret == -EINPROGRESS) - break; - } - type++; - } - - return ret; -} - int sysreset_get_last_walk(void) { struct udevice *dev; @@ -90,39 +72,6 @@ int sysreset_get_last_walk(void) return value; } -void sysreset_walk_halt(enum sysreset_t type) -{ - int ret; - - ret = sysreset_walk(type); - - /* Wait for the reset to take effect */ - if (ret == -EINPROGRESS) - mdelay(100); - - /* Still no reset? Give up */ - log_err("System reset not supported on this platform\n"); - hang(); -} - -/** - * reset_cpu() - calls sysreset_walk(SYSRESET_WARM) - */ -void reset_cpu(ulong addr) -{ - sysreset_walk_halt(SYSRESET_WARM); -} - - -int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) -{ - printf("resetting ...\n"); - - sysreset_walk_halt(SYSRESET_COLD); - - return 0; -} - #if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -161,3 +110,74 @@ UCLASS_DRIVER(sysreset) = { .name = "sysreset", .post_bind = sysreset_post_bind, }; +#else +int tiny_sysreset_request(struct tinydev *tdev, enum sysreset_t type) +{ + struct tiny_sysreset_ops *ops = tiny_sysreset_get_ops(tdev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(tdev, type); +} +#endif + +int sysreset_walk(enum sysreset_t type) +{ + int ret = -ENOSYS; + + while (ret != -EINPROGRESS && type < SYSRESET_COUNT) { + if (!CONFIG_IS_ENABLED(TINY_SYSRESET)) { + struct udevice *dev; + + for (uclass_first_device(UCLASS_SYSRESET, &dev); + dev; + uclass_next_device(&dev)) { + ret = sysreset_request(dev, type); + if (ret == -EINPROGRESS) + break; + } + } else { + struct tinydev *tdev; + + tdev = tiny_dev_get(UCLASS_SYSRESET, 0); + if (tdev) + ret = tiny_sysreset_request(tdev, type); + } + type++; + } + + return ret; +} + +void sysreset_walk_halt(enum sysreset_t type) +{ + int ret; + + ret = sysreset_walk(type); + + /* Wait for the reset to take effect */ + if (ret == -EINPROGRESS) + mdelay(100); + + /* Still no reset? Give up */ + log_err("System reset not supported on this platform\n"); + hang(); +} + +/** + * reset_cpu() - calls sysreset_walk(SYSRESET_WARM) + */ +void reset_cpu(ulong addr) +{ + sysreset_walk_halt(SYSRESET_WARM); +} + +int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + printf("resetting ...\n"); + + sysreset_walk_halt(SYSRESET_COLD); + + return 0; +} diff --git a/drivers/sysreset/sysreset_rockchip.c b/drivers/sysreset/sysreset_rockchip.c index 0fc6b683f2..195ebc229a 100644 --- a/drivers/sysreset/sysreset_rockchip.c +++ b/drivers/sysreset/sysreset_rockchip.c @@ -3,6 +3,8 @@ * (C) Copyright 2017 Rockchip Electronics Co., Ltd */ +#define LOG_CATEGORY UCLASS_SYSRESET + #include #include #include @@ -13,9 +15,9 @@ #include #include -int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type) +static int rockchip_sysreset_request_(struct sysreset_reg *priv, + enum sysreset_t type) { - struct sysreset_reg *offset = dev_get_priv(dev); unsigned long cru_base = (unsigned long)rockchip_get_cru(); if (IS_ERR_VALUE(cru_base)) @@ -23,10 +25,10 @@ int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type) switch (type) { case SYSRESET_WARM: - writel(0xeca8, cru_base + offset->glb_srst_snd_value); + writel(0xeca8, cru_base + priv->glb_srst_snd_value); break; case SYSRESET_COLD: - writel(0xfdb9, cru_base + offset->glb_srst_fst_value); + writel(0xfdb9, cru_base + priv->glb_srst_fst_value); break; default: return -EPROTONOSUPPORT; @@ -35,12 +37,57 @@ int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type) return -EINPROGRESS; } -static struct sysreset_ops rockchip_sysreset = { +#if !CONFIG_IS_ENABLED(TINY_SYSRESET) +int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + struct sysreset_reg *priv = dev_get_priv(dev); + + return rockchip_sysreset_request_(priv, type); +} + +static int rockchip_sysreset_probe(struct udevice *dev) +{ + return rockchip_cru_setup_sysreset(dev); +} + +static struct sysreset_ops rockchip_sysreset_ops = { .request = rockchip_sysreset_request, }; -U_BOOT_DRIVER(sysreset_rockchip) = { +static const struct udevice_id rockchip_sysreset_ids[] = { + { .compatible = "rockchip,sysreset" }, + { } +}; + +U_BOOT_DRIVER(rockchip_sysreset) = { .name = "rockchip_sysreset", .id = UCLASS_SYSRESET, - .ops = &rockchip_sysreset, + .of_match = rockchip_sysreset_ids, + .ops = &rockchip_sysreset_ops, + .probe = rockchip_sysreset_probe, + .priv_auto_alloc_size = sizeof(struct sysreset_reg), +}; +#else +int rockchip_sysreset_tiny_request(struct tinydev *tdev, enum sysreset_t type) +{ + struct sysreset_reg *priv = tinydev_get_priv(tdev); + + return rockchip_sysreset_request_(priv, type); +} + +static int rockchip_sysreset_tiny_probe(struct tinydev *tdev) +{ + return rockchip_cru_setup_tiny_sysreset(tdev); +} + +static struct tiny_sysreset_ops rockchip_sysreset_tiny_ops = { + .request = rockchip_sysreset_tiny_request, +}; + +U_BOOT_TINY_DRIVER(rockchip_sysreset) = { + .uclass_id = UCLASS_SYSRESET, + .probe = rockchip_sysreset_tiny_probe, + .ops = &rockchip_sysreset_tiny_ops, + DM_TINY_PRIV(, sizeof(struct sysreset_reg)) }; +#endif diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 8c78792cc9..d6f6c3080f 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -23,6 +23,7 @@ #include #include #include +#include typedef struct global_data { struct bd_info *bd; @@ -68,6 +69,9 @@ typedef struct global_data { struct udevice *dm_root_f; /* Pre-relocation root instance */ struct list_head uclass_root; /* Head of core tree */ #endif +#if CONFIG_IS_ENABLED(TINY) + struct tinydev_info tinydev_info; +#endif #ifdef CONFIG_TIMER struct udevice *timer; /* Timer instance for Driver Model */ #endif @@ -95,7 +99,7 @@ typedef struct global_data { #if CONFIG_VAL(SYS_MALLOC_F_LEN) unsigned long malloc_base; /* base address of early malloc() */ unsigned long malloc_limit; /* limit address */ - unsigned long malloc_ptr; /* current address */ + unsigned long malloc_ptr; /* offset of next byte to allocate */ #endif #ifdef CONFIG_PCI struct pci_controller *hose; /* PCI hose for early use */ @@ -105,6 +109,7 @@ typedef struct global_data { int pcidelay_done; #endif struct udevice *cur_serial_dev; /* current serial device */ + struct tinydev *tiny_serial; struct arch_global_data arch; /* architecture-specific data */ #ifdef CONFIG_CONSOLE_RECORD struct membuff console_out; /* console output */ diff --git a/include/clk-uclass.h b/include/clk-uclass.h index dac42dab36..b652975195 100644 --- a/include/clk-uclass.h +++ b/include/clk-uclass.h @@ -100,4 +100,15 @@ struct clk_ops { int (*disable)(struct clk *clk); }; +struct tiny_clk_ops { + /** + * set_rate() - Set current clock rate. + * + * @tclk: The clock to manipulate. + * @rate: New clock rate in Hz. + * @return new rate, or -ve error code. + */ + ulong (*set_rate)(struct tiny_clk *tclk, ulong rate); +}; + #endif diff --git a/include/clk.h b/include/clk.h index a62e2efa2c..f3348a3b9e 100644 --- a/include/clk.h +++ b/include/clk.h @@ -63,9 +63,8 @@ struct clk { long long rate; /* in HZ */ u32 flags; int enable_count; - /* - * Written by of_xlate. In the future, we might add more fields here. - */ + + /* Written by of_xlate. In the future, we might add more fields here */ unsigned long id; unsigned long data; }; @@ -87,11 +86,22 @@ struct clk_bulk { unsigned int count; }; +struct tiny_clk { + struct tinydev *tdev; + long long rate; /* in HZ */ + + /* Written by of_xlate. In the future, we might add more fields here */ + unsigned long id; +}; + #if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) struct phandle_1_arg; int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells, struct clk *clk); +int tiny_clk_get_by_driver_info(struct phandle_1_arg *cells, + struct tiny_clk *tclk); + /** * clk_get_by_index - Get/request a clock by integer index. * @@ -455,6 +465,10 @@ int clk_get_by_id(ulong id, struct clk **clkp); */ bool clk_dev_binded(struct clk *clk); +int tiny_clk_request(struct tinydev *tdev, struct tiny_clk *tclk); + +ulong tiny_clk_set_rate(struct tiny_clk *tclk, ulong rate); + #else /* CONFIG_IS_ENABLED(CLK) */ static inline int clk_request(struct udevice *dev, struct clk *clk) @@ -526,6 +540,7 @@ static inline bool clk_dev_binded(struct clk *clk) { return false; } + #endif /* CONFIG_IS_ENABLED(CLK) */ /** @@ -539,6 +554,17 @@ static inline bool clk_valid(struct clk *clk) return clk && !!clk->dev; } +/** + * tiny_clk_valid() - check if clk is valid + * + * @tiny_clk: the clock to check + * @return true if valid, or false + */ +static inline bool tiny_clk_valid(struct tiny_clk *tclk) +{ + return tclk && !!tclk->tdev; +} + int soc_clk_dump(void); #endif diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 1b9151714c..4a715a966a 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -332,6 +332,13 @@ struct mtd_info { }; #if IS_ENABLED(CONFIG_DM) +struct tiny_mtd_info { + uint64_t size; // Total size of the MTD +// int (*_read) (struct mtd_info *mtd, loff_t from, size_t len, +// size_t *retlen, u_char *buf); + void *priv; +}; + static inline void mtd_set_of_node(struct mtd_info *mtd, const struct device_node *np) { @@ -354,7 +361,7 @@ static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) { return NULL; } -#endif +#endif /* DM */ static inline bool mtd_is_partition(const struct mtd_info *mtd) { @@ -402,7 +409,7 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys); int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len); -#endif +#endif /* __UBOOT__ */ unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, unsigned long offset, unsigned long flags); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, @@ -430,7 +437,7 @@ int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len); #ifndef __UBOOT__ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); -#endif +#endif /* __UBOOT__ */ static inline void mtd_sync(struct mtd_info *mtd) { @@ -456,7 +463,7 @@ static inline void mtd_resume(struct mtd_info *mtd) if (mtd->_resume) mtd->_resume(mtd); } -#endif +#endif /* __UBOOT__ */ static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) { @@ -533,7 +540,7 @@ struct mtd_notifier { extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); -#endif +#endif /* __UBOOT__ */ void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size); #ifdef CONFIG_MTD_PARTITIONS @@ -544,7 +551,7 @@ static inline void mtd_erase_callback(struct erase_info *instr) if (instr->callback) instr->callback(instr); } -#endif +#endif /* CONFIG_MTD_PARTITIONS */ static inline int mtd_is_bitflip(int err) { return err == -EUCLEAN; @@ -580,7 +587,7 @@ static inline int del_mtd_partitions(struct mtd_info *mtd) { return 0; } -#endif +#endif /* CONFIG_MTD_PARTITIONS */ struct mtd_info *__mtd_next_device(int i); #define mtd_for_each_device(mtd) \ @@ -598,5 +605,5 @@ bool mtd_dev_list_updated(void); int mtd_search_alternate_name(const char *mtdname, char *altname, unsigned int max_len); -#endif +#endif /* __UBOOT__ */ #endif /* __MTD_MTD_H__ */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 233fdc341a..81b61e47bc 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -352,6 +352,24 @@ struct spi_nor { u32 erase_size; }; +struct tiny_spi_nor { + struct tinydev *tdev; /* SPI device */ + struct tiny_mtd_info mtd; + struct spi_slave *spi; + const struct flash_info *info; + u8 addr_width; + u8 read_opcode; + u8 read_dummy; + enum spi_nor_protocol read_proto; + u32 flags; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + void *priv; + u32 size; +}; + +int tiny_spi_nor_read(struct tiny_mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); + static inline void spi_nor_set_flash_node(struct spi_nor *nor, const struct device_node *np) { @@ -435,6 +453,10 @@ struct spi_nor_hwcaps { * * Return: 0 for success, others for failure. */ +#if !CONFIG_IS_ENABLED(TINY_SPI) int spi_nor_scan(struct spi_nor *nor); +#else +int spi_nor_scan(struct tiny_spi_nor *nor); +#endif #endif diff --git a/include/log.h b/include/log.h index 63052f74eb..a417333153 100644 --- a/include/log.h +++ b/include/log.h @@ -16,6 +16,7 @@ #include struct cmd_tbl; +struct global_data; /** Log levels supported, ranging from most to least important */ enum log_level_t { @@ -60,6 +61,7 @@ enum log_category_t { LOGC_DEVRES, /* Device resources (devres_... functions) */ /* Advanced Configuration and Power Interface (ACPI) */ LOGC_ACPI, + LOGC_TINYDEV, /* Tiny devices (struct tinydev) */ LOGC_COUNT, /* Number of log categories */ LOGC_END, /* Sentinel value for a list of log categories */ @@ -484,4 +486,8 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); } +void log_check(const char *msg); + +void log_fixup_for_gd_move(struct global_data *new_gd); + #endif diff --git a/include/malloc.h b/include/malloc.h index f66c2e8617..566c2d3ee7 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -935,6 +935,9 @@ int initf_malloc(void); void *malloc_simple(size_t size); void *memalign_simple(size_t alignment, size_t bytes); +uint malloc_ptr_to_ofs(void *ptr); +void *malloc_ofs_to_ptr(uint offset); + #pragma GCC visibility push(hidden) # if __STD_C diff --git a/include/ns16550.h b/include/ns16550.h index 18c9077755..1d5c311bcd 100644 --- a/include/ns16550.h +++ b/include/ns16550.h @@ -233,12 +233,11 @@ void NS16550_reinit(NS16550_t com_port, int baud_divisor); * Given the UART input clock and required baudrate, calculate the divisor * that should be used. * - * @port: UART port * @clock: UART input clock speed in Hz * @baudrate: Required baud rate * @return baud rate divisor that should be used */ -int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate); +int ns16550_calc_divisor(int clock, int baudrate); /** * ns16550_serial_ofdata_to_platdata() - convert DT to platform data @@ -266,3 +265,7 @@ int ns16550_serial_probe(struct udevice *dev); * These should be used by the client driver for the driver's 'ops' member */ extern const struct dm_serial_ops ns16550_serial_ops; + +int ns16550_tiny_probe_plat(struct ns16550_platdata *plat); +int ns16550_tiny_setbrg(struct ns16550_platdata *plat, int baud_rate); +int ns16550_tiny_putc(struct ns16550_platdata *plat, const char ch); diff --git a/include/ram.h b/include/ram.h index 67e22d76c9..434e65a85a 100644 --- a/include/ram.h +++ b/include/ram.h @@ -34,4 +34,29 @@ struct ram_ops { */ int ram_get_info(struct udevice *dev, struct ram_info *info); +/** + * struct tiny_ram_ops - Operations for tiny RAM devices + */ +struct tiny_ram_ops { + /** + * get_info() - Get basic memory info + * + * @dev: Device to check (UCLASS_RAM) + * @info: Place to put info + * @return 0 if OK, -ve on error + */ + int (*get_info)(struct tinydev *dev, struct ram_info *info); +}; + +#define tiny_ram_get_ops(dev) ((struct tiny_ram_ops *)(dev)->drv->ops) + +/** + * tiny_ram_get_info() - Get information about a RAM device + * + * @dev: Device to check (UCLASS_RAM) + * @info: Returns RAM info + * @return 0 if OK, -ve on error + */ +int tiny_ram_get_info(struct tinydev *tdev, struct ram_info *info); + #endif diff --git a/include/regmap.h b/include/regmap.h index 30183c5e71..01c3e62317 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -318,7 +318,6 @@ int regmap_init_mem(ofnode node, struct regmap **mapp); * regmap_init_mem_platdata() - Set up a new memory register map for * of-platdata * - * @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 @@ -330,8 +329,7 @@ int regmap_init_mem(ofnode node, struct regmap **mapp); * Use regmap_uninit() to free it. * */ -int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count, - struct regmap **mapp); +int regmap_init_mem_platdata(fdt_val_t *reg, int count, struct regmap **mapp); int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index); diff --git a/include/serial.h b/include/serial.h index 5e384db8ee..d53cb26432 100644 --- a/include/serial.h +++ b/include/serial.h @@ -9,6 +9,7 @@ void serial_initialize(void); #ifdef CONFIG_USB_TTY struct stdio_dev; +struct tinydev; int usbtty_getc(struct stdio_dev *dev); void usbtty_putc(struct stdio_dev *dev, const char c); @@ -115,7 +116,7 @@ struct serial_device_info { #define SERIAL_DEFAULT_CLOCK (16 * 115200) /** - * struct struct dm_serial_ops - Driver model serial operations + * struct dm_serial_ops - Driver model serial operations * * The uclass interface is implemented by all serial devices which use * driver model. @@ -243,6 +244,48 @@ struct serial_dev_priv { /* Access the serial operations for a device */ #define serial_get_ops(dev) ((struct dm_serial_ops *)(dev)->driver->ops) +/** + * struct tiny_serial_ops - Tiny operations support for serial + * + * This interface is optional for serial drivers and depends on + * CONFIG_SPL/TPL_TINY_SERIAL + */ +struct tiny_serial_ops { + /** + * setbrg() - Set up the baud rate generator + * + * Adjust baud rate divisors to set up a new baud rate for this + * device. Not all devices will support all rates. If the rate + * cannot be supported, the driver is free to select the nearest + * available rate. or return -EINVAL if this is not possible. + * + * @dev: Device pointer + * @baudrate: New baud rate to use + * @return 0 if OK, -ve on error + */ + int (*setbrg)(struct tinydev *tdev, int baudrate); + /** + * putc() - Write a character + * + * @dev: Device pointer + * @ch: character to write + * @return 0 if OK, -ve on error + */ + int (*putc)(struct tinydev *tdev, const char ch); +}; + +#define tiny_serial_get_ops(dev) ((struct tiny_serial_ops *)(dev)->drv->ops) + +/** + * tiny_serial_setbrg() - Set the baud rate + * + * Set the baud rate for a tiny-serial device + * + * @tdev: Tiny device + * @baudrate: Baud rate to set (e.g. 115200) + */ +int tiny_serial_setbrg(struct tinydev *tdev, int baudrate); + /** * serial_getconfig() - Get the uart configuration * (parity, 5/6/7/8 bits word length, stop bits) diff --git a/include/spi.h b/include/spi.h index 9b4fb8dc0b..df96fcf307 100644 --- a/include/spi.h +++ b/include/spi.h @@ -12,6 +12,8 @@ #include #include +struct spi_mem_op; + /* SPI mode flags */ #define SPI_CPHA BIT(0) /* clock phase */ #define SPI_CPOL BIT(1) /* clock polarity */ @@ -133,6 +135,7 @@ enum spi_polarity { struct spi_slave { #if CONFIG_IS_ENABLED(DM_SPI) struct udevice *dev; /* struct spi_slave is dev->parentdata */ + struct tinydev *tdev; uint max_hz; uint speed; #else @@ -542,6 +545,33 @@ struct dm_spi_emul_ops { const void *dout, void *din, unsigned long flags); }; +struct tiny_spi_ops { + int (*claim_bus)(struct tinydev *tdev); + int (*release_bus)(struct tinydev *tdev); + int (*xfer)(struct tinydev *tdev, uint bitlen, const void *dout, + void *din, ulong flags); + /** + * Set transfer speed and mode + * This sets a new speed to be applied for next tiny_spi_xfer(). + * @bus: The SPI bus + * @hz: The transfer speed + * @return 0 if OK, -ve on error + */ + int (*set_speed_mode)(struct tinydev *tbus, uint hz, uint mode); + + int (*adjust_op_size)(struct tinydev *tdev, struct spi_mem_op *op); + bool (*supports_op)(struct tinydev *tdev, + const struct spi_mem_op *op); + int (*exec_op)(struct tinydev *tdev, + const struct spi_mem_op *op); +}; + +int tiny_spi_claim_bus(struct tinydev *tdev); +int tiny_spi_release_bus(struct tinydev *tdev); +int tiny_spi_xfer(struct tinydev *tdev, uint bitlen, const void *dout, + void *din, ulong flags); +int tiny_spi_set_speed_mode(struct tinydev *bus, uint hz, uint mode); + /** * spi_find_bus_and_cs() - Find bus and slave devices by number * @@ -717,6 +747,7 @@ int dm_spi_get_mmap(struct udevice *dev, ulong *map_basep, uint *map_sizep, /* Access the operations for a SPI device */ #define spi_get_ops(dev) ((struct dm_spi_ops *)(dev)->driver->ops) #define spi_emul_get_ops(dev) ((struct dm_spi_emul_ops *)(dev)->driver->ops) +#define tiny_spi_get_ops(tdev) ((struct tiny_spi_ops *)(tdev)->drv->ops) #endif /* CONFIG_DM_SPI */ #endif /* _SPI_H_ */ diff --git a/include/spi_flash.h b/include/spi_flash.h index b336619487..773bc8eea2 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -178,4 +178,11 @@ static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len, return flash->flash_unlock(flash, ofs, len); } +struct tiny_spi_flash_ops { + int (*read)(struct tinydev *tdev, u32 offset, size_t len, void *buf); +}; + +int tiny_spi_flash_read(struct tinydev *tdev, u32 offset, size_t len, + void *buf); + #endif /* _SPI_FLASH_H_ */ diff --git a/include/spl.h b/include/spl.h index b31c9bb4ab..02b32b5a30 100644 --- a/include/spl.h +++ b/include/spl.h @@ -158,19 +158,23 @@ struct spl_image_info { /* * Information required to load data from a device * - * @dev: Pointer to the device, e.g. struct mmc * + * @dev: Pointer to the device (NULL if using tdev) + * @tdev: Pointer to the tiny device (NULL if using dev) * @priv: Private data for the device * @bl_len: Block length for reading in bytes * @filename: Name of the fit image file. * @read: Function to call to read from the device + * @legacy_dev: Pointer to the device, e.g. struct mmc * */ struct spl_load_info { - void *dev; + struct udevice *dev; + struct tinydev *tdev; void *priv; int bl_len; const char *filename; ulong (*read)(struct spl_load_info *load, ulong sector, ulong count, void *buf); + void *legacy_dev; /* Do not use */ }; /* diff --git a/include/syscon.h b/include/syscon.h index 3df96e3276..86a3fac1a1 100644 --- a/include/syscon.h +++ b/include/syscon.h @@ -102,4 +102,6 @@ void *syscon_get_first_range(ulong driver_data); */ struct regmap *syscon_node_to_regmap(ofnode node); +int tiny_syscon_setup(struct tinydev *tdev); + #endif diff --git a/include/sysreset.h b/include/sysreset.h index 61295e3fcb..46f727cf1b 100644 --- a/include/sysreset.h +++ b/include/sysreset.h @@ -50,6 +50,13 @@ struct sysreset_ops { #define sysreset_get_ops(dev) ((struct sysreset_ops *)(dev)->driver->ops) +struct tiny_sysreset_ops { + int (*request)(struct tinydev *tdev, enum sysreset_t type); +}; + +#define tiny_sysreset_get_ops(dev) \ + ((struct tiny_sysreset_ops *)(dev)->drv->ops) + /** * sysreset_request() - request a sysreset * @@ -116,4 +123,6 @@ void sysreset_walk_halt(enum sysreset_t type); */ void reset_cpu(ulong addr); +int tiny_sysreset_request(struct tinydev *tdev, enum sysreset_t type); + #endif From patchwork Thu Jul 2 21:10:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 240664 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Thu, 2 Jul 2020 15:10:03 -0600 Subject: [RFC PATCH v2 2/3] dm: Arch-specific changes for tiny-dm In-Reply-To: <20200702211004.1491489-1-sjg@chromium.org> References: <20200702211004.1491489-1-sjg@chromium.org> Message-ID: <20200702211004.1491489-3-sjg@chromium.org> Signed-off-by: Simon Glass --- (no changes since v1) arch/arm/dts/rk3288-u-boot.dtsi | 17 ++++-- arch/arm/dts/rk3288-veyron.dtsi | 26 ++++----- arch/arm/dts/rk3288.dtsi | 3 + arch/arm/include/asm/arch-rockchip/clock.h | 9 +++ .../include/asm/arch-rockchip/sdram_rk3288.h | 21 +++++++ arch/arm/include/asm/arch-rockchip/spi.h | 1 + arch/arm/include/asm/io.h | 18 ++++++ arch/arm/mach-rockchip/rk3288/clk_rk3288.c | 46 +++++++++++++++ arch/arm/mach-rockchip/rk3288/rk3288.c | 2 + arch/arm/mach-rockchip/rk3288/syscon_rk3288.c | 45 ++++----------- arch/arm/mach-rockchip/sdram.c | 33 ++++++++--- arch/sandbox/cpu/u-boot-spl.lds | 12 ++++ arch/sandbox/dts/sandbox.dts | 3 +- arch/sandbox/dts/sandbox.dtsi | 2 +- arch/x86/cpu/apollolake/cpu_spl.c | 5 +- arch/x86/cpu/apollolake/uart.c | 56 +++++++++++++++++++ arch/x86/dts/chromebook_coral.dts | 1 + arch/x86/lib/tpl.c | 4 +- configs/chromebook_coral_defconfig | 1 + configs/chromebook_jerry_defconfig | 11 ++++ configs/rock2_defconfig | 3 + 21 files changed, 247 insertions(+), 72 deletions(-) diff --git a/arch/arm/dts/rk3288-u-boot.dtsi b/arch/arm/dts/rk3288-u-boot.dtsi index e4e762aabf..d120eed884 100644 --- a/arch/arm/dts/rk3288-u-boot.dtsi +++ b/arch/arm/dts/rk3288-u-boot.dtsi @@ -6,6 +6,11 @@ #include "rockchip-u-boot.dtsi" / { + aliases { + ram0 = &dmc; + clk0 = &cru; + }; + chosen { u-boot,spl-boot-order = \ "same-as-spl", &emmc, &sdmmc; @@ -50,6 +55,10 @@ &cru { u-boot,dm-pre-reloc; + + sysreset { + u-boot,dm-pre-reloc; + }; }; &grf { @@ -57,17 +66,13 @@ }; &vopb { - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; }; &vopl { - u-boot,dm-pre-reloc; + u-boot,dm-pre-proper; }; &noc { u-boot,dm-pre-reloc; }; - -&gpio7 { - u-boot,dm-pre-reloc; -}; diff --git a/arch/arm/dts/rk3288-veyron.dtsi b/arch/arm/dts/rk3288-veyron.dtsi index 8754043b9b..9b10eaf1f5 100644 --- a/arch/arm/dts/rk3288-veyron.dtsi +++ b/arch/arm/dts/rk3288-veyron.dtsi @@ -10,6 +10,15 @@ #include "rk3288.dtsi" / { + aliases { + spi_flash0 = &spi_flash; + sysreset0 = &sysreset; + syscon0 = &grf; + syscon1 = &sgrf; + syscon2 = &pmu; + syscon3 = &noc; + }; + memory { reg = <0x0 0x80000000>; }; @@ -303,7 +312,7 @@ spi_flash: spiflash at 0 { u-boot,dm-pre-reloc; - compatible = "spidev", "jedec,spi-nor"; + compatible = "jedec,spi-nor"; spi-max-frequency = <20000000>; /* Reduce for Dediprog em100 pro */ reg = <0>; }; @@ -315,7 +324,6 @@ clock-frequency = <400000>; i2c-scl-falling-time-ns = <50>; /* 2.5ns measured */ i2c-scl-rising-time-ns = <100>; /* 45ns measured */ - u-boot,dm-pre-reloc; rk808: pmic at 1b { compatible = "rockchip,rk808"; @@ -328,7 +336,6 @@ rockchip,system-power-controller; wakeup-source; #clock-cells = <1>; - u-boot,dm-pre-reloc; vcc1-supply = <&vcc33_sys>; vcc2-supply = <&vcc33_sys>; @@ -601,7 +608,6 @@ }; &pinctrl { - u-boot,dm-pre-reloc; pinctrl-names = "default", "sleep"; pinctrl-0 = < /* Common for sleep and wake, but no owners */ @@ -826,15 +832,3 @@ assigned-clocks = <&cru SCLK_USBPHY480M_SRC>; assigned-clock-parents = <&cru SCLK_OTGPHY0>; }; - -&sdmmc { - u-boot,dm-pre-reloc; -}; - -&gpio3 { - u-boot,dm-pre-reloc; -}; - -&gpio8 { - u-boot,dm-pre-reloc; -}; diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi index 866fc08215..3983b746f4 100644 --- a/arch/arm/dts/rk3288.dtsi +++ b/arch/arm/dts/rk3288.dtsi @@ -608,6 +608,9 @@ <150000000>, <75000000>, <300000000>, <150000000>, <75000000>; + sysreset: sysreset { + compatible = "rockchip,sysreset"; + }; }; grf: syscon at ff770000 { diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h index 22de0aef8d..772d1423ce 100644 --- a/arch/arm/include/asm/arch-rockchip/clock.h +++ b/arch/arm/include/asm/arch-rockchip/clock.h @@ -6,6 +6,9 @@ #ifndef _ASM_ARCH_CLOCK_H #define _ASM_ARCH_CLOCK_H +/* Include this so that struct syscon_uc_info is available for dt-platdata */ +#include + /* define pll mode */ #define RKCLK_PLL_MODE_SLOW 0 #define RKCLK_PLL_MODE_NORMAL 1 @@ -156,10 +159,16 @@ void *rockchip_get_pmucru(void); struct rockchip_cru; struct rk3288_grf; +int rockchip_cru_setup_sysreset(struct udevice *dev); + +int rockchip_cru_setup_tiny_sysreset(struct tinydev *tdev); + void rk3288_clk_configure_cpu(struct rockchip_cru *cru, struct rk3288_grf *grf); int rockchip_get_clk(struct udevice **devp); +struct tinydev *tiny_rockchip_get_clk(void); + /* * rockchip_reset_bind() - Bind soft reset device as child of clock device * diff --git a/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h b/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h index 9220763fa7..3358584580 100644 --- a/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h @@ -8,6 +8,9 @@ #ifndef _ASM_ARCH_RK3288_SDRAM_H__ #define _ASM_ARCH_RK3288_SDRAM_H__ +#include +#include + struct rk3288_sdram_channel { /* * bit width in address, eg: @@ -99,4 +102,22 @@ struct rk3288_base_params { u32 odt; }; +struct rk_chan_info { + struct rk3288_ddr_pctl *pctl; + struct rk3288_ddr_publ *publ; + struct rk3288_msch *msch; +}; + +struct rk_dram_info { + struct rk_chan_info chan[2]; + struct ram_info info; + struct clk ddr_clk; + struct tiny_clk tiny_ddr_clk; + struct rockchip_cru *cru; + struct rk3288_grf *grf; + struct rk3288_sgrf *sgrf; + struct rk3288_pmu *pmu; + bool is_veyron; +}; + #endif diff --git a/arch/arm/include/asm/arch-rockchip/spi.h b/arch/arm/include/asm/arch-rockchip/spi.h index a9d210397a..594b0967b5 100644 --- a/arch/arm/include/asm/arch-rockchip/spi.h +++ b/arch/arm/include/asm/arch-rockchip/spi.h @@ -12,6 +12,7 @@ struct rockchip_spi_priv { struct rockchip_spi *regs; struct clk clk; + struct tiny_clk tiny_clk; unsigned int max_freq; unsigned int mode; ulong last_transaction_us; /* Time of last transaction end */ diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 8959749ad6..d26aedd812 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -470,6 +470,24 @@ out: #define isa_check_signature(io,sig,len) (0) #endif /* __mem_isa */ + +/* + * Provide dummy I/O access on ARM, to allow portable code to compile (e.g. the + * ns16550 driver). These functions do nothing and should be guarded by a CONFIG + * on ARM so that they are never executed. + */ +#define outb(val, port) ({ \ + __maybe_unused ulong _val = (val); \ + __maybe_unused ulong _port = (uintptr_t)(port); \ +}) + +#define outw(val, port) outb(val, port) +#define outl(val, port) outb(val, port) + +#define inb(port) ({ __maybe_unused ulong _port = (uintptr_t)(port); 0; }) +#define inw(port) inb(port) +#define inl(port) inb(port) + #endif /* __KERNEL__ */ #include diff --git a/arch/arm/mach-rockchip/rk3288/clk_rk3288.c b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c index e05bd06a8d..144dfe7256 100644 --- a/arch/arm/mach-rockchip/rk3288/clk_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c @@ -11,6 +11,17 @@ #include #include +static int rockchip_sysreset_probe_(struct sysreset_reg *priv) +{ + priv->glb_srst_fst_value = offsetof(struct rockchip_cru, + cru_glb_srst_fst_value); + priv->glb_srst_snd_value = offsetof(struct rockchip_cru, + cru_glb_srst_snd_value); + + return 0; +} + +#if !CONFIG_IS_ENABLED(TINY_CLK) int rockchip_get_clk(struct udevice **devp) { return uclass_get_device_by_driver(UCLASS_CLK, @@ -31,3 +42,38 @@ void *rockchip_get_cru(void) return priv->cru; } + +int rockchip_cru_setup_sysreset(struct udevice *dev) +{ + struct sysreset_reg *priv = dev_get_priv(dev); + + return rockchip_sysreset_probe_(priv); +} + +#else /* TINY_CLK */ + +struct tinydev *tiny_rockchip_get_clk(void) +{ + return tiny_dev_get(UCLASS_CLK, 0); +} + +void *rockchip_get_cru(void) +{ + struct rk3288_clk_priv *priv; + struct tinydev *tdev; + + tdev = tiny_rockchip_get_clk(); + if (!tdev) + return NULL; + priv = tdev->priv; + + return priv->cru; +} + +int rockchip_cru_setup_tiny_sysreset(struct tinydev *tdev) +{ + struct sysreset_reg *priv = tinydev_get_priv(tdev); + + return rockchip_sysreset_probe_(priv); +} +#endif diff --git a/arch/arm/mach-rockchip/rk3288/rk3288.c b/arch/arm/mach-rockchip/rk3288/rk3288.c index 804abe8a1b..1e7baeea45 100644 --- a/arch/arm/mach-rockchip/rk3288/rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/rk3288.c @@ -115,6 +115,7 @@ int rk_board_late_init(void) return rk3288_board_late_init(); } +#if !CONFIG_IS_ENABLED(TINY_CLK) static int do_clock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -165,3 +166,4 @@ U_BOOT_CMD( "display information about clocks", "" ); +#endif diff --git a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c index e3da0a0194..52697e9cac 100644 --- a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c @@ -10,6 +10,7 @@ #include #include +#if !CONFIG_IS_ENABLED(TINY_SYSCON) static const struct udevice_id rk3288_syscon_ids[] = { { .compatible = "rockchip,rk3288-noc", .data = ROCKCHIP_SYSCON_NOC }, { .compatible = "rockchip,rk3288-grf", .data = ROCKCHIP_SYSCON_GRF }, @@ -24,40 +25,18 @@ U_BOOT_DRIVER(syscon_rk3288) = { .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); +U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_noc) +U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_pmu) +U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_grf) +U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_sgrf) - return 0; -} +#else -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, +U_BOOT_TINY_DRIVER(syscon_rk3288) = { + .uclass_id = UCLASS_SYSCON, + .probe = tiny_syscon_setup, +// .ops = &rockchip_clk_tiny_ops, + DM_TINY_PRIV(, \ + sizeof(struct syscon_uc_info)) }; #endif diff --git a/arch/arm/mach-rockchip/sdram.c b/arch/arm/mach-rockchip/sdram.c index 24fe6cc8f0..e4c6644a0f 100644 --- a/arch/arm/mach-rockchip/sdram.c +++ b/arch/arm/mach-rockchip/sdram.c @@ -189,15 +189,30 @@ int dram_init(void) struct udevice *dev; int ret; - ret = uclass_get_device(UCLASS_RAM, 0, &dev); - if (ret) { - debug("DRAM init failed: %d\n", ret); - return ret; - } - ret = ram_get_info(dev, &ram); - if (ret) { - debug("Cannot get DRAM size: %d\n", ret); - return ret; + if (!CONFIG_IS_ENABLED(TINY_RAM)) { + ret = uclass_get_device(UCLASS_RAM, 0, &dev); + if (ret) { + debug("DRAM init failed: %d\n", ret); + return log_msg_ret("get", ret); + } + ret = ram_get_info(dev, &ram); + if (ret) { + debug("Cannot get DRAM size: %d\n", ret); + return log_msg_ret("info", ret); + } + } else { + struct tinydev *dev; + + dev = tiny_dev_get(UCLASS_RAM, 0); + if (!dev) { + debug("DRAM init failed\n"); + return log_msg_ret("get", -ENOKEY); + } + ret = tiny_ram_get_info(dev, &ram); + if (ret) { + debug("Cannot get DRAM size: %d\n", ret); + return log_msg_ret("info", ret); + } } gd->ram_size = ram.size; debug("SDRAM base=%lx, size=%lx\n", diff --git a/arch/sandbox/cpu/u-boot-spl.lds b/arch/sandbox/cpu/u-boot-spl.lds index c60eb109b1..c2e3e65721 100644 --- a/arch/sandbox/cpu/u-boot-spl.lds +++ b/arch/sandbox/cpu/u-boot-spl.lds @@ -5,9 +5,21 @@ * found in the LICENSE file. */ +/* Put the tiny devices in the data section since they are changed at runtime */ SECTIONS { + . = ALIGN(4); + .tiny_dev : { + __tiny_dev_start = .; + KEEP(*(SORT(.u_boot_list*tiny_dev*))); + __tiny_dev_end = .; + } +} +INSERT AFTER .data; + +SECTIONS +{ . = ALIGN(4); .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 20f6893829..5480c69ebe 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -9,10 +9,11 @@ compatible = "sandbox"; aliases { + axi0 = &axi; i2c0 = &i2c_0; pci0 = &pcic; rtc0 = &rtc_0; - axi0 = &axi; + serial0 = &serial0; spi0 = &spi; }; diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index e1f68cd552..99a6b6328c 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -259,7 +259,7 @@ }; /* Needs to be available prior to relocation */ - uart0: serial { + serial0: serial { u-boot,dm-spl; compatible = "sandbox,serial"; sandbox,text-colour = "cyan"; diff --git a/arch/x86/cpu/apollolake/cpu_spl.c b/arch/x86/cpu/apollolake/cpu_spl.c index 707ceb3e64..0f32ca7dc5 100644 --- a/arch/x86/cpu/apollolake/cpu_spl.c +++ b/arch/x86/cpu/apollolake/cpu_spl.c @@ -171,7 +171,7 @@ static void early_ec_init(void) static int arch_cpu_init_tpl(void) { - struct udevice *pmc, *sa, *p2sb, *serial, *spi, *lpc; + struct udevice *pmc, *sa, *p2sb, *spi, *lpc; int ret; ret = uclass_first_device_err(UCLASS_ACPI_PMC, &pmc); @@ -192,9 +192,6 @@ static int arch_cpu_init_tpl(void) if (ret) return log_msg_ret("northbridge", ret); gd->baudrate = CONFIG_BAUDRATE; - ret = uclass_first_device_err(UCLASS_SERIAL, &serial); - if (ret) - return log_msg_ret("serial", ret); if (CONFIG_IS_ENABLED(SPI_FLASH_SUPPORT)) { ret = uclass_first_device_err(UCLASS_SPI, &spi); if (ret) diff --git a/arch/x86/cpu/apollolake/uart.c b/arch/x86/cpu/apollolake/uart.c index f368f7d2db..3ab3fc9d4d 100644 --- a/arch/x86/cpu/apollolake/uart.c +++ b/arch/x86/cpu/apollolake/uart.c @@ -8,10 +8,12 @@ */ #include +#include #include #include #include #include +#include #include #include #include @@ -64,6 +66,7 @@ void apl_uart_init(pci_dev_t bdf, ulong base) uart_lpss_init((void *)base); } +#if !CONFIG_IS_ENABLED(TINY_SERIAL) /* * This driver uses its own compatible string but almost everything else from * the standard ns16550 driver. This allows us to provide an of-platdata @@ -132,3 +135,56 @@ U_BOOT_DRIVER(apl_ns16550) = { .ofdata_to_platdata = apl_ns16550_ofdata_to_platdata, .probe = apl_ns16550_probe, }; + +#else /* TINY_SERIAL */ + +static int apl_ns16550_tiny_probe(struct tinydev *tdev) +{ + struct dtd_intel_apl_ns16550 *dtplat = tdev->dtplat; + struct ns16550_platdata *plat = tdev->priv; + ulong base; + pci_dev_t bdf; + + base = dtplat->early_regs[0]; + bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + + if (!CONFIG_IS_ENABLED(PCI)) + apl_uart_init(bdf, base); + + plat->base = base; + plat->reg_shift = dtplat->reg_shift; + plat->reg_width = 1; + plat->clock = dtplat->clock_frequency; + plat->fcr = UART_FCR_DEFVAL; + + return ns16550_tiny_probe_plat(plat); +} + +static int apl_ns16550_tiny_setbrg(struct tinydev *tdev, int baudrate) +{ + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_setbrg(plat, baudrate); +} + +static int apl_ns16550_tiny_putc(struct tinydev *tdev, const char ch) +{ + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_putc(plat, ch); +} + +struct tiny_serial_ops apl_ns16550_tiny_ops = { + .probe = apl_ns16550_tiny_probe, + .setbrg = apl_ns16550_tiny_setbrg, + .putc = apl_ns16550_tiny_putc, +}; + +U_BOOT_TINY_DRIVER(apl_ns16550) = { + .uclass_id = UCLASS_SERIAL, + .probe = apl_ns16550_tiny_probe, + .ops = &apl_ns16550_tiny_ops, + DM_TINY_PRIV(, sizeof(struct ns16550_platdata)) +}; + +#endif diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts index 965d9f387d..f1a1f98c08 100644 --- a/arch/x86/dts/chromebook_coral.dts +++ b/arch/x86/dts/chromebook_coral.dts @@ -40,6 +40,7 @@ i2c5 = &i2c_5; i2c6 = &i2c_6; i2c7 = &i2c_7; + serial0 = &serial; }; config { diff --git a/arch/x86/lib/tpl.c b/arch/x86/lib/tpl.c index 6f7eb43a17..139de881e8 100644 --- a/arch/x86/lib/tpl.c +++ b/arch/x86/lib/tpl.c @@ -41,7 +41,7 @@ static int x86_tpl_init(void) ret = arch_cpu_init(); if (ret) { debug("%s: arch_cpu_init() failed\n", __func__); - return ret; + return log_msg_ret("arch", ret); } ret = arch_cpu_init_dm(); if (ret) { @@ -59,7 +59,7 @@ void board_init_f(ulong flags) ret = x86_tpl_init(); if (ret) { - debug("Error %d\n", ret); + printf("Error %d\n", ret); panic("x86_tpl_init fail"); } diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig index 79f9b5a232..af8b7a50e8 100644 --- a/configs/chromebook_coral_defconfig +++ b/configs/chromebook_coral_defconfig @@ -102,3 +102,4 @@ CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y # CONFIG_GZIP is not set # CONFIG_EFI_LOADER is not set +CONFIG_NS16550_DYNAMIC=y diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig index 96246b7512..3a44543bc5 100644 --- a/configs/chromebook_jerry_defconfig +++ b/configs/chromebook_jerry_defconfig @@ -107,3 +107,14 @@ CONFIG_DISPLAY_ROCKCHIP_HDMI=y CONFIG_SPL_TINY_MEMSET=y CONFIG_CMD_DHRYSTONE=y CONFIG_ERRNO_STR=y +CONFIG_NS16550_DYNAMIC=y +CONFIG_SPL_TINY_ONLY=y +# CONFIG_TINY_CHECK is not set +CONFIG_SPL_TINY_RAM=y +CONFIG_SPL_TINY_SERIAL=y +CONFIG_SPL_TINY_CLK=y +# CONFIG_SPL_CLK_FIXED_RATE is not set +CONFIG_SPL_TINY_SPI=y +CONFIG_SPL_TINY_SPI_FLASH=y +CONFIG_SPL_TINY_SYSRESET=y +CONFIG_SPL_TINY_SYSCON=y diff --git a/configs/rock2_defconfig b/configs/rock2_defconfig index 2118402bbe..a458935422 100644 --- a/configs/rock2_defconfig +++ b/configs/rock2_defconfig @@ -83,3 +83,6 @@ CONFIG_DISPLAY_ROCKCHIP_HDMI=y CONFIG_CONSOLE_SCROLL_LINES=10 CONFIG_CMD_DHRYSTONE=y CONFIG_ERRNO_STR=y +#CONFIG_SPL_TINY_SERIAL=y +#CONFIG_SPL_TINY=y +CONFIG_SPL_OF_PLATDATA=y From patchwork Thu Jul 2 21:10:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 240665 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Thu, 2 Jul 2020 15:10:04 -0600 Subject: [RFC PATCH v2 3/3] dm: Core changes for tiny-dm In-Reply-To: <20200702211004.1491489-1-sjg@chromium.org> References: <20200702211004.1491489-1-sjg@chromium.org> Message-ID: <20200702211004.1491489-4-sjg@chromium.org> This patch includes changes to support tiny-dm in driver model and dtoc. Signed-off-by: Simon Glass --- Changes in v2: - Various updates, and ported to chromebook_jerry (rockchip) board/Synology/ds109/ds109.c | 3 +- common/console.c | 2 +- common/log.c | 36 +++- common/malloc_simple.c | 31 ++++ common/spl/spl.c | 17 +- common/spl/spl_spi.c | 91 +++++---- doc/develop/debugging.rst | 35 ++++ doc/driver-model/tiny-dm.rst | 315 +++++++++++++++++++++++++++++++ drivers/core/Kconfig | 106 +++++++++++ drivers/core/Makefile | 3 + drivers/core/of_extra.c | 49 ++++- drivers/core/regmap.c | 3 +- drivers/core/syscon-uclass.c | 68 +++++-- drivers/core/tiny.c | 249 +++++++++++++++++++++++++ include/dm/device.h | 121 ++++++++++++ include/dm/of_extra.h | 6 + include/dm/platdata.h | 20 +- include/dm/tiny_struct.h | 42 +++++ include/linker_lists.h | 6 + scripts/Makefile.spl | 6 +- tools/dtoc/dtb_platdata.py | 316 +++++++++++++++++++++++++++----- tools/dtoc/dtoc_test_simple.dts | 12 +- tools/dtoc/fdt.py | 7 +- tools/dtoc/main.py | 9 +- tools/dtoc/test_dtoc.py | 91 ++++++++- tools/patman/tools.py | 4 +- 26 files changed, 1525 insertions(+), 123 deletions(-) create mode 100644 doc/develop/debugging.rst create mode 100644 doc/driver-model/tiny-dm.rst create mode 100644 drivers/core/tiny.c create mode 100644 include/dm/tiny_struct.h diff --git a/board/Synology/ds109/ds109.c b/board/Synology/ds109/ds109.c index aa2987d924..d5d7396ad3 100644 --- a/board/Synology/ds109/ds109.c +++ b/board/Synology/ds109/ds109.c @@ -106,8 +106,7 @@ void reset_misc(void) printf("Synology reset..."); udelay(50000); - b_d = ns16550_calc_divisor((NS16550_t)CONFIG_SYS_NS16550_COM2, - CONFIG_SYS_NS16550_CLK, 9600); + b_d = ns16550_calc_divisor(CONFIG_SYS_NS16550_CLK, 9600); NS16550_init((NS16550_t)CONFIG_SYS_NS16550_COM2, b_d); NS16550_putc((NS16550_t)CONFIG_SYS_NS16550_COM2, SOFTWARE_REBOOT); } diff --git a/common/console.c b/common/console.c index f149624954..466e45ae1b 100644 --- a/common/console.c +++ b/common/console.c @@ -259,7 +259,7 @@ static inline void console_doenv(int file, struct stdio_dev *dev) iomux_doenv(file, dev->name); } #endif -#else +#else /* !CONSOLE_MUX */ static inline int console_getc(int file) { return stdio_devices[file]->getc(stdio_devices[file]); diff --git a/common/log.c b/common/log.c index f44f15743f..abe6fe7096 100644 --- a/common/log.c +++ b/common/log.c @@ -45,11 +45,13 @@ const char *log_get_cat_name(enum log_category_t cat) if (cat >= LOGC_NONE) return log_cat_name[cat - LOGC_NONE]; -#if CONFIG_IS_ENABLED(DM) - name = uclass_get_name((enum uclass_id)cat); -#else - name = NULL; -#endif + /* With tiny-dm we don't have uclasses, so cannot get the name */ + if (CONFIG_IS_ENABLED(TINY_ONLY)) + return "tiny"; + if (CONFIG_IS_ENABLED(DM)) + name = uclass_get_name((enum uclass_id)cat); + else + name = NULL; return name ? name : ""; } @@ -182,6 +184,21 @@ static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) return false; } +void log_check(const char *msg) +{ + struct log_device *ldev; + int count = 0; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + count++; + if (count > 1) { + printf("%s: %s error\n", msg, __func__); + panic("log"); + } + } + printf("%s: log OK\n", msg); +} + /** * log_dispatch() - Send a log record to all log devices for processing * @@ -296,6 +313,15 @@ int log_remove_filter(const char *drv_name, int filter_num) return -ENOENT; } +void log_fixup_for_gd_move(struct global_data *new_gd) +{ + /* The sentinel node has moved, so update things that point to it */ + if (gd->log_head.next) { + new_gd->log_head.next->prev = &new_gd->log_head; + new_gd->log_head.prev->next = &new_gd->log_head; + } +} + int log_init(void) { struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); diff --git a/common/malloc_simple.c b/common/malloc_simple.c index 34f0b49093..7e73fd7231 100644 --- a/common/malloc_simple.c +++ b/common/malloc_simple.c @@ -80,3 +80,34 @@ void malloc_simple_info(void) log_info("malloc_simple: %lx bytes used, %lx remain\n", gd->malloc_ptr, CONFIG_VAL(SYS_MALLOC_F_LEN) - gd->malloc_ptr); } + +uint malloc_ptr_to_ofs(void *ptr) +{ + ulong addr = map_to_sysmem(ptr); + ulong offset; + + offset = addr - gd->malloc_base; + if (offset >= gd->malloc_limit) { + log_debug("Invalid malloc ptr %p (base=%lx, size=%lx)\n", ptr, + gd->malloc_base, gd->malloc_limit); + panic("malloc ptr invalid"); + } + + return offset; +} + +void *malloc_ofs_to_ptr(uint offset) +{ + void *base = map_sysmem(gd->malloc_base, gd->malloc_limit); + void *ptr; + + if (offset >= gd->malloc_limit) { + log_debug("Invalid malloc offset %lx (size=%lx)\n", + gd->malloc_base, gd->malloc_limit); + panic("malloc offset invalid"); + } + ptr = base + offset; + unmap_sysmem(base); + + return ptr; +} diff --git a/common/spl/spl.c b/common/spl/spl.c index 0e96a8cd10..61867c81a3 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -399,7 +399,7 @@ static int spl_common_init(bool setup_malloc) if (ret) { debug("%s: Failed to set up bootstage: ret=%d\n", __func__, ret); - return ret; + return log_msg_ret("bootstage", ret); } #ifdef CONFIG_BOOTSTAGE_STASH if (!u_boot_first_phase()) { @@ -418,17 +418,17 @@ static int spl_common_init(bool setup_malloc) ret = log_init(); if (ret) { debug("%s: Failed to set up logging\n", __func__); - return ret; + return log_msg_ret("log", ret); } #endif if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { ret = fdtdec_setup(); if (ret) { debug("fdtdec_setup() returned error %d\n", ret); - return ret; + return log_msg_ret("fdtdec", ret); } } - if (CONFIG_IS_ENABLED(DM)) { + if (CONFIG_IS_ENABLED(DM) && !CONFIG_IS_ENABLED(TINY_ONLY)) { bootstage_start(BOOTSTAGE_ID_ACCUM_DM_SPL, spl_phase() == PHASE_TPL ? "dm tpl" : "dm_spl"); /* With CONFIG_SPL_OF_PLATDATA, bring in all devices */ @@ -436,7 +436,7 @@ static int spl_common_init(bool setup_malloc) bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_SPL); if (ret) { debug("dm_init_and_scan() returned error %d\n", ret); - return ret; + return log_msg_ret("init", ret); } } @@ -819,9 +819,10 @@ ulong spl_relocate_stack_gd(void) ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16); new_gd = (gd_t *)ptr; memcpy(new_gd, (void *)gd, sizeof(gd_t)); -#if CONFIG_IS_ENABLED(DM) - dm_fixup_for_gd_move(new_gd); -#endif + if (CONFIG_IS_ENABLED(DM) && !CONFIG_IS_ENABLED(TINY_ONLY)) + dm_fixup_for_gd_move(new_gd); + if (CONFIG_IS_ENABLED(LOG)) + log_fixup_for_gd_move(new_gd); #if !defined(CONFIG_ARM) && !defined(CONFIG_RISCV) gd = new_gd; #endif diff --git a/common/spl/spl_spi.c b/common/spl/spl_spi.c index 2744fb5d52..fe198354f3 100644 --- a/common/spl/spl_spi.c +++ b/common/spl/spl_spi.c @@ -9,12 +9,14 @@ */ #include +#include #include #include #include #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -55,7 +57,7 @@ static int spi_load_image_os(struct spl_image_info *spl_image, static ulong spl_spi_fit_read(struct spl_load_info *load, ulong sector, ulong count, void *buf) { - struct spi_flash *flash = load->dev; + struct spi_flash *flash = load->legacy_dev; ulong ret; ret = spi_flash_read(flash, sector, count, buf); @@ -70,6 +72,21 @@ unsigned int __weak spl_spi_get_uboot_offs(struct spi_flash *flash) return CONFIG_SYS_SPI_U_BOOT_OFFS; } +static int spl_read(struct spl_load_info *load, u32 offset, size_t len, + void *buf) +{ + int ret; + + if (!CONFIG_IS_ENABLED(TINY_SPI_FLASH)) + ret = spi_flash_read(load->legacy_dev, offset, len, buf); + else + ret = tiny_spi_flash_read(load->tdev, offset, len, buf); + + if (ret) + return log_ret(ret); + + return 0; +} /* * The main entry for SPI booting. It's necessary that SDRAM is already * configured and available since this code loads the main U-Boot image @@ -80,41 +97,53 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, { int err = 0; unsigned int payload_offs; + struct spl_load_info load; struct spi_flash *flash; struct image_header *header; + struct tinydev *tdev; /* * Load U-Boot image from SPI flash into RAM * In DM mode: defaults speed and mode will be * taken from DT when available */ - - flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, - CONFIG_SF_DEFAULT_CS, - CONFIG_SF_DEFAULT_SPEED, - CONFIG_SF_DEFAULT_MODE); - if (!flash) { - puts("SPI probe failed.\n"); - return -ENODEV; + memset(&load, '\0', sizeof(load)); + if (!CONFIG_IS_ENABLED(TINY_SPI_FLASH)) { + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!flash) { + puts("SPI probe failed\n"); + return -ENODEV; + } + payload_offs = spl_spi_get_uboot_offs(flash); + if (CONFIG_IS_ENABLED(OF_CONTROL) && + !CONFIG_IS_ENABLED(OF_PLATDATA)) { + payload_offs = ofnode_read_config_int( + "u-boot,spl-payload-offset", + payload_offs); + } + load.legacy_dev = flash; + } else { + tdev = tiny_dev_get(UCLASS_SPI_FLASH, 0); + if (!tdev) { + puts("SPI probe failed\n"); + return -ENODEV; + } + load.tdev = tdev; + payload_offs = CONFIG_SYS_SPI_U_BOOT_OFFS; } - payload_offs = spl_spi_get_uboot_offs(flash); - header = spl_get_load_buffer(-sizeof(*header), sizeof(*header)); -#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) - payload_offs = fdtdec_get_config_int(gd->fdt_blob, - "u-boot,spl-payload-offset", - payload_offs); -#endif - #ifdef CONFIG_SPL_OS_BOOT if (spl_start_uboot() || spi_load_image_os(spl_image, flash, header)) #endif { - /* Load u-boot, mkimage header is 64 bytes. */ - err = spi_flash_read(flash, payload_offs, sizeof(*header), - (void *)header); + /* Load U-Boot, mkimage header is 64 bytes. */ + err = spl_read(&load, payload_offs, sizeof(*header), + (void *)header); if (err) { debug("%s: Failed to read from SPI flash (err=%d)\n", __func__, err); @@ -123,30 +152,23 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) && image_get_magic(header) == FDT_MAGIC) { - err = spi_flash_read(flash, payload_offs, - roundup(fdt_totalsize(header), 4), - (void *)CONFIG_SYS_LOAD_ADDR); + err = spl_read(&load, payload_offs, + roundup(fdt_totalsize(header), 4), + (void *)CONFIG_SYS_LOAD_ADDR); if (err) return err; err = spl_parse_image_header(spl_image, - (struct image_header *)CONFIG_SYS_LOAD_ADDR); + (struct image_header *)CONFIG_SYS_LOAD_ADDR); } else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) { - struct spl_load_info load; - debug("Found FIT\n"); - load.dev = flash; load.priv = NULL; load.filename = NULL; load.bl_len = 1; load.read = spl_spi_fit_read; err = spl_load_simple_fit(spl_image, &load, - payload_offs, - header); + payload_offs, header); } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { - struct spl_load_info load; - - load.dev = flash; load.priv = NULL; load.filename = NULL; load.bl_len = 1; @@ -158,9 +180,8 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, err = spl_parse_image_header(spl_image, header); if (err) return err; - err = spi_flash_read(flash, payload_offs, - spl_image->size, - (void *)spl_image->load_addr); + err = spl_read(&load, payload_offs, spl_image->size, + (void *)spl_image->load_addr); } } diff --git a/doc/develop/debugging.rst b/doc/develop/debugging.rst new file mode 100644 index 0000000000..2b06a4a38f --- /dev/null +++ b/doc/develop/debugging.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2020 Heinrich Schuchardt + +Debugging +========= + +This describes a few debugging techniques for different parts of U-Boot. + +Makefiles +--------- + +You can use $(warning) to show debugging information in a makefile:: + + $(warning SPL: $(CONFIG_SPL_BUILD) . $(SPL_TPL_)) + +When make executes these they produce a message. If you put them in a rule, they +are executed when the rule is executed. For example, to show the value of a +variable at the point where it is used:: + + tools-only: scripts_basic $(version_h) $(timestamp_h) tools/version.h + $(warning version_h: $(version_h)) + $(Q)$(MAKE) $(build)=tools + +You can use ifndef in makefiles for simple CONFIG checks:: + + ifndef CONFIG_DM_DEV_READ_INLINE + obj-$(CONFIG_OF_CONTROL) += read.o + endif + +but for those which require variable expansion you should use ifeq or ifneq:: + + ifeq ($(CONFIG_$(SPL_TPL_)TINY_ONLY),) + obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o + endif + diff --git a/doc/driver-model/tiny-dm.rst b/doc/driver-model/tiny-dm.rst new file mode 100644 index 0000000000..cc5c3e00b1 --- /dev/null +++ b/doc/driver-model/tiny-dm.rst @@ -0,0 +1,315 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Tiny driver model (tiny-dm) +=========================== + +Purpose +------- + +Reduce the overhead of using driver model in SPL and TPL. + + +Introduction +------------ + +On some platforms that use SPL [1]_, SRAM is extremely limited. There is a +need to use as little space as possible for U-Boot SPL. + +With the migration to driver model and devicetree, the extra software complexity +has created more pressure on U-Boot's code and data size. + +A few features have been introduced to help with this problem: + + - fdtgrep, introduced in 2015, automatically removes unnecessary parts of the + device tree, e.g. those used by drivers not present in SPL. At the time, + this typically reduced SPL size from about 40KB to perhaps 3KB and made + it feasible to look at using driver model with SPL. The minimum overhead + was reduced to approximately 7KB on Thumb systems, for example [2]_ + - of-platdata, introduced in 2016 [3]_, converts the device tree into C data + structures which are placed in the SPL image. This saves approximately + 3KB of code and replaces the devicetree with something typically 30% + smaller. + +However the problem still exists. Even with of-platdata, the driver-model +overhead is typically 3KB at the minimum. This excludes the size of allocated +data structures, which is 84 bytes per device and 76 bytes per uclass on +32-bit machines. On 64-bit machines these sizes approximately double. + +With the driver-model migration deadlines passing, a solution is needed to +allow boards to complete migration to driver model in SPL, without taking on +the full ~5KB overhead that this entails. + + +Concept +------- + +The idea of tiny devices ('tiny-dm') builds on of-platdata, but additionally +removes most of the rich feature-set of driver model. + +In particular tiny-dm takes away the concept of a uclass (except that it stil +uses uclass IDs), drastically reduces the size of a device (to 16 bytes on +32-bit) and removes the need for a driver_info structure. + +With tiny-dm, dtoc outputs U_BOOT_TINY_DEVICE() instead of U_BOOT_DEVICE(). +A new 'struct tiny_dev' is used instead of 'struct udevice'. Devices can be +located based on uclass ID and sequence number with tiny_dev_find(). Devices can +be probed with tiny_dev_probe(). + +In fact, tiny-dm is effectively a bypass for most of driver model. It retains +some capability with in (chiefly by using the same device tree), but new code +is added to implement simple features in a simple way. + +Tiny-dm is not suitable for complex device and interactions, but it can +support a serial port (output only), I2C buses and other features needed to +set up the machine just enough to load U-Boot proper. + +It is possible to enable Tiny-dm on a subsystem-by-subsystem basis. For example, +enabling CONFIG_TPL_TINY_SERIAL on chromebook_coral saves about 900 bytes of +code and data, with no perceptable difference in operation. + + +Tiny devices +------------ + +Below is an example of a tiny device, a UART that uses NS16550. It works by +setting up a platform structure to pass to the ns16550 driver, perhaps the +worst driver in U-Boot. + +.. code-block:: c + + static int apl_ns16550_tiny_probe(struct tiny_dev *tdev) + { + struct dtd_intel_apl_ns16550 *dtplat = tdev->dtplat; + struct ns16550_platdata *plat = tdev->priv; + ulong base; + pci_dev_t bdf; + + base = dtplat->early_regs[0]; + bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + + if (!CONFIG_IS_ENABLED(PCI)) + apl_uart_init(bdf, base); + + plat->base = base; + plat->reg_shift = dtplat->reg_shift; + plat->reg_width = 1; + plat->clock = dtplat->clock_frequency; + plat->fcr = UART_FCR_DEFVAL; + + return ns16550_tiny_probe_plat(plat); + } + + static int apl_ns16550_tiny_setbrg(struct tiny_dev *tdev, int baudrate) + { + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_setbrg(plat, baudrate); + } + + static int apl_ns16550_tiny_putc(struct tiny_dev *tdev, const char ch) + { + struct ns16550_platdata *plat = tdev->priv; + + return ns16550_tiny_putc(plat, ch); + } + + struct tiny_serial_ops apl_ns16550_tiny_ops = { + .probe = apl_ns16550_tiny_probe, + .setbrg = apl_ns16550_tiny_setbrg, + .putc = apl_ns16550_tiny_putc, + }; + + U_BOOT_TINY_DRIVER(apl_ns16550) = { + .uclass_id = UCLASS_SERIAL, + .probe = apl_ns16550_tiny_probe, + .ops = &apl_ns16550_tiny_ops, + DM_TINY_PRIV(, sizeof(struct ns16550_platdata)) + }; + +The probe function is responsible for setting up the hardware so that the UART +can output characters. This driver enables the device on PCI and assigns an +address to its BAR (Base-Address Register). That code is in apl_uart_init() and +is not show here. Then it sets up a platdata data structure for use by the +ns16550 driver and calls its probe function. + +The 'tdev' device is declared like this in the device tree: + +.. code-block:: c + + serial: serial at 18,2 { + reg = <0x0200c210 0 0 0 0>; + u-boot,dm-pre-reloc; + compatible = "intel,apl-ns16550"; + early-regs = <0xde000000 0x20>; + reg-shift = <2>; + clock-frequency = <1843200>; + current-speed = <115200>; + }; + +When dtoc runs it outputs the following code for this, into dt-platdata.c: + +.. code-block:: c + + static struct dtd_intel_apl_ns16550 dtv_serial_at_18_2 = { + .clock_frequency = 0x1c2000, + .current_speed = 0x1c200, + .early_regs = {0xde000000, 0x20}, + .reg = {0x200c210, 0x0}, + .reg_shift = 0x2, + }; + + DM_DECL_TINY_DRIVER(apl_ns16550); + #include + u8 _serial_at_18_2_priv[sizeof(struct ns16550_platdata)] __attribute__ ((section (".data"))); + U_BOOT_TINY_DEVICE(serial_at_18_2) = { + .dtplat = &dtv_serial_at_18_2, + .drv = DM_REF_TINY_DRIVER(apl_ns16550), + .priv = _serial_at_18_2_priv, + }; + +This basically creates a device, with a pointer to the dtplat data (a C +structure similar to the devicetree node) and a pointer to the driver, the +U_BOOT_TINY_DRIVER() thing shown above. + +So far, tiny-dm might look pretty similar to the full driver model, but there +are quite a few differences that may not be immediately apparent: + + - Whereas U_BOOT_DEVICE() emits a driver_info structure and then allocates + the udevice structure at runtime, U_BOOT_TINY_DEVICE() emits an actual + tiny_dev device structure into the image. On platforms where SPL runs in + read-only memory, U-Boot automatically copies this into RAM as needed. + - The DM_TINY_PRIV() macro tells U-Boot about the private data needed by + the device. But this is not allocated at runtime. Instead it is declared + in the C structure above. However on platforms where SPL runs in read-only + memory, allocation is left until runtime. + - There is a corresponding 'full' driver in the same file with the same name. + Like of-platdata, it is not possible to use tiny-dm without 'full' support + added as well. This makes sense because the device needs to be supported + in U-Boot proper as well. + - While this driver is in the UCLASS_SERIAL uclass, there is in fact no + uclass available. The serial-uclass.c implementation has an entirely + separate (small) piece of code to support tiny-dm: + +.. code-block:: c + + int serial_init(void) + { + struct tiny_dev *tdev; + int ret; + + tdev = tiny_dev_find(UCLASS_SERIAL, 0); + if (!tdev) { + if (IS_ENABLED(CONFIG_REQUIRE_SERIAL_CONSOLE)) + panic_str("No serial"); + return -ENODEV; + } + ret = tiny_dev_probe(tdev); + if (ret) + return log_msg_ret("probe", ret); + gd->tiny_serial = tdev; + gd->flags |= GD_FLG_SERIAL_READY; + serial_setbrg(); + + return 0; + } + + void serial_putc(const char ch) + { + struct tiny_dev *tdev = gd->tiny_serial; + struct tiny_serial_ops *ops; + + if (!tdev) + goto err; + + ops = tdev->drv->ops; + if (!ops->putc) + goto err; + if (ch == '\n') + ops->putc(tdev, '\r'); + ops->putc(tdev, ch); + + return; + err: + if (IS_ENABLED(DEBUG_UART)) + printch(ch); + } + + void serial_puts(const char *str) + { + for (const char *s = str; *s; s++) + serial_putc(*s); + } + + +When serial_putc() is called from within U-Boot, this code looks up the tiny-dm +device and sends it the character. + + +Potential costs and benefits +---------------------------- + +It is hard to estimate the savings to be had by switching a subsystem over to +tiny-dm. Further work will illuminate this. In the example above (on x86), +about 1KB bytes is saved (code and data), but this may or may not be +representative of other subsystems. + +If all devices in an image use tiny-dm then it is possible to remove all the +core driver-model support. This is the 3KB mentioned earlier. Of course, tiny-dm +has its own overhead, although it is substantialy less than the full driver +model. + +These benefits come with some drawbacks: + + - Drivers that want to use it must implement tiny-dm in addition to their + normal support. + - of-platdata must be used. This cannot be made to work with device tree. + - Tiny-dm drivers have none of the rich support provided by driver model. + There is no pre-probe support, no concept of buses holding information + about child devices, no automatic pin control or power control when a + device is probed. Tiny-dm is designed to save memory, not to make it easy + to write complex device drivers. + - Subsystems must be fully migrated to driver model with the old code + removed. This is partly a technical limitation (see ns16550.c for how ugly + it is to support both, let alone three) and partly a quid-pro-quo for + this feature, since it should remove existing concerns about migrating to + driver model. + + +Next steps +---------- + +This is currently an RFC so the final result may change somewhat from what is +presented here. Some features are missing, in particular the concept of sequence +numbers is designed but not implemented. The code is extremely rough. + +To judge the impact of tiny-dm a suitable board needs to be fully converted to +it. At present I am leaning towards rock2, since it already supports +of-platdata. + +The goal is to sent initial patches in June 2020 with the first version in +mainline in July 2020 ready for the October release. Refinements based on +feedback and patches received can come after that. It isn't clear yet when this +could become a 'stable' feature, but likely after a release or two, perhaps with +5-10 boards converted. + + +Trying it out +------------- + +The source tree is available at https://github.com/sjg20/u-boot/tree/dtoc-working + +Only two boards are supported at present: + + - sandbox_spl - run spl/u-boot-spl to try the SPL with tiny-dm + - chromebook_coral - TPL uses tiny-dm + + +.. [1] This discussion refers to SPL but for devices that use TPL, the same + features are available there. +.. [2] https://www.elinux.org/images/c/c4/\Order_at_last_-_U-Boot_driver_model_slides_%282%29.pdf +.. [3] https://elinux.org/images/8/82/What%27s_New_with_U-Boot_%281%29.pdf + + +.. Simon Glass +.. Google LLC +.. Memorial Day 2020 diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 3942d11f2b..c4ea6e5a1a 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -36,6 +36,94 @@ config TPL_DM CONFIG_SPL_SYS_MALLOC_F_LEN for more details on how to enable it. Disable this for very small implementations. +config SPL_TINY + bool "Support tiny drivers in TPL without full driver-model support" + depends on SPL_OF_PLATDATA + default y + help + Enable support for reducing driver-model overhead with 'tiny' + devices. These drivers have very basic support and do not support + the full driver-model infrastructure. This can be useful for saving + memory in SPL. + +config TIMYDEV_SHRINK_DATA + bool "Use smaller data structures" + help + Tinydev supports storing some data structures in a smaller form to + save memory. However this does increase code sixe so only enable + this option if you have quite a lot of devices used by your board + in SPL/TPL. + +config TINYDEV_DATA_MAX_COUNT + int "Number of tinydev data records to allow" + default 10 + help + With tinydev each device has a single priv pointer but it is possible + to attach other kinds of pointers to tiny devices using a separate + mechanism. This sets the maximum number that can be attached. See + struct tinydev_info for the details. Mostly these slots are used by + buses, so if you have a lot of I2C or SPI devices you may need to + increase it. + +config SPL_TINY_RELOC + bool "Relocate devices into allocated memory before using them" + depends on SPL_TINY + help + Some architectures load SPL into read-only memory. U-Boot needs write + access to tiny devices (specifically struct tinydev) so this cannot + work. If this option is enabled, U-Boot relocates these devices into + RAM when they are needed. + +config SPL_TINY_ONLY + bool "Only support tiny drivers in SPL, not full drivers" + depends on SPL_TINY + help + Enable this to drop support for full drivers in SPL. This enables + 'tiny' drivers for all subsystems and removes the core driver-model + support for full drivers. This can same space, but only works if all + the subsystems used by your board support tiny drivers. + +config TPL_TINY + bool "Support tiny drivers in TPL without full driver-model support" + depends on TPL_OF_PLATDATA + default y + help + Enable support for reducing driver-model overhead with 'tiny' + devices. These drivers have very basic support and do not support + the full driver-model infrastructure. This can be useful for saving + memory in TPL. + +config TPL_TINY_RELOC + bool "Relocate devices into allocated memory before using them" + depends on TPL_TINY + default y if X86 + help + Some architectures load SPL into read-only memory. U-Boot needs write + access to tiny devices (specifically struct tinydev) so this cannot + work. If this option is enabled, U-Boot relocates these devices into + RAM when they are needed. + +config TPL_TINY_ONLY + bool "Only support tiny drivers in SPL, not full drivers" + depends on TPL_TINY + help + Enable this to drop support for full drivers in SPL. This enables + 'tiny' drivers for all subsystems and removes the core driver-model + support for full drivers. This can same space, but only works if all + the subsystems used by your board support tiny drivers. + +config TINY_CHECK + bool "Enable run-time consistency checks" + default y + help + Enabling this catches some errors like drivers with required but + missing options. It adds slightly to code size. Provided that your + code is written correct and doesn't produce errors with this option + enabled, it is generally safe to disable it for production. + + Note that this does not add checks for drivers without an operations + struct. A few drivers don't need operations. Otherwise, don't do that. + config DM_WARN bool "Enable warnings in driver model" depends on DM @@ -148,6 +236,15 @@ config SPL_SYSCON by this uclass, including accessing registers via regmap and assigning a unique number to each. +config SPL_TINY_SYSCON + bool "Support tiny syscon drivers in SPL" + depends on SPL_TINY + default y if SPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config TPL_SYSCON bool "Support system controllers in TPL" depends on TPL_REGMAP @@ -157,6 +254,15 @@ config TPL_SYSCON by this uclass, including accessing registers via regmap and assigning a unique number to each. +config TPL_TINY_SYSCON + bool "Support tiny syscon drivers in TPL" + depends on TPL_TINY + default y if TPL_TINY_ONLY + help + In constrained environments the driver-model overhead of several KB + of code and data structures can be problematic. Enable this to use a + tiny implementation that only supports a single driver. + config DEVRES bool "Managed device resources" depends on DM diff --git a/drivers/core/Makefile b/drivers/core/Makefile index c707026a3a..f3a863c27e 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -2,7 +2,9 @@ # # Copyright (c) 2013 Google, Inc +ifeq ($(CONFIG_$(SPL_TPL_)TINY_ONLY),) obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o +endif obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi.o obj-$(CONFIG_DEVRES) += devres.o obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE) += device-remove.o @@ -15,5 +17,6 @@ ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o +obj-$(CONFIG_$(SPL_TPL_)TINY) += tiny.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/of_extra.c b/drivers/core/of_extra.c index 6420e6ec44..b93b7e43c8 100644 --- a/drivers/core/of_extra.c +++ b/drivers/core/of_extra.c @@ -79,11 +79,9 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type, ofnode node; if (!ofnode_valid(config_node)) { - config_node = ofnode_path("/config"); - if (!ofnode_valid(config_node)) { - debug("%s: Cannot find /config node\n", __func__); + config_node = ofnode_get_config_node(); + if (!ofnode_valid(config_node)) return -ENOENT; - } } if (!suffix) suffix = ""; @@ -127,3 +125,46 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type, return 0; } + +ofnode ofnode_get_config_node(void) +{ + ofnode node; + + node = ofnode_path("/config"); + if (!ofnode_valid(node)) + debug("%s: Cannot find /config node\n", __func__); + + return node; +} + +int ofnode_read_config_int(const char *prop_name, int default_val) +{ + ofnode node; + + log_debug("%s\n", prop_name); + node = ofnode_get_config_node(); + if (!ofnode_valid(node)) + return default_val; + + return ofnode_read_u32_default(node, prop_name, default_val); +} + +int ofnode_read_config_bool(const char *prop_name) +{ + ofnode node; + + log_debug("%s\n", prop_name); + node = ofnode_get_config_node(); + + return ofnode_read_bool(node, prop_name); +} + +const char *ofnode_read_config_string(const char *prop_name) +{ + ofnode node; + + log_debug("%s\n", prop_name); + node = ofnode_get_config_node(); + + return ofnode_read_string(node, prop_name); +} diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a67a237b88..4c33234d1d 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -37,8 +37,7 @@ static struct regmap *regmap_alloc(int count) } #if CONFIG_IS_ENABLED(OF_PLATDATA) -int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count, - struct regmap **mapp) +int regmap_init_mem_platdata(fdt_val_t *reg, int count, struct regmap **mapp) { struct regmap_range *range; struct regmap *map; diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index b5cd763b6b..ec91964a32 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -16,6 +16,17 @@ #include #include +void *syscon_get_first_range(ulong driver_data) +{ + struct regmap *map; + + map = syscon_get_regmap_by_driver_data(driver_data); + if (IS_ERR(map)) + return map; + return regmap_get_range(map, 0); +} + +#if !CONFIG_IS_ENABLED(TINY_SYSCON) /* * Caution: * This API requires the given device has alerady been bound to syscon driver. @@ -52,7 +63,7 @@ static int syscon_pre_probe(struct udevice *dev) #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), + return regmap_init_mem_platdata(plat->reg, ARRAY_SIZE(plat->reg), &priv->regmap); #else return regmap_init_mem(dev_ofnode(dev), &priv->regmap); @@ -155,16 +166,6 @@ struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data) return priv->regmap; } -void *syscon_get_first_range(ulong driver_data) -{ - struct regmap *map; - - map = syscon_get_regmap_by_driver_data(driver_data); - if (IS_ERR(map)) - return map; - return regmap_get_range(map, 0); -} - UCLASS_DRIVER(syscon) = { .id = UCLASS_SYSCON, .name = "syscon", @@ -208,3 +209,48 @@ struct regmap *syscon_node_to_regmap(ofnode node) return r; } +#else +struct tinydev *tiny_syscon_get_by_driver_data(ulong driver_data) +{ + struct tinydev *tdev; + + tdev = tiny_dev_get_by_drvdata(UCLASS_SYSCON, driver_data); + if (!tdev) + return NULL; + + return tdev; +} + +struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data) +{ + struct syscon_uc_info *uc_priv; + struct tinydev *tdev; + + tdev = tiny_syscon_get_by_driver_data(driver_data); + if (!tdev) + return ERR_PTR(-ENODEV); + /* + * We assume that the device has struct syscon_uc_info at the start of + * its private data + */ + uc_priv = tinydev_get_priv(tdev); + + return uc_priv->regmap; +} + +int tiny_syscon_setup(struct tinydev *tdev) +{ + struct syscon_uc_info *priv = tinydev_get_priv(tdev); + + /* + * 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. + */ + struct syscon_base_platdata *plat = tdev->dtplat; + + return regmap_init_mem_platdata(plat->reg, ARRAY_SIZE(plat->reg), + &priv->regmap); +} +#endif diff --git a/drivers/core/tiny.c b/drivers/core/tiny.c new file mode 100644 index 0000000000..4c8d0ced20 --- /dev/null +++ b/drivers/core/tiny.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for tiny device (those without a fully uclass and driver) + * + * Copyright 2020 Google LLC + */ + +#define LOG_CATEGORY LOGC_TINYDEV + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +const char *tiny_dev_name(struct tinydev *tdev) +{ + return tdev->name; +} + +static struct tinydev *tiny_dev_find_tail(struct tinydev *tdev) +{ + if (CONFIG_IS_ENABLED(TINY_RELOC)) { + struct tinydev *copy; + + copy = malloc(sizeof(*copy)); + if (!copy) + return NULL; + memcpy(copy, tdev, sizeof(*copy)); + log_debug(" - found, copied to %p\n", copy); + return copy; + } + log_debug(" - found at %p\n", tdev); + + return tdev; +} + +struct tinydev *tiny_dev_find(enum uclass_id uclass_id, int seq) +{ + struct tinydev *info = ll_entry_start(struct tinydev, tiny_dev); + const int n_ents = ll_entry_count(struct tinydev, tiny_dev); + struct tinydev *entry; + + log_debug("find %d seq %d: n_ents=%d\n", uclass_id, seq, n_ents); + for (entry = info; entry != info + n_ents; entry++) { + struct tiny_drv *drv = entry->drv; + + log_content(" - entry %p, uclass %d %d\n", entry, + drv->uclass_id, uclass_id); + if (drv->uclass_id == uclass_id) + return tiny_dev_find_tail(entry); + } + log_debug(" - not found\n"); + + return NULL; +} + +int tiny_dev_probe(struct tinydev *tdev) +{ + struct tiny_drv *drv; + int ret; + + if (tdev->flags & DM_FLAG_ACTIVATED) + return 0; + if (tdev->parent) { + ret = tiny_dev_probe(tdev->parent); + if (ret) + return log_msg_ret("parent", ret); + /* + * The device might have already been probed during the call to + * tiny_dev_probe() on its parent device. + */ + if (tdev->flags & DM_FLAG_ACTIVATED) + return 0; + } + drv = tdev->drv; + + if (!tdev->priv && drv->priv_size) { + void *priv; + + // This doesn't work with TINY_RELOC + priv = calloc(1, drv->priv_size); + if (!priv) + return -ENOMEM; + tdev->priv = priv; + log_debug("probe: %s: priv=%p\n", tiny_dev_name(tdev), priv); + } + if (drv->probe) { + ret = drv->probe(tdev); + if (ret) + return log_msg_ret("probe", ret); + } + + tdev->flags |= DM_FLAG_ACTIVATED; + + return 0; +} + +struct tinydev *tiny_dev_get(enum uclass_id uclass_id, int seq) +{ + struct tinydev *dev; + int ret; + + dev = tiny_dev_find(uclass_id, seq); + if (!dev) + return NULL; + + ret = tiny_dev_probe(dev); + if (ret) + return NULL; + + return dev; +} + +struct tinydev *tinydev_from_dev_idx(tinydev_idx_t index) +{ + struct tinydev *start = U_BOOT_TINY_DEVICE_START; + + return start + index; +} + +tinydev_idx_t tinydev_to_dev_idx(const struct tinydev *tdev) +{ + struct tinydev *start = U_BOOT_TINY_DEVICE_START; + + return tdev - start; +} + +struct tinydev *tinydev_get_parent(const struct tinydev *tdev) +{ + return tdev->parent; +} + +#ifndef CONFIG_SYS_MALLOC_F +#error "Must enable CONFIG_SYS_MALLOC_F with tinydev" +#endif + +static void *tinydev_lookup_data(struct tinydev *tdev, enum dm_data_t type) +{ + struct tinydev_info *info = &((gd_t *)gd)->tinydev_info; + struct tinydev_data *data; + int i; +#ifdef TIMYDEV_SHRINK_DATA + uint idx = tinydev_to_dev_idx(tdev); + + for (i = 0, data = info->data; i < info->data_count; i++, data++) { + if (data->type == type && data->tdev_idx == idx) + return malloc_ofs_to_ptr(data->ofs); + } +#else + for (i = 0, data = info->data; i < info->data_count; i++, data++) { + if (data->type == type && data->tdev == tdev) + return data->ptr; + } +#endif + + return NULL; +} + +void *tinydev_alloc_data(struct tinydev *tdev, enum dm_data_t type, int size) +{ + struct tinydev_info *info = &((gd_t *)gd)->tinydev_info; + struct tinydev_data *data; + void *ptr; + + if (info->data_count == ARRAY_SIZE(info->data)) { + /* To fix this, increase CONFIG_TINYDEV_DATA_MAX_COUNT */ + panic("tinydev data exhusted"); + return NULL; + } + data = &info->data[info->data_count]; + ptr = calloc(1, size); + if (!ptr) + return NULL; /* alloc_simple() has already written a message */ + data->type = type; +#ifdef TIMYDEV_SHRINK_DATA + data->tdev_idx = tinydev_to_dev_idx(tdev); + data->ofs = malloc_ptr_to_ofs(ptr); +#else + data->tdev = tdev; + data->ptr = ptr; +#endif + log_debug("alloc_data: %d: %s: tdev=%p, type=%d, size=%x, ptr=%p\n", + info->data_count, tiny_dev_name(tdev), tdev, type, size, ptr); + info->data_count++; + + return ptr; +} + +void *tinydev_ensure_data(struct tinydev *tdev, enum dm_data_t type, int size, + bool *existsp) +{ + bool exists = true; + void *ptr; + + ptr = tinydev_lookup_data(tdev, type); + if (!ptr) { + exists = false; + ptr = tinydev_alloc_data(tdev, type, size); + } + if (existsp) + *existsp = exists; + + return ptr; +} + +void *tinydev_get_data(struct tinydev *tdev, enum dm_data_t type) +{ + void *ptr = tinydev_lookup_data(tdev, type); + + if (!ptr) { + log_debug("Cannot find type %d for device %p\n", type, tdev); + panic("tinydev missing data"); + } + + return ptr; +} + +struct tinydev *tiny_dev_get_by_drvdata(enum uclass_id uclass_id, + ulong driver_data) +{ + struct tinydev *info = ll_entry_start(struct tinydev, tiny_dev); + const int n_ents = ll_entry_count(struct tinydev, tiny_dev); + struct tinydev *entry; + + log_debug("find %d driver_data %lx: n_ents=%d\n", uclass_id, + driver_data, n_ents); + for (entry = info; entry != info + n_ents; entry++) { + struct tiny_drv *drv = entry->drv; + + log_content(" - entry %p, uclass %d, driver_data %x\n", entry, + drv->uclass_id, entry->driver_data); + if (drv->uclass_id == uclass_id && + entry->driver_data == driver_data) { + struct tinydev *tdev = tiny_dev_find_tail(entry); + int ret; + + if (!tdev) + return NULL; + ret = tiny_dev_probe(tdev); + if (ret) + return NULL; + return tdev; + } + } + + return NULL; +} diff --git a/include/dm/device.h b/include/dm/device.h index f5738a0cee..1229f0aea6 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -11,6 +11,7 @@ #define _DM_DEVICE_H #include +#include #include #include #include @@ -289,6 +290,126 @@ struct driver { */ #define U_BOOT_DRIVER_ALIAS(__name, __alias) +/** + * struct tiny_drv: A tiny driver + * + * This provides a smaller driver than the full-blown driver-model. It is + * intended for SPL or TPL and offers just a probe() function and some basic + * operations. It has a limit of 256 bytes for the private size. + * + * Note that tiny drivers exist alongside normal ones. Each subsystem must be + * enabled for tiny drivers (e.g. CONFIG_SPL_TINY_SERIAL for serial). + * + * Naming here is changed from struct driver, to make it easier to search code. + * + * @uclass_id: Identifies the uclass we belong to + * @priv_size: If non-zero this is the size of the private data to be allocated + * in the device's ->priv pointer. If zero, then the driver is responsible for + * allocating any data required (but malloc() is discouraged in tiny drivers) + * @ops: Driver-specific operations. This is typically a list of function + * pointers defined by the driver, to implement driver functions required by + * the uclass. + */ +struct tiny_drv { + u8 uclass_id; + u8 priv_size; + int (*probe)(struct tinydev *dev); + struct tinydev *tdev; + void *ops; +}; + +/* Declare a new 'tiny' U-Boot driver */ +#define U_BOOT_TINY_DRIVER(__name) \ + ll_entry_declare(struct tiny_drv, __name, tiny_drv) + +/* Get a pointer to a given tiny driver */ +#define DM_GET_TINY_DRIVER(__name) \ + ll_entry_get(struct tiny_drv, __name, tiny_drv) + +#define DM_DECL_TINY_DRIVER(__name) \ + ll_entry_decl(struct tiny_drv, __name, tiny_drv) + +/* + * Get a pointer to a given tiny driver, for use in data structures. This + * requires that the symbol be declared with DM_DECL_TINY_DRIVER() first + */ +#define DM_REF_TINY_DRIVER(__name) \ + ll_entry_ref(struct tiny_drv, __name, tiny_drv) + +/** + * DM_TINY_PRIV() - Specifies the size of the private data + * + * This does not generate any code, but is parsed by dtoc. Put it inside the + * U_BOOT_TINY_DRIVER on its own lines to specify the amount of data to be + * allocated in tiny_dev->priv + */ +#define DM_TINY_PRIV(hdr,size) .priv_size = size, + +/** + * struct tinydev - A tiny device + * + * This does not have a separate struct driver_info like full devices. The + * platform data is combined into this struct, which is used by dtoc to declare + * devices it finds in the devicetree. + * + * @dtplat: Pointer to the platform data, as generated by dtoc + * @drv: Pointer to the driver + * @flags: Flags for this device DM_FLAG_... + */ +struct tinydev { + const char *name; /* allow this to be dropped */ + /* TODO: Convert these into ushort offsets to a base address */ + void *dtplat; /* u16 word index into dtd data section */ + void *priv; /* u16 word index into device priv section */ + struct tiny_drv *drv; /* u8 index into driver list? */ + u16 flags; /* switch to u8? */ + u8 driver_data; + struct tinydev *parent; +}; + +/* Declare a tiny device with a given name */ +#define U_BOOT_TINY_DEVICE(__name) \ + ll_entry_declare(struct tinydev, __name, tiny_dev) + +#define DM_REF_TINY_DEVICE(__name) \ + ll_entry_ref(struct tinydev, __name, tiny_dev) + +/** + * U_BOOT_TINY_DEVICE_START - Find the start of the list of tiny devices + * + * Use this like this: + * struct tinydev *start = U_BOOT_TINY_DEVICE_START; + */ +#define U_BOOT_TINY_DEVICE_START \ + ll_entry_start(struct tinydev, tiny_dev) + +struct tinydev *tiny_dev_find(enum uclass_id uclass_id, int seq); + +int tiny_dev_probe(struct tinydev *tdev); + +struct tinydev *tiny_dev_get(enum uclass_id uclass_id, int seq); + +struct tinydev *tinydev_from_dev_idx(tinydev_idx_t index); + +tinydev_idx_t tinydev_to_dev_idx(const struct tinydev *tdev); + +struct tinydev *tinydev_get_parent(const struct tinydev *tdev); + +static inline void *tinydev_get_priv(const struct tinydev *tdev) +{ + return tdev->priv; +} + +void *tinydev_get_data(struct tinydev *tdev, enum dm_data_t type); + +void *tinydev_alloc_data(struct tinydev *tdev, enum dm_data_t type, int size); + +void *tinydev_ensure_data(struct tinydev *tdev, enum dm_data_t type, int size, + bool *existsp); + +struct tinydev *tiny_dev_get_by_drvdata(enum uclass_id uclass_id, + ulong driver_data); + /** * dev_get_platdata() - Get the platform data for a device * diff --git a/include/dm/of_extra.h b/include/dm/of_extra.h index ca15df21b0..ccc58b228b 100644 --- a/include/dm/of_extra.h +++ b/include/dm/of_extra.h @@ -86,4 +86,10 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type, const char *suffix, fdt_addr_t *basep, fdt_size_t *sizep); +ofnode ofnode_get_config_node(void); + +int ofnode_read_config_int(const char *prop_name, int default_val); +int ofnode_read_config_bool(const char *prop_name); +const char *ofnode_read_config_string(const char *prop_name); + #endif diff --git a/include/dm/platdata.h b/include/dm/platdata.h index cab93b071b..f75f7d9cee 100644 --- a/include/dm/platdata.h +++ b/include/dm/platdata.h @@ -45,7 +45,22 @@ struct driver_info { #define U_BOOT_DEVICES(__name) \ ll_entry_declare_list(struct driver_info, __name, driver_info) -/* Get a pointer to a given driver */ +/** + * Get a pointer to a given device info given its name + * + * With the declaration U_BOOT_DEVICE(name), DM_GET_DEVICE(name) will return a + * pointer to the struct driver_info created by that declaration. + * + * if OF_PLATDATA is enabled, from this it is possible to use the @dev member of + * struct driver_info to find the device pointer itself. + * + * TODO(sjg at chromium.org): U_BOOT_DEVICE() tells U-Boot to create a device, so + * the naming seems sensible, but DM_GET_DEVICE() is a bit of misnomer, since it + * finds the driver_info record, not the device. + * + * @__name: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000) + * @return struct driver_info * to the driver that created the device + */ #define DM_GET_DEVICE(__name) \ ll_entry_get(struct driver_info, __name, driver_info) @@ -57,4 +72,7 @@ struct driver_info { * by dtoc when parsing dtb. */ void dm_populate_phandle_data(void); + +#define IF_OF_PLATDATA(x) CONFIG_IS_ENABLED(OF_PLATDATA, (x)) + #endif diff --git a/include/dm/tiny_struct.h b/include/dm/tiny_struct.h new file mode 100644 index 0000000000..3de52d83ec --- /dev/null +++ b/include/dm/tiny_struct.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Structures for inclusion in global_data + * + * Copyright 2020 Google LLC + * Written by Simon Glass + */ + +#ifndef __DM_TINY_STRUCT_H +#define __DM_TINY_STRUCT_H + +/* A struct tinydev * stored as an index into the device linker-list */ +typedef u8 tinydev_idx_t; + +/* enum dm_data_t - Types of data that can be attached to devices */ +enum dm_data_t { + DEVDATAT_PLAT, + DEVDATAT_PARENT_PLAT, + DEVDATAT_UC_PLAT, + + DEVDATAT_PRIV, + DEVDATAT_PARENT_PRIV, + DEVDATAT_UC_PRIV, +}; + +struct tinydev_data { + u8 type; +#ifdef TIMYDEV_SHRINK_DATA + tinydev_idx_t tdev_idx; + ushort ofs; +#else + struct tinydev *tdev; + void *ptr; +#endif +}; + +struct tinydev_info { + int data_count; + struct tinydev_data data[CONFIG_TINYDEV_DATA_MAX_COUNT]; +}; + +#endif diff --git a/include/linker_lists.h b/include/linker_lists.h index d775d041e0..8419e752b1 100644 --- a/include/linker_lists.h +++ b/include/linker_lists.h @@ -210,6 +210,12 @@ _ll_result; \ }) +#define ll_entry_decl(_type, _name, _list) \ + extern _type _u_boot_list_2_##_list##_2_##_name + +#define ll_entry_ref(_type, _name, _list) \ + (_type *)&_u_boot_list_2_##_list##_2_##_name + /** * ll_start() - Point to first entry of first linker-generated array * @_type: Data type of the entry diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index e6d56a1286..47d9a60c22 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -303,10 +303,12 @@ $(obj)/$(SPL_BIN).dtb: dts/dt-spl.dtb FORCE pythonpath = PYTHONPATH=scripts/dtc/pylibfdt quiet_cmd_dtocc = DTOC C $@ -cmd_dtocc = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb -o $@ platdata +cmd_dtocc = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb \ + -o $@ -c $(KCONFIG_CONFIG) -s $(srctree) platdata quiet_cmd_dtoch = DTOC H $@ -cmd_dtoch = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb -o $@ struct +cmd_dtoch = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb \ + -o $@ -s $(srctree) struct quiet_cmd_plat = PLAT $@ cmd_plat = $(CC) $(c_flags) -c $< -o $(filter-out $(PHONY),$@) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 6afe1e0aad..4e5fdfa8ba 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -21,6 +21,7 @@ import sys from dtoc import fdt from dtoc import fdt_util +from patman import tools # When we see these properties we ignore them - i.e. do not create a structure # member @@ -56,6 +57,22 @@ VAL_PREFIX = 'dtv_' # phandles is len(args). This is a list of integers. PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) +class DriverInfo: + def __init__(self, name, uclass_id, compat): + self.name = name + self.uclass_id = uclass_id + self.compat = compat + self.priv_size = 0 + + def __eq__(self, other): + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.compat == other.compat and + self.priv_size == other.priv_size) + + def __repr__(self): + return ("DriverInfo(name='%s', uclass_id='%s', compat=%s, priv_size=%s)" % + (self.name, self.uclass_id, self.compat, self.priv_size)) def conv_name_to_c(name): """Convert a device-tree name to a C identifier @@ -144,6 +161,7 @@ class DtbPlatdata(object): Properties: _fdt: Fdt object, referencing the device tree _dtb_fname: Filename of the input device tree binary file + _config_fname: Filename of the .config file for the build _valid_nodes: A list of Node object with compatible strings _include_disabled: true to include nodes marked status = "disabled" _outfile: The current output file (sys.stdout or a real file) @@ -158,19 +176,27 @@ class DtbPlatdata(object): U_BOOT_DRIVER_ALIAS(driver_alias, driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name) _links: List of links to be included in dm_populate_phandle_data() + _tiny_uclasses: List of uclass names that are marked as 'tiny' """ - def __init__(self, dtb_fname, include_disabled, warning_disabled): + def __init__(self, dtb_fname, config_fname, include_disabled, + warning_disabled): self._fdt = None self._dtb_fname = dtb_fname + self._config_fname = config_fname self._valid_nodes = None self._include_disabled = include_disabled self._outfile = None self._warning_disabled = warning_disabled self._lines = [] - self._aliases = {} - self._drivers = [] + self._compat_aliases = {} + self._drivers = {} self._driver_aliases = {} self._links = [] + self._aliases = {} + self._aliases_by_path = {} + self._tiny_uclasses = [] + self._of_match = {} + self._compat_to_driver = {} def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -195,8 +221,8 @@ class DtbPlatdata(object): compat_c = self._driver_aliases.get(compat_c) if not compat_c: if not self._warning_disabled: - print('WARNING: the driver %s was not found in the driver list' - % (compat_c_old)) + print('WARNING: the driver %s was not found in the driver list. Check that your driver has the same name as one of its compatible strings' % + (compat_c_old)) compat_c = compat_c_old else: aliases_c = [compat_c_old] + aliases_c @@ -217,6 +243,10 @@ class DtbPlatdata(object): else: self._outfile = open(fname, 'w') + def close_output(self): + if self._outfile is not sys.stdout: + self._outfile.close() + def out(self, line): """Output a string to the output file @@ -287,8 +317,8 @@ class DtbPlatdata(object): break target = self._fdt.phandle_to_node.get(phandle) if not target: - raise ValueError("Cannot parse '%s' in node '%s'" % - (prop.name, node_name)) + raise ValueError("Cannot parse '%s' in node '%s' (phandle=%d)" % + (prop.name, node_name, phandle)) cells = None for prop_name in ['#clock-cells', '#gpio-cells']: cells = target.props.get(prop_name) @@ -315,33 +345,144 @@ class DtbPlatdata(object): with open(fn) as fd: buff = fd.read() - # The following re will search for driver names declared as - # U_BOOT_DRIVER(driver_name) - drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff) + drivers = {} + + # Dict of compatible strings in a udevice_id array: + # key: udevice_id array name (e.g. 'rk3288_syscon_ids_noc') + # value: Dict of compatible strings in that array: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + of_match = {} + + m_drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) + if m_drivers: + driver_name = None + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + uclass_id = None + re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Collect the compatible string, e.g. 'rockchip,rk3288-grf' + compat = None + #re_compat = re.compile('{\s*.compatible\s*=\s*"(.*)"\s*},') + + re_compat = re.compile('{\s*.compatible\s*=\s*"(.*)"\s*' + '(,\s*.data\s*=\s*(.*))?\s*},') + + # This is a dict of compatible strings that were found: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + compat_dict = {} + + # Holds the var nane of the udevice_id list, e.g. + # 'rk3288_syscon_ids_noc' in + # static const struct udevice_id rk3288_syscon_ids_noc[] = { + ids_name = None + re_ids = re.compile('struct udevice_id (.*)\[\]\s*=') + + # Matches the references to the udevice_id list + re_of_match = re.compile('\.of_match\s*=\s*([a-z0-9_]+),') + + # Matches the header/size information for tinydev + re_tiny_priv = re.compile('^\s*DM_TINY_PRIV\((.*)\)$') + tiny_name = None + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue - for driver in drivers: - self._drivers.append(driver) + # If we have seen U_BOOT_DRIVER()... + if driver_name: + id_m = re_id.search(line) + id_of_match = re_of_match.search(line) + if id_m: + uclass_id = id_m.group(1) + elif id_of_match: + compat = id_of_match.group(1) + elif '};' in line: + if uclass_id and compat: + if compat not in of_match: + raise ValueError("%s: Unknown compatible var '%s' (found %s)" % + (fn, compat, ','.join(of_match.keys()))) + driver = DriverInfo(driver_name, uclass_id, + of_match[compat]) + drivers[driver_name] = driver + + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for id in of_match[compat]: + old = self._compat_to_driver.get(id) + if not old or driver.name < old.name: + self._compat_to_driver[id] = driver + else: + pass + #print("%s: Cannot find .id/.of_match in driver '%s': uclass_id=%s, compat=%s" % + #(fn, driver_name, uclass_id, compat)) + driver_name = None + uclass_id = None + ids_name = None + compat = None + compat_dict = {} + + # If we have seen U_BOOT_TINY_DRIVER()... + elif tiny_name: + tiny_priv = re_tiny_priv.match(line) + if tiny_priv: + drivers[tiny_name].priv_size = tiny_priv.group(1) + elif '};' in line: + tiny_name = None + elif ids_name: + compat_m = re_compat.search(line) + if compat_m: + compat_dict[compat_m.group(1)] = compat_m.group(3) + elif '};' in line: + of_match[ids_name] = compat_dict + ids_name = None + elif 'U_BOOT_DRIVER' in line: + match = re.search(r'U_BOOT_DRIVER\((.*)\)', line) + if match: + driver_name = match.group(1) + elif 'U_BOOT_TINY_DRIVER' in line: + match = re.search(r'U_BOOT_TINY_DRIVER\((.*)\)', line) + if match: + tiny_name = match.group(1) + if tiny_name not in drivers: + raise ValueError("%s: Tiny driver '%s' must have a corresponding full driver in the same file (found %s)" % + (fn, tiny_name, drivers)) + else: + ids_m = re_ids.search(line) + if ids_m: + ids_name = ids_m.group(1) - # The following re will search for driver aliases declared as - # U_BOOT_DRIVER_ALIAS(alias, driver_name) - driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', - buff) + self._drivers.update(drivers) + self._of_match.update(of_match) - for alias in driver_aliases: # pragma: no cover - if len(alias) != 2: - continue - self._driver_aliases[alias[1]] = alias[0] + # The following re will search for driver aliases declared as + # U_BOOT_DRIVER_ALIAS(alias, driver_name) + driver_aliases = re.findall( + 'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', + buff) - def scan_drivers(self): + for alias in driver_aliases: # pragma: no cover + if len(alias) != 2: + continue + self._driver_aliases[alias[1]] = alias[0] + + def scan_drivers(self, srcpath): """Scan the driver folders to build a list of driver names and aliases This procedure will populate self._drivers and self._driver_aliases """ - basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') - if basedir == '': - basedir = './' - for (dirpath, dirnames, filenames) in os.walk(basedir): + for (dirpath, dirnames, filenames) in os.walk(srcpath): for fn in filenames: if not fn.endswith('.c'): continue @@ -355,23 +496,30 @@ class DtbPlatdata(object): """ self._fdt = fdt.FdtScan(self._dtb_fname) - def scan_node(self, root): + def scan_node(self, parent, level): """Scan a node and subnodes to build a tree of node and phandle info This adds each node to self._valid_nodes. Args: - root: Root node for scan + parent: Parent node for scan """ - for node in root.subnodes: + for node in parent.subnodes: if 'compatible' in node.props: status = node.props.get('status') if (not self._include_disabled and not status or status.value != 'disabled'): self._valid_nodes.append(node) + if level == 0 and node.name == 'aliases': + for prop in node.props.values(): + self._aliases[prop.name] = prop.value + match = re.match('^(.*[a-z])[0-9]+', prop.name) + if match: + self._aliases_by_path[prop.value] = match.group(1) + # recurse to handle any subnodes - self.scan_node(node) + self.scan_node(node, level + 1) def scan_tree(self): """Scan the device tree for useful information @@ -381,7 +529,26 @@ class DtbPlatdata(object): platform data """ self._valid_nodes = [] - return self.scan_node(self._fdt.GetRoot()) + self.scan_node(self._fdt.GetRoot(), 0) + + def parse_config(self, config_data): + tiny_list = re.findall(r'CONFIG_[ST]PL_TINY_(.*)=y', config_data) + self._tiny_uclasses = [n.lower() for n in tiny_list + if n not in ['MEMSET', 'RELOC', 'ONLY']] + + def scan_config(self): + if self._config_fname: + self.parse_config(tools.ReadFile(self._config_fname, binary=False)) + unused = set(self._tiny_uclasses) + for node in self._valid_nodes: + node.is_tiny = False + alias = self._aliases_by_path.get(node.path) + if alias and alias in self._tiny_uclasses: + node.is_tiny = True + unused.discard(alias) + if unused: + print('Warning: Some tiny uclasses lack aliases or a device: %s' % + ', '.join(unused)) @staticmethod def get_num_cells(node): @@ -491,7 +658,7 @@ class DtbPlatdata(object): struct_name, aliases = self.get_normalized_compat_name(node) for alias in aliases: - self._aliases[alias] = struct_name + self._compat_aliases[alias] = struct_name return structs @@ -555,7 +722,7 @@ class DtbPlatdata(object): self.out(';\n') self.out('};\n') - for alias, struct_name in self._aliases.items(): + for alias, struct_name in self._compat_aliases.items(): if alias not in sorted(structs): self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias, STRUCT_PREFIX, struct_name)) @@ -600,7 +767,8 @@ class DtbPlatdata(object): (VAL_PREFIX, var_name, member_name, item) # Save the the link information to be use to define # dm_populate_phandle_data() - self._links.append({'var_node': var_node, 'dev_name': name}) + if not target_node.is_tiny: + self._links.append({'var_node': var_node, 'dev_name': name}) item += 1 for val in vals: self.buf('\n\t\t%s,' % val) @@ -617,6 +785,8 @@ class DtbPlatdata(object): struct_name, _ = self.get_normalized_compat_name(node) var_name = conv_name_to_c(node.name) + + # Tiny devices don't have 'static' since it is used by the driver self.buf('static struct %s%s %s%s = {\n' % (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) for pname in sorted(node.props): @@ -634,13 +804,59 @@ class DtbPlatdata(object): 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') + if node.is_tiny: + val = node.props['compatible'].value + if not isinstance(val, list): + val = [val] + for compat in val: + driver = self._compat_to_driver.get(compat) + if driver: + break + if not driver: + raise ValueError("Cant' find driver for compatible '%s' (%s)'" % + (', '.join(val), 'all')) + self.buf('DM_DECL_TINY_DRIVER(%s);\n' % driver.name); + priv_name = None + inline = True + if inline and driver.priv_size: + parts = driver.priv_size.split(',') + if len(parts) == 2: + hdr, size = parts + else: + hdr = None + size = parts[0] + priv_name = '_%s_priv' % var_name + if hdr: + self.buf('#include %s\n' % hdr) + section = '__attribute__ ((section (".data")))' + + self.buf('u8 %s[%s] %s;\n' % (priv_name, size.strip(), section)) + + self.buf('U_BOOT_TINY_DEVICE(%s) = {\n' % var_name) + self.buf('\t.dtplat\t\t= &%s%s,\n' % (VAL_PREFIX, var_name)) + self.buf('\t.drv\t\t= DM_REF_TINY_DRIVER(%s),\n' % driver.name) + driver_data = driver.compat[compat] + if driver_data is not None: + self.buf('\t.driver_data\t\t= %s,\n' % driver_data) + if priv_name: + self.buf('\t.priv\t\t= %s,\n' % priv_name) + self.buf('\t.name\t\t= "%s",\n' % node.name) + if node.parent and node.parent.name != '/': + parent_name = conv_name_to_c(node.parent.name) + self.buf('\t.parent\t\t= DM_REF_TINY_DEVICE(%s),\n' % + parent_name) + self.buf('};\n') + self.buf('\n') + else: + # 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('\t.dev\t\t= NULL,\n') + self.buf('};\n') + self.buf('\n') self.out(''.join(self.get_buf())) @@ -659,6 +875,15 @@ class DtbPlatdata(object): self.out('#include \n') self.out('#include \n') self.out('\n') + + self.out('/*\n') + self.out(' * Tiny uclasses: %s\n' % (', '.join(self._tiny_uclasses))) + self.out(' * Aliases with CONFIG_SPL_TINY_... enabled\n') + for path, alias in self._aliases_by_path.items(): + if alias in self._tiny_uclasses: + self.out(' * %s: %s\n' % (path, alias)) + self.out('*/\n') + self.out('\n') nodes_to_output = list(self._valid_nodes) # Keep outputing nodes until there is none left @@ -682,8 +907,10 @@ class DtbPlatdata(object): self.buf('}\n') self.out(''.join(self.get_buf())) + self.close_output() -def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False): +def run_steps(args, dtb_file, config_file, include_disabled, output, srcpath, + warning_disabled=False): """Run all the steps of the dtoc tool Args: @@ -697,10 +924,12 @@ def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False): if not args: raise ValueError('Please specify a command: struct, platdata') - plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled) - plat.scan_drivers() + plat = DtbPlatdata(dtb_file, config_file, include_disabled, + warning_disabled) + plat.scan_drivers(srcpath) plat.scan_dtb() plat.scan_tree() + plat.scan_config() plat.scan_reg_sizes() plat.setup_output(output) structs = plat.scan_structs() @@ -714,3 +943,4 @@ def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False): else: raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd) + return plat diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/dtoc_test_simple.dts index 165680bd4b..43d347bad3 100644 --- a/tools/dtoc/dtoc_test_simple.dts +++ b/tools/dtoc/dtoc_test_simple.dts @@ -10,6 +10,12 @@ / { #address-cells = <1>; #size-cells = <1>; + + aliases { + i2c0 = &i2c0; + serial0 = &serial0; + }; + spl-test { u-boot,dm-pre-reloc; compatible = "sandbox,spl-test"; @@ -47,7 +53,7 @@ compatible = "sandbox,spl-test.2"; }; - i2c at 0 { + i2c0: i2c at 0 { compatible = "sandbox,i2c-test"; u-boot,dm-pre-reloc; #address-cells = <1>; @@ -59,4 +65,8 @@ low-power; }; }; + + serial0: serial { + compatible = "sandbox,serial"; + }; }; diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index f78b7f84f0..3d4bc3b2ef 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -60,9 +60,9 @@ def BytesToValue(data): Type of data Data, either a single element or a list of elements. Each element is one of: - Type.STRING: str/bytes value from the property - Type.INT: a byte-swapped integer stored as a 4-byte str/bytes - Type.BYTE: a byte stored as a single-byte str/bytes + Type.STRING: str value from the property + Type.INT: a big-endian integer stored as a 4-byte bytes + Type.BYTE: a byte stored as a single-byte bytes """ data = bytes(data) size = len(data) @@ -104,6 +104,7 @@ class Prop: Properties: name: Property name (as per the device tree) + bytes: Property value as raw bytes value: Property value as a string of bytes, or a list of strings of bytes type: Value type diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index b94d9c301f..2fa3e26cf8 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -87,6 +87,8 @@ if __name__ != '__main__': parser = OptionParser() parser.add_option('-B', '--build-dir', type='string', default='b', help='Directory containing the build output') +parser.add_option('-c', '--config', action='store', + help='Select .config filename') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') parser.add_option('--include-disabled', action='store_true', @@ -95,6 +97,8 @@ parser.add_option('-o', '--output', action='store', default='-', help='Select output filename') parser.add_option('-P', '--processes', type=int, help='set number of processes to use for running tests') +parser.add_option('-s', '--srcpath', type='string', + help='Specify the source directory for U-Boot') parser.add_option('-t', '--test', action='store_true', dest='test', default=False, help='run tests') parser.add_option('-T', '--test-coverage', action='store_true', @@ -110,5 +114,6 @@ elif options.test_coverage: RunTestCoverage() else: - dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, - options.output) + dtb_platdata.run_steps(args, options.dtb_file, options.config, + options.include_disabled, options.output, + options.srcpath) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 7afab2ef66..c730ecdb1a 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -11,12 +11,14 @@ tool. import collections import os +import re import struct import sys import unittest from dtoc import dtb_platdata from dtb_platdata import conv_name_to_c +from dtb_platdata import DriverInfo from dtb_platdata import get_compat_name from dtb_platdata import get_value from dtb_platdata import tab_to @@ -52,6 +54,38 @@ C_EMPTY_POPULATE_PHANDLE_DATA = '''void dm_populate_phandle_data(void) { } ''' +CONFIG_FILE_DATA = ''' +CONFIG_SOMETHING=1234 +CONFIG_SPL_TINY_SPI=y +# CONFIG_SPL_TINY_SPI is not set +CONFIG_SPL_TINY_I2C=y +CONFIG_SOMETHING_ELSE=5678 +''' + +DRIVER_FILE_DATA = ''' +static const struct udevice_id xhci_usb_ids[] = { + { .compatible = "rockchip,rk3328-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci_rockchip", + .id = UCLASS_USB, + .of_match = xhci_usb_ids, + .ops = &xhci_usb_ops, +}; + +static const struct udevice_id usb_phy_ids[] = { + { .compatible = "rockchip,rk3328-usb3-phy" }, + { } +}; + +U_BOOT_DRIVER(usb_phy) = { + .name = "usb_phy_rockchip", + .id = UCLASS_PHY, + .of_match = usb_phy_ids, +}; +''' def get_dtb_file(dts_fname, capture_stderr=False): """Compile a .dts file to a .dtb @@ -75,7 +109,8 @@ class TestDtoc(unittest.TestCase): @classmethod def tearDownClass(cls): - tools._RemoveOutputDir() + #tools._RemoveOutputDir() + pass def _WritePythonString(self, fname, data): """Write a string with tabs expanded as done in this Python file @@ -860,3 +895,57 @@ U_BOOT_DEVICE(spl_test2) = { self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn("Unknown command 'invalid-cmd': (use: struct, platdata)", str(e.exception)) + + def testAliases(self): + """Test we can get a list of aliases""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + plat = dtb_platdata.DtbPlatdata(dtb_file, False) + plat.scan_dtb() + plat.scan_tree() + self.assertEqual({'i2c0': '/i2c at 0'}, plat._aliases) + + def testReadConfig(self): + """Test we can get a list of 'tiny' uclasses""" + plat = dtb_platdata.DtbPlatdata(None, None, None) + plat.parse_config(CONFIG_FILE_DATA) + self.assertEqual(['serial', 'i2c'], plat._tiny_uclasses) + + def do_platdata_run(self): + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + config_file = tools.GetOutputFilename('config') + tools.WriteFile(config_file, CONFIG_FILE_DATA, binary=False) + plat = dtb_platdata.run_steps(['platdata'], dtb_file, config_file, + False, output) + return plat, output + + def testDetectTiny(self): + """Test we detect devices that need to be 'tiny'""" + plat, _ = self.do_platdata_run() + tiny_nodes = [node.name for node in plat._valid_nodes if node.is_tiny] + self.assertEqual(['i2c at 0'], tiny_nodes) + + def testTinyNoStruct(self): + """Test we don't output U_BOOT_DEVICE for 'tiny' devices""" + plat, output = self.do_platdata_run() + data = tools.ReadFile(output) + dev_list = re.findall(b'U_BOOT_DEVICE\((.*)\)', data) + self.assertNotIn(b'i2c_at_0', dev_list) + + # Find dtd declarations with 'static' in them + #dtv_list = re.findall(b'(.*)struct dtd.* dtv_(.*) =', data) + #has_static = [b for a, b in dtv_list if a == 'static '] + #self.assertNotIn(b'i2c_at_0', has_static) + + def testTinyUclass(self): + plat = dtb_platdata.DtbPlatdata(None, None, None) + driver_file = tools.GetOutputFilename('driver.c') + tools.WriteFile(driver_file, DRIVER_FILE_DATA, binary=False) + plat.scan_driver(driver_file) + self.assertEqual(['usb_phy', 'usb_xhci'], sorted(plat._drivers.keys())) + self.assertEqual(DriverInfo(name='usb_phy', uclass_id='UCLASS_PHY', + compat=['rockchip,rk3328-usb3-phy']), + plat._drivers['usb_phy']) + self.assertEqual(DriverInfo(name='usb_xhci', uclass_id='UCLASS_USB', + compat=['rockchip,rk3328-xhci']), + plat._drivers['usb_xhci']) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index e1977a2ff3..c81dfabfe2 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -270,7 +270,7 @@ def ReadFile(fname, binary=True): #(fname, len(data), len(data))) return data -def WriteFile(fname, data): +def WriteFile(fname, data, binary=True): """Write data into a file. Args: @@ -279,7 +279,7 @@ def WriteFile(fname, data): """ #self._out.Info("Write file '%s' size %d (%#0x)" % #(fname, len(data), len(data))) - with open(Filename(fname), 'wb') as fd: + with open(Filename(fname), binary and 'wb' or 'w') as fd: fd.write(data) def GetBytes(byte, size):