* [RFC PATCH v2 1/3] dm: Driver and uclass changes for tiny-dm
2020-07-02 21:10 [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Simon Glass
@ 2020-07-02 21:10 ` Simon Glass
2020-07-02 21:10 ` [RFC PATCH v2 2/3] dm: Arch-specific " Simon Glass
` (3 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2020-07-02 21:10 UTC (permalink / raw)
To: u-boot
This includes various changes to support tiny-dm in serial, ram, clock,
spi, SPI flash, syscon and sysreset drivers.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
(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(<asm/arch-rockchip/cru_rk3288.h>, \
+ 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 <common.h>
#include <dm.h>
+#include <dt-structs.h>
#include <log.h>
#include <malloc.h>
#include <spi.h>
@@ -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(<spi_flash.h>, 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 <common.h>
#include <log.h>
#include <dm/device_compat.h>
@@ -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 <dm/lists.h>
#include <dm/root.h>
+#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 <power/regulator.h>
#include <power/rk8xx_pmic.h>
-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(<asm/arch-rockchip/sdram_rk3288.h>, \
+ 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 <debug_uart.h>
+
+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 <linux/compiler.h>
#include <asm/state.h>
+#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 <debug_uart.h>
-
-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 <debug_uart.h>
+
+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 <common.h>
+#include <debug_uart.h>
#include <dm.h>
#include <env_internal.h>
#include <errno.h>
+#include <log.h>
#include <malloc.h>
#include <os.h>
#include <serial.h>
@@ -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 <debug_uart.h>
#include <dm.h>
#include <dt-structs.h>
+#include <log.h>
#include <ns16550.h>
#include <serial.h>
#include <asm/arch-rockchip/clock.h>
@@ -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(<ns16550.h>, 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, <superpeter.cai@gmail.com>.
*/
+#define LOG_CATEGORY UCLASS_SPI
+
#include <common.h>
#include <clk.h>
#include <dm.h>
@@ -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(<asm/arch-rockchip/spi.h>, \
+ 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 <common.h>
#include <dm.h>
#include <errno.h>
@@ -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 <linux/delay.h>
#include <linux/err.h>
+#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 <common.h>
#include <dm.h>
#include <errno.h>
@@ -13,9 +15,9 @@
#include <asm/arch-rockchip/hardware.h>
#include <linux/err.h>
-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(<asm/arch-rockchip/clock.h>, 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 <fdtdec.h>
#include <membuff.h>
#include <linux/list.h>
+#include <dm/tiny_struct.h>
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 <linux/list.h>
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 <common.h>
#include <linux/bitops.h>
+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
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 3/3] dm: Core changes for tiny-dm
2020-07-02 21:10 [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Simon Glass
2020-07-02 21:10 ` [RFC PATCH v2 1/3] dm: Driver and uclass changes for tiny-dm Simon Glass
2020-07-02 21:10 ` [RFC PATCH v2 2/3] dm: Arch-specific " Simon Glass
@ 2020-07-02 21:10 ` Simon Glass
2020-07-03 2:19 ` [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Heinrich Schuchardt
2020-07-10 4:12 ` Walter Lozano
4 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2020-07-02 21:10 UTC (permalink / raw)
To: u-boot
This patch includes changes to support tiny-dm in driver model and dtoc.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
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 +
| 49 ++++-
drivers/core/regmap.c | 3 +-
drivers/core/syscon-uclass.c | 68 +++++--
drivers/core/tiny.c | 249 +++++++++++++++++++++++++
include/dm/device.h | 121 ++++++++++++
| 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 : "<missing>";
}
@@ -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 <common.h>
+#include <dm.h>
#include <image.h>
#include <log.h>
#include <spi.h>
#include <spi_flash.h>
#include <errno.h>
#include <spl.h>
+#include <dm/of_extra.h>
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(<ns16550.h>, 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 <ns16550.h>
+ 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 <sjg@chromium.org>
+.. 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
--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 <dm/root.h>
#include <linux/err.h>
+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 <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+
+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 <dm/ofnode.h>
+#include <dm/tiny_struct.h>
#include <dm/uclass-id.h>
#include <fdtdec.h>
#include <linker_lists.h>
@@ -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
*
--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 <sjg@chromium.org>
+ */
+
+#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 <dm.h>\n')
self.out('#include <dt-structs.h>\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):
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread