All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
@ 2020-07-02 21:10 Simon Glass
  2020-07-02 21:10 ` [RFC PATCH v2 1/3] dm: Driver and uclass changes for tiny-dm Simon Glass
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Simon Glass @ 2020-07-02 21:10 UTC (permalink / raw)
  To: u-boot

This series provides a proposed enhancement to driver model to reduce
overhead in SPL.

These patches should not be reviewed other than to comment on the
approach. The code is all lumped together in a few patches and so cannot
be applied as is.

For now, the source tree is available at:

   https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working

Comments welcome!

Benefits (good news)
--------------------

As an example of the impact of tiny-dm, chromebook_jerry is converted to
use it. This shows approximately a 30% reduction in code and data size and
a 85% reduction in malloc() space over of-platdata:

   text	   data	    bss	    dec	    hex	filename
  25248	   1836	     12	  27096	   69d8	spl/u-boot-spl (original with DT)
  19727	   3436	     12	  23175	   5a87	spl/u-boot-spl (OF_PLATDATA)
    78%    187%    100%     86%         as %age of original

  13784	   1408	     12	  15204	   3b64	spl/u-boot-spl (SPL_TINY)
    70%     41%    100%     66%         as %age of platdata
    55%     77%    100%     56%         as %age of original

SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116 (SPL_TINY).

Overall the 'overhead' of tiny-dm is much less than the full driver
model. Code size is currently about 600 bytes for these functions on
Thumb2:

   00000054 T tiny_dev_probe
   00000034 T tiny_dev_get_by_drvdata
   00000024 T tiny_dev_find
   0000001a T tiny_dev_get
   0000003c T tinydev_alloc_data
   0000002a t tinydev_lookup_data
   00000022 T tinydev_ensure_data
   00000014 T tinydev_get_data
   00000004 T tinydev_get_parent

Effort (bad news)
-----------------

Unfortunately it is quite a bit of work to convert drivers over to
tiny-dm. First, the of-platdata conversion must be done. But on top of
that, tiny-dm needs entirely separate code for dealing with devices. This
means that instead of 'struct udevice' and 'struct uclass' there is just
'struct tinydev'. Each driver and uclass must be modified to support
both, pulling common code into internal static functions.

Another option
--------------

Note: It is assumed that any board that is space-contrained should use
of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
reduce device-tree overhead by approximately 4KB.

Designing tiny-dm has suggested a number of things that could be changed
in the current driver model to make it more space-efficient for TPL and
SPL. The ones with least impact on driver code are (CS=reduces code size,
DS=reduces data size):

   CS - drop driver_bind() and create devices (struct udevice) at
        build-time
   CS - allocate all device- and uclass-private data at build-time
   CS - remove all but the basic operations for each uclass (e.g. SPI
        flash only supports reading)
   DS - use 8-bit indexes instead of 32/64-bit pointers for device
        pointers possible since these are created at build-time)
   DS - use singly-linked lists
   DS - use 16-bit offsets to private data, instead of 32/64-bit pointers
        (possible since it is all in SRAM relative to malloc() base,
        presumably word-aligned and < 256KB)
   DS - move private pointers into a separate data structure so that NULLs
        are not stored
   CS / DS - Combine req_seq and seq and calculate the new value at
        build-time

More difficult are:

   DS - drop some of the lesser-used driver and uclass methods
   DS - drop all uclass methods except init()
   DS - drop all driver methods except probe()
   CS / DS - drop uclasses and require drivers to manually call uclass
        functions

Even with all of this we would not reach tiny-dm and it would muddy up the
driver-model datas structures. But we might come close to tiny-dm on size
and there are some advantages:

- much of the benefit would apply to all boards that use of-platdata (i.e.
  with very little effort on behalf of board maintainers)
- the impact on the code base is much less (we keep a single, unified
  driver mode in SPL and U-Boot proper)

Overall I think it is worth looking@this option. While it doesn't have
the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
driver code as much and it is easier to learn.

Changes in v2:
- Various updates, and ported to chromebook_jerry (rockchip)

Simon Glass (3):
  dm: Driver and uclass changes for tiny-dm
  dm: Arch-specific changes for tiny-dm
  dm: Core changes for tiny-dm

 arch/arm/dts/rk3288-u-boot.dtsi               |  17 +-
 arch/arm/dts/rk3288-veyron.dtsi               |  26 +-
 arch/arm/dts/rk3288.dtsi                      |   3 +
 arch/arm/include/asm/arch-rockchip/clock.h    |   9 +
 .../include/asm/arch-rockchip/sdram_rk3288.h  |  21 ++
 arch/arm/include/asm/arch-rockchip/spi.h      |   1 +
 arch/arm/include/asm/io.h                     |  18 +
 arch/arm/mach-rockchip/rk3288/clk_rk3288.c    |  46 +++
 arch/arm/mach-rockchip/rk3288/rk3288.c        |   2 +
 arch/arm/mach-rockchip/rk3288/syscon_rk3288.c |  45 +--
 arch/arm/mach-rockchip/sdram.c                |  33 +-
 arch/sandbox/cpu/u-boot-spl.lds               |  12 +
 arch/sandbox/dts/sandbox.dts                  |   3 +-
 arch/sandbox/dts/sandbox.dtsi                 |   2 +-
 arch/x86/cpu/apollolake/cpu_spl.c             |   5 +-
 arch/x86/cpu/apollolake/uart.c                |  56 ++++
 arch/x86/dts/chromebook_coral.dts             |   1 +
 arch/x86/lib/tpl.c                            |   4 +-
 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 +++--
 configs/chromebook_coral_defconfig            |   1 +
 configs/chromebook_jerry_defconfig            |  11 +
 configs/rock2_defconfig                       |   3 +
 doc/develop/debugging.rst                     |  35 ++
 doc/driver-model/tiny-dm.rst                  | 315 +++++++++++++++++
 drivers/clk/Kconfig                           |  54 +++
 drivers/clk/Makefile                          |   4 +-
 drivers/clk/clk-uclass.c                      |  53 ++-
 drivers/clk/rockchip/clk_rk3288.c             | 106 ++++--
 drivers/core/Kconfig                          | 106 ++++++
 drivers/core/Makefile                         |   3 +
 drivers/core/of_extra.c                       |  49 ++-
 drivers/core/regmap.c                         |   3 +-
 drivers/core/syscon-uclass.c                  |  68 +++-
 drivers/core/tiny.c                           | 249 ++++++++++++++
 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/dm/device.h                           | 121 +++++++
 include/dm/of_extra.h                         |   6 +
 include/dm/platdata.h                         |  20 +-
 include/dm/tiny_struct.h                      |  42 +++
 include/linker_lists.h                        |   6 +
 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 +
 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 +-
 92 files changed, 3454 insertions(+), 540 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

-- 
2.27.0.212.ge8ba1cc988-goog

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [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), &params->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(&regs->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(&regs->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, &regs->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, &regs->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, &regs->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 2/3] dm: Arch-specific 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 ` Simon Glass
  2020-07-02 21:10 ` [RFC PATCH v2 3/3] dm: Core " Simon Glass
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2020-07-02 21:10 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Simon Glass <sjg@chromium.org>
---

(no changes since v1)

 arch/arm/dts/rk3288-u-boot.dtsi               | 17 ++++--
 arch/arm/dts/rk3288-veyron.dtsi               | 26 ++++-----
 arch/arm/dts/rk3288.dtsi                      |  3 +
 arch/arm/include/asm/arch-rockchip/clock.h    |  9 +++
 .../include/asm/arch-rockchip/sdram_rk3288.h  | 21 +++++++
 arch/arm/include/asm/arch-rockchip/spi.h      |  1 +
 arch/arm/include/asm/io.h                     | 18 ++++++
 arch/arm/mach-rockchip/rk3288/clk_rk3288.c    | 46 +++++++++++++++
 arch/arm/mach-rockchip/rk3288/rk3288.c        |  2 +
 arch/arm/mach-rockchip/rk3288/syscon_rk3288.c | 45 ++++-----------
 arch/arm/mach-rockchip/sdram.c                | 33 ++++++++---
 arch/sandbox/cpu/u-boot-spl.lds               | 12 ++++
 arch/sandbox/dts/sandbox.dts                  |  3 +-
 arch/sandbox/dts/sandbox.dtsi                 |  2 +-
 arch/x86/cpu/apollolake/cpu_spl.c             |  5 +-
 arch/x86/cpu/apollolake/uart.c                | 56 +++++++++++++++++++
 arch/x86/dts/chromebook_coral.dts             |  1 +
 arch/x86/lib/tpl.c                            |  4 +-
 configs/chromebook_coral_defconfig            |  1 +
 configs/chromebook_jerry_defconfig            | 11 ++++
 configs/rock2_defconfig                       |  3 +
 21 files changed, 247 insertions(+), 72 deletions(-)

diff --git a/arch/arm/dts/rk3288-u-boot.dtsi b/arch/arm/dts/rk3288-u-boot.dtsi
index e4e762aabf..d120eed884 100644
--- a/arch/arm/dts/rk3288-u-boot.dtsi
+++ b/arch/arm/dts/rk3288-u-boot.dtsi
@@ -6,6 +6,11 @@
 #include "rockchip-u-boot.dtsi"
 
 / {
+	aliases {
+		ram0 = &dmc;
+		clk0 = &cru;
+	};
+
 	chosen {
 		u-boot,spl-boot-order = \
 			"same-as-spl", &emmc, &sdmmc;
@@ -50,6 +55,10 @@
 
 &cru {
 	u-boot,dm-pre-reloc;
+
+	sysreset {
+		u-boot,dm-pre-reloc;
+	};
 };
 
 &grf {
@@ -57,17 +66,13 @@
 };
 
 &vopb {
-	u-boot,dm-pre-reloc;
+	u-boot,dm-pre-proper;
 };
 
 &vopl {
-	u-boot,dm-pre-reloc;
+	u-boot,dm-pre-proper;
 };
 
 &noc {
 	u-boot,dm-pre-reloc;
 };
-
-&gpio7 {
-	u-boot,dm-pre-reloc;
-};
diff --git a/arch/arm/dts/rk3288-veyron.dtsi b/arch/arm/dts/rk3288-veyron.dtsi
index 8754043b9b..9b10eaf1f5 100644
--- a/arch/arm/dts/rk3288-veyron.dtsi
+++ b/arch/arm/dts/rk3288-veyron.dtsi
@@ -10,6 +10,15 @@
 #include "rk3288.dtsi"
 
 / {
+	aliases {
+		spi_flash0 = &spi_flash;
+		sysreset0 = &sysreset;
+		syscon0 = &grf;
+		syscon1 = &sgrf;
+		syscon2 = &pmu;
+		syscon3 = &noc;
+	};
+
 	memory {
 		reg = <0x0 0x80000000>;
 	};
@@ -303,7 +312,7 @@
 
 	spi_flash: spiflash at 0 {
 		u-boot,dm-pre-reloc;
-		compatible = "spidev", "jedec,spi-nor";
+		compatible = "jedec,spi-nor";
 		spi-max-frequency = <20000000>; /* Reduce for Dediprog em100 pro */
 		reg = <0>;
 	};
@@ -315,7 +324,6 @@
 	clock-frequency = <400000>;
 	i2c-scl-falling-time-ns = <50>;		/* 2.5ns measured */
 	i2c-scl-rising-time-ns = <100>;		/* 45ns measured */
-	u-boot,dm-pre-reloc;
 
 	rk808: pmic at 1b {
 		compatible = "rockchip,rk808";
@@ -328,7 +336,6 @@
 		rockchip,system-power-controller;
 		wakeup-source;
 		#clock-cells = <1>;
-		u-boot,dm-pre-reloc;
 
 		vcc1-supply = <&vcc33_sys>;
 		vcc2-supply = <&vcc33_sys>;
@@ -601,7 +608,6 @@
 };
 
 &pinctrl {
-	u-boot,dm-pre-reloc;
 	pinctrl-names = "default", "sleep";
 	pinctrl-0 = <
 		/* Common for sleep and wake, but no owners */
@@ -826,15 +832,3 @@
 	assigned-clocks = <&cru SCLK_USBPHY480M_SRC>;
 	assigned-clock-parents = <&cru SCLK_OTGPHY0>;
 };
-
-&sdmmc {
-	u-boot,dm-pre-reloc;
-};
-
-&gpio3 {
-	u-boot,dm-pre-reloc;
-};
-
-&gpio8 {
-	u-boot,dm-pre-reloc;
-};
diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi
index 866fc08215..3983b746f4 100644
--- a/arch/arm/dts/rk3288.dtsi
+++ b/arch/arm/dts/rk3288.dtsi
@@ -608,6 +608,9 @@
 				       <150000000>, <75000000>,
 				       <300000000>, <150000000>,
 				       <75000000>;
+		sysreset: sysreset {
+			compatible = "rockchip,sysreset";
+		};
 	};
 
 	grf: syscon at ff770000 {
diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index 22de0aef8d..772d1423ce 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -6,6 +6,9 @@
 #ifndef _ASM_ARCH_CLOCK_H
 #define _ASM_ARCH_CLOCK_H
 
+/* Include this so that struct syscon_uc_info is available for dt-platdata */
+#include <syscon.h>
+
 /* define pll mode */
 #define RKCLK_PLL_MODE_SLOW		0
 #define RKCLK_PLL_MODE_NORMAL		1
@@ -156,10 +159,16 @@ void *rockchip_get_pmucru(void);
 struct rockchip_cru;
 struct rk3288_grf;
 
+int rockchip_cru_setup_sysreset(struct udevice *dev);
+
+int rockchip_cru_setup_tiny_sysreset(struct tinydev *tdev);
+
 void rk3288_clk_configure_cpu(struct rockchip_cru *cru, struct rk3288_grf *grf);
 
 int rockchip_get_clk(struct udevice **devp);
 
+struct tinydev *tiny_rockchip_get_clk(void);
+
 /*
  * rockchip_reset_bind() - Bind soft reset device as child of clock device
  *
diff --git a/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h b/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h
index 9220763fa7..3358584580 100644
--- a/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h
+++ b/arch/arm/include/asm/arch-rockchip/sdram_rk3288.h
@@ -8,6 +8,9 @@
 #ifndef _ASM_ARCH_RK3288_SDRAM_H__
 #define _ASM_ARCH_RK3288_SDRAM_H__
 
+#include <clk.h>
+#include <ram.h>
+
 struct rk3288_sdram_channel {
 	/*
 	 * bit width in address, eg:
@@ -99,4 +102,22 @@ struct rk3288_base_params {
 	u32 odt;
 };
 
+struct rk_chan_info {
+	struct rk3288_ddr_pctl *pctl;
+	struct rk3288_ddr_publ *publ;
+	struct rk3288_msch *msch;
+};
+
+struct rk_dram_info {
+	struct rk_chan_info chan[2];
+	struct ram_info info;
+	struct clk ddr_clk;
+	struct tiny_clk tiny_ddr_clk;
+	struct rockchip_cru *cru;
+	struct rk3288_grf *grf;
+	struct rk3288_sgrf *sgrf;
+	struct rk3288_pmu *pmu;
+	bool is_veyron;
+};
+
 #endif
diff --git a/arch/arm/include/asm/arch-rockchip/spi.h b/arch/arm/include/asm/arch-rockchip/spi.h
index a9d210397a..594b0967b5 100644
--- a/arch/arm/include/asm/arch-rockchip/spi.h
+++ b/arch/arm/include/asm/arch-rockchip/spi.h
@@ -12,6 +12,7 @@
 struct rockchip_spi_priv {
 	struct rockchip_spi *regs;
 	struct clk clk;
+	struct tiny_clk tiny_clk;
 	unsigned int max_freq;
 	unsigned int mode;
 	ulong last_transaction_us;	/* Time of last transaction end */
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 8959749ad6..d26aedd812 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -470,6 +470,24 @@ out:
 #define isa_check_signature(io,sig,len)	(0)
 
 #endif	/* __mem_isa */
+
+/*
+ * Provide dummy I/O access on ARM, to allow portable code to compile (e.g. the
+ * ns16550 driver). These functions do nothing and should be guarded by a CONFIG
+ * on ARM so that they are never executed.
+ */
+#define outb(val, port) ({ \
+	__maybe_unused ulong _val = (val); \
+	__maybe_unused ulong _port = (uintptr_t)(port); \
+})
+
+#define outw(val, port) outb(val, port)
+#define outl(val, port) outb(val, port)
+
+#define inb(port) ({ __maybe_unused ulong _port = (uintptr_t)(port); 0; })
+#define inw(port) inb(port)
+#define inl(port) inb(port)
+
 #endif	/* __KERNEL__ */
 
 #include <asm-generic/io.h>
diff --git a/arch/arm/mach-rockchip/rk3288/clk_rk3288.c b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c
index e05bd06a8d..144dfe7256 100644
--- a/arch/arm/mach-rockchip/rk3288/clk_rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/clk_rk3288.c
@@ -11,6 +11,17 @@
 #include <asm/arch-rockchip/cru.h>
 #include <linux/err.h>
 
+static int rockchip_sysreset_probe_(struct sysreset_reg *priv)
+{
+	priv->glb_srst_fst_value = offsetof(struct rockchip_cru,
+					    cru_glb_srst_fst_value);
+	priv->glb_srst_snd_value = offsetof(struct rockchip_cru,
+					    cru_glb_srst_snd_value);
+
+	return 0;
+}
+
+#if !CONFIG_IS_ENABLED(TINY_CLK)
 int rockchip_get_clk(struct udevice **devp)
 {
 	return uclass_get_device_by_driver(UCLASS_CLK,
@@ -31,3 +42,38 @@ void *rockchip_get_cru(void)
 
 	return priv->cru;
 }
+
+int rockchip_cru_setup_sysreset(struct udevice *dev)
+{
+	struct sysreset_reg *priv = dev_get_priv(dev);
+
+	return rockchip_sysreset_probe_(priv);
+}
+
+#else /* TINY_CLK */
+
+struct tinydev *tiny_rockchip_get_clk(void)
+{
+	return tiny_dev_get(UCLASS_CLK, 0);
+}
+
+void *rockchip_get_cru(void)
+{
+	struct rk3288_clk_priv *priv;
+	struct tinydev *tdev;
+
+	tdev = tiny_rockchip_get_clk();
+	if (!tdev)
+		return NULL;
+	priv = tdev->priv;
+
+	return priv->cru;
+}
+
+int rockchip_cru_setup_tiny_sysreset(struct tinydev *tdev)
+{
+	struct sysreset_reg *priv = tinydev_get_priv(tdev);
+
+	return rockchip_sysreset_probe_(priv);
+}
+#endif
diff --git a/arch/arm/mach-rockchip/rk3288/rk3288.c b/arch/arm/mach-rockchip/rk3288/rk3288.c
index 804abe8a1b..1e7baeea45 100644
--- a/arch/arm/mach-rockchip/rk3288/rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/rk3288.c
@@ -115,6 +115,7 @@ int rk_board_late_init(void)
 	return rk3288_board_late_init();
 }
 
+#if !CONFIG_IS_ENABLED(TINY_CLK)
 static int do_clock(struct cmd_tbl *cmdtp, int flag, int argc,
 		    char *const argv[])
 {
@@ -165,3 +166,4 @@ U_BOOT_CMD(
 	"display information about clocks",
 	""
 );
+#endif
diff --git a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c
index e3da0a0194..52697e9cac 100644
--- a/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/syscon_rk3288.c
@@ -10,6 +10,7 @@
 #include <syscon.h>
 #include <asm/arch-rockchip/clock.h>
 
+#if !CONFIG_IS_ENABLED(TINY_SYSCON)
 static const struct udevice_id rk3288_syscon_ids[] = {
 	{ .compatible = "rockchip,rk3288-noc", .data = ROCKCHIP_SYSCON_NOC },
 	{ .compatible = "rockchip,rk3288-grf", .data = ROCKCHIP_SYSCON_GRF },
@@ -24,40 +25,18 @@ U_BOOT_DRIVER(syscon_rk3288) = {
 	.of_match = rk3288_syscon_ids,
 };
 
-#if CONFIG_IS_ENABLED(OF_PLATDATA)
-static int rk3288_syscon_bind_of_platdata(struct udevice *dev)
-{
-	dev->driver_data = dev->driver->of_match->data;
-	debug("syscon: %s %d\n", dev->name, (uint)dev->driver_data);
+U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_noc)
+U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_pmu)
+U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_grf)
+U_BOOT_DRIVER_ALIAS(syscon_rk3288, rockchip_rk3288_sgrf)
 
-	return 0;
-}
+#else
 
-U_BOOT_DRIVER(rockchip_rk3288_noc) = {
-	.name = "rockchip_rk3288_noc",
-	.id = UCLASS_SYSCON,
-	.of_match = rk3288_syscon_ids,
-	.bind = rk3288_syscon_bind_of_platdata,
-};
-
-U_BOOT_DRIVER(rockchip_rk3288_grf) = {
-	.name = "rockchip_rk3288_grf",
-	.id = UCLASS_SYSCON,
-	.of_match = rk3288_syscon_ids + 1,
-	.bind = rk3288_syscon_bind_of_platdata,
-};
-
-U_BOOT_DRIVER(rockchip_rk3288_sgrf) = {
-	.name = "rockchip_rk3288_sgrf",
-	.id = UCLASS_SYSCON,
-	.of_match = rk3288_syscon_ids + 2,
-	.bind = rk3288_syscon_bind_of_platdata,
-};
-
-U_BOOT_DRIVER(rockchip_rk3288_pmu) = {
-	.name = "rockchip_rk3288_pmu",
-	.id = UCLASS_SYSCON,
-	.of_match = rk3288_syscon_ids + 3,
-	.bind = rk3288_syscon_bind_of_platdata,
+U_BOOT_TINY_DRIVER(syscon_rk3288) = {
+	.uclass_id	= UCLASS_SYSCON,
+	.probe		= tiny_syscon_setup,
+// 	.ops		= &rockchip_clk_tiny_ops,
+	DM_TINY_PRIV(<asm/arch-rockchip/clock.h>, \
+		     sizeof(struct syscon_uc_info))
 };
 #endif
diff --git a/arch/arm/mach-rockchip/sdram.c b/arch/arm/mach-rockchip/sdram.c
index 24fe6cc8f0..e4c6644a0f 100644
--- a/arch/arm/mach-rockchip/sdram.c
+++ b/arch/arm/mach-rockchip/sdram.c
@@ -189,15 +189,30 @@ int dram_init(void)
 	struct udevice *dev;
 	int ret;
 
-	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
-	if (ret) {
-		debug("DRAM init failed: %d\n", ret);
-		return ret;
-	}
-	ret = ram_get_info(dev, &ram);
-	if (ret) {
-		debug("Cannot get DRAM size: %d\n", ret);
-		return ret;
+	if (!CONFIG_IS_ENABLED(TINY_RAM)) {
+		ret = uclass_get_device(UCLASS_RAM, 0, &dev);
+		if (ret) {
+			debug("DRAM init failed: %d\n", ret);
+			return log_msg_ret("get", ret);
+		}
+		ret = ram_get_info(dev, &ram);
+		if (ret) {
+			debug("Cannot get DRAM size: %d\n", ret);
+			return log_msg_ret("info", ret);
+		}
+	} else {
+		struct tinydev *dev;
+
+		dev = tiny_dev_get(UCLASS_RAM, 0);
+		if (!dev) {
+			debug("DRAM init failed\n");
+			return log_msg_ret("get", -ENOKEY);
+		}
+		ret = tiny_ram_get_info(dev, &ram);
+		if (ret) {
+			debug("Cannot get DRAM size: %d\n", ret);
+			return log_msg_ret("info", ret);
+		}
 	}
 	gd->ram_size = ram.size;
 	debug("SDRAM base=%lx, size=%lx\n",
diff --git a/arch/sandbox/cpu/u-boot-spl.lds b/arch/sandbox/cpu/u-boot-spl.lds
index c60eb109b1..c2e3e65721 100644
--- a/arch/sandbox/cpu/u-boot-spl.lds
+++ b/arch/sandbox/cpu/u-boot-spl.lds
@@ -5,9 +5,21 @@
  * found in the LICENSE file.
  */
 
+/* Put the tiny devices in the data section since they are changed at runtime */
 SECTIONS
 {
+	. = ALIGN(4);
+	.tiny_dev : {
+		__tiny_dev_start = .;
+		KEEP(*(SORT(.u_boot_list*tiny_dev*)));
+		__tiny_dev_end = .;
+	}
+}
 
+INSERT AFTER .data;
+
+SECTIONS
+{
 	. = ALIGN(4);
 	.u_boot_list : {
 		KEEP(*(SORT(.u_boot_list*)));
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index 20f6893829..5480c69ebe 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -9,10 +9,11 @@
 	compatible = "sandbox";
 
 	aliases {
+		axi0 = &axi;
 		i2c0 = &i2c_0;
 		pci0 = &pcic;
 		rtc0 = &rtc_0;
-		axi0 = &axi;
+		serial0 = &serial0;
 		spi0 = &spi;
 	};
 
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi
index e1f68cd552..99a6b6328c 100644
--- a/arch/sandbox/dts/sandbox.dtsi
+++ b/arch/sandbox/dts/sandbox.dtsi
@@ -259,7 +259,7 @@
 	};
 
 	/* Needs to be available prior to relocation */
-	uart0: serial {
+	serial0: serial {
 		u-boot,dm-spl;
 		compatible = "sandbox,serial";
 		sandbox,text-colour = "cyan";
diff --git a/arch/x86/cpu/apollolake/cpu_spl.c b/arch/x86/cpu/apollolake/cpu_spl.c
index 707ceb3e64..0f32ca7dc5 100644
--- a/arch/x86/cpu/apollolake/cpu_spl.c
+++ b/arch/x86/cpu/apollolake/cpu_spl.c
@@ -171,7 +171,7 @@ static void early_ec_init(void)
 
 static int arch_cpu_init_tpl(void)
 {
-	struct udevice *pmc, *sa, *p2sb, *serial, *spi, *lpc;
+	struct udevice *pmc, *sa, *p2sb, *spi, *lpc;
 	int ret;
 
 	ret = uclass_first_device_err(UCLASS_ACPI_PMC, &pmc);
@@ -192,9 +192,6 @@ static int arch_cpu_init_tpl(void)
 	if (ret)
 		return log_msg_ret("northbridge", ret);
 	gd->baudrate = CONFIG_BAUDRATE;
-	ret = uclass_first_device_err(UCLASS_SERIAL, &serial);
-	if (ret)
-		return log_msg_ret("serial", ret);
 	if (CONFIG_IS_ENABLED(SPI_FLASH_SUPPORT)) {
 		ret = uclass_first_device_err(UCLASS_SPI, &spi);
 		if (ret)
diff --git a/arch/x86/cpu/apollolake/uart.c b/arch/x86/cpu/apollolake/uart.c
index f368f7d2db..3ab3fc9d4d 100644
--- a/arch/x86/cpu/apollolake/uart.c
+++ b/arch/x86/cpu/apollolake/uart.c
@@ -8,10 +8,12 @@
  */
 
 #include <common.h>
+#include <debug_uart.h>
 #include <dm.h>
 #include <dt-structs.h>
 #include <malloc.h>
 #include <ns16550.h>
+#include <serial.h>
 #include <spl.h>
 #include <asm/io.h>
 #include <asm/pci.h>
@@ -64,6 +66,7 @@ void apl_uart_init(pci_dev_t bdf, ulong base)
 	uart_lpss_init((void *)base);
 }
 
+#if !CONFIG_IS_ENABLED(TINY_SERIAL)
 /*
  * This driver uses its own compatible string but almost everything else from
  * the standard ns16550 driver. This allows us to provide an of-platdata
@@ -132,3 +135,56 @@ U_BOOT_DRIVER(apl_ns16550) = {
 	.ofdata_to_platdata = apl_ns16550_ofdata_to_platdata,
 	.probe = apl_ns16550_probe,
 };
+
+#else /* TINY_SERIAL */
+
+static int apl_ns16550_tiny_probe(struct tinydev *tdev)
+{
+	struct dtd_intel_apl_ns16550 *dtplat = tdev->dtplat;
+	struct ns16550_platdata *plat = tdev->priv;
+	ulong base;
+	pci_dev_t bdf;
+
+	base = dtplat->early_regs[0];
+	bdf = pci_ofplat_get_devfn(dtplat->reg[0]);
+
+	if (!CONFIG_IS_ENABLED(PCI))
+		apl_uart_init(bdf, base);
+
+	plat->base = base;
+	plat->reg_shift = dtplat->reg_shift;
+	plat->reg_width = 1;
+	plat->clock = dtplat->clock_frequency;
+	plat->fcr = UART_FCR_DEFVAL;
+
+	return ns16550_tiny_probe_plat(plat);
+}
+
+static int apl_ns16550_tiny_setbrg(struct tinydev *tdev, int baudrate)
+{
+	struct ns16550_platdata *plat = tdev->priv;
+
+	return ns16550_tiny_setbrg(plat, baudrate);
+}
+
+static int apl_ns16550_tiny_putc(struct tinydev *tdev, const char ch)
+{
+	struct ns16550_platdata *plat = tdev->priv;
+
+	return ns16550_tiny_putc(plat, ch);
+}
+
+struct tiny_serial_ops apl_ns16550_tiny_ops = {
+	.probe	= apl_ns16550_tiny_probe,
+	.setbrg	= apl_ns16550_tiny_setbrg,
+	.putc	= apl_ns16550_tiny_putc,
+};
+
+U_BOOT_TINY_DRIVER(apl_ns16550) = {
+	.uclass_id	= UCLASS_SERIAL,
+	.probe		= apl_ns16550_tiny_probe,
+	.ops		= &apl_ns16550_tiny_ops,
+	DM_TINY_PRIV(<ns16550.h>, sizeof(struct ns16550_platdata))
+};
+
+#endif
diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts
index 965d9f387d..f1a1f98c08 100644
--- a/arch/x86/dts/chromebook_coral.dts
+++ b/arch/x86/dts/chromebook_coral.dts
@@ -40,6 +40,7 @@
 		i2c5 = &i2c_5;
 		i2c6 = &i2c_6;
 		i2c7 = &i2c_7;
+		serial0 = &serial;
 	};
 
 	config {
diff --git a/arch/x86/lib/tpl.c b/arch/x86/lib/tpl.c
index 6f7eb43a17..139de881e8 100644
--- a/arch/x86/lib/tpl.c
+++ b/arch/x86/lib/tpl.c
@@ -41,7 +41,7 @@ static int x86_tpl_init(void)
 	ret = arch_cpu_init();
 	if (ret) {
 		debug("%s: arch_cpu_init() failed\n", __func__);
-		return ret;
+		return log_msg_ret("arch", ret);
 	}
 	ret = arch_cpu_init_dm();
 	if (ret) {
@@ -59,7 +59,7 @@ void board_init_f(ulong flags)
 
 	ret = x86_tpl_init();
 	if (ret) {
-		debug("Error %d\n", ret);
+		printf("Error %d\n", ret);
 		panic("x86_tpl_init fail");
 	}
 
diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig
index 79f9b5a232..af8b7a50e8 100644
--- a/configs/chromebook_coral_defconfig
+++ b/configs/chromebook_coral_defconfig
@@ -102,3 +102,4 @@ CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
 # CONFIG_GZIP is not set
 # CONFIG_EFI_LOADER is not set
+CONFIG_NS16550_DYNAMIC=y
diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig
index 96246b7512..3a44543bc5 100644
--- a/configs/chromebook_jerry_defconfig
+++ b/configs/chromebook_jerry_defconfig
@@ -107,3 +107,14 @@ CONFIG_DISPLAY_ROCKCHIP_HDMI=y
 CONFIG_SPL_TINY_MEMSET=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_ERRNO_STR=y
+CONFIG_NS16550_DYNAMIC=y
+CONFIG_SPL_TINY_ONLY=y
+# CONFIG_TINY_CHECK is not set
+CONFIG_SPL_TINY_RAM=y
+CONFIG_SPL_TINY_SERIAL=y
+CONFIG_SPL_TINY_CLK=y
+# CONFIG_SPL_CLK_FIXED_RATE is not set
+CONFIG_SPL_TINY_SPI=y
+CONFIG_SPL_TINY_SPI_FLASH=y
+CONFIG_SPL_TINY_SYSRESET=y
+CONFIG_SPL_TINY_SYSCON=y
diff --git a/configs/rock2_defconfig b/configs/rock2_defconfig
index 2118402bbe..a458935422 100644
--- a/configs/rock2_defconfig
+++ b/configs/rock2_defconfig
@@ -83,3 +83,6 @@ CONFIG_DISPLAY_ROCKCHIP_HDMI=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_ERRNO_STR=y
+#CONFIG_SPL_TINY_SERIAL=y
+#CONFIG_SPL_TINY=y
+CONFIG_SPL_OF_PLATDATA=y
-- 
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 +
 drivers/core/of_extra.c         |  49 ++++-
 drivers/core/regmap.c           |   3 +-
 drivers/core/syscon-uclass.c    |  68 +++++--
 drivers/core/tiny.c             | 249 +++++++++++++++++++++++++
 include/dm/device.h             | 121 ++++++++++++
 include/dm/of_extra.h           |   6 +
 include/dm/platdata.h           |  20 +-
 include/dm/tiny_struct.h        |  42 +++++
 include/linker_lists.h          |   6 +
 scripts/Makefile.spl            |   6 +-
 tools/dtoc/dtb_platdata.py      | 316 +++++++++++++++++++++++++++-----
 tools/dtoc/dtoc_test_simple.dts |  12 +-
 tools/dtoc/fdt.py               |   7 +-
 tools/dtoc/main.py              |   9 +-
 tools/dtoc/test_dtoc.py         |  91 ++++++++-
 tools/patman/tools.py           |   4 +-
 26 files changed, 1525 insertions(+), 123 deletions(-)
 create mode 100644 doc/develop/debugging.rst
 create mode 100644 doc/driver-model/tiny-dm.rst
 create mode 100644 drivers/core/tiny.c
 create mode 100644 include/dm/tiny_struct.h

diff --git a/board/Synology/ds109/ds109.c b/board/Synology/ds109/ds109.c
index aa2987d924..d5d7396ad3 100644
--- a/board/Synology/ds109/ds109.c
+++ b/board/Synology/ds109/ds109.c
@@ -106,8 +106,7 @@ void reset_misc(void)
 	printf("Synology reset...");
 	udelay(50000);
 
-	b_d = ns16550_calc_divisor((NS16550_t)CONFIG_SYS_NS16550_COM2,
-		CONFIG_SYS_NS16550_CLK, 9600);
+	b_d = ns16550_calc_divisor(CONFIG_SYS_NS16550_CLK, 9600);
 	NS16550_init((NS16550_t)CONFIG_SYS_NS16550_COM2, b_d);
 	NS16550_putc((NS16550_t)CONFIG_SYS_NS16550_COM2, SOFTWARE_REBOOT);
 }
diff --git a/common/console.c b/common/console.c
index f149624954..466e45ae1b 100644
--- a/common/console.c
+++ b/common/console.c
@@ -259,7 +259,7 @@ static inline void console_doenv(int file, struct stdio_dev *dev)
 	iomux_doenv(file, dev->name);
 }
 #endif
-#else
+#else  /* !CONSOLE_MUX */
 static inline int console_getc(int file)
 {
 	return stdio_devices[file]->getc(stdio_devices[file]);
diff --git a/common/log.c b/common/log.c
index f44f15743f..abe6fe7096 100644
--- a/common/log.c
+++ b/common/log.c
@@ -45,11 +45,13 @@ const char *log_get_cat_name(enum log_category_t cat)
 	if (cat >= LOGC_NONE)
 		return log_cat_name[cat - LOGC_NONE];
 
-#if CONFIG_IS_ENABLED(DM)
-	name = uclass_get_name((enum uclass_id)cat);
-#else
-	name = NULL;
-#endif
+	/* With tiny-dm we don't have uclasses, so cannot get the name */
+	if (CONFIG_IS_ENABLED(TINY_ONLY))
+		return "tiny";
+	if (CONFIG_IS_ENABLED(DM))
+		name = uclass_get_name((enum uclass_id)cat);
+	else
+		name = NULL;
 
 	return name ? name : "<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
diff --git a/drivers/core/of_extra.c b/drivers/core/of_extra.c
index 6420e6ec44..b93b7e43c8 100644
--- a/drivers/core/of_extra.c
+++ b/drivers/core/of_extra.c
@@ -79,11 +79,9 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type,
 	ofnode node;
 
 	if (!ofnode_valid(config_node)) {
-		config_node = ofnode_path("/config");
-		if (!ofnode_valid(config_node)) {
-			debug("%s: Cannot find /config node\n", __func__);
+		config_node = ofnode_get_config_node();
+		if (!ofnode_valid(config_node))
 			return -ENOENT;
-		}
 	}
 	if (!suffix)
 		suffix = "";
@@ -127,3 +125,46 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type,
 
 	return 0;
 }
+
+ofnode ofnode_get_config_node(void)
+{
+	ofnode node;
+
+	node = ofnode_path("/config");
+	if (!ofnode_valid(node))
+		debug("%s: Cannot find /config node\n", __func__);
+
+	return node;
+}
+
+int ofnode_read_config_int(const char *prop_name, int default_val)
+{
+	ofnode node;
+
+	log_debug("%s\n", prop_name);
+	node = ofnode_get_config_node();
+	if (!ofnode_valid(node))
+		return default_val;
+
+	return ofnode_read_u32_default(node, prop_name, default_val);
+}
+
+int ofnode_read_config_bool(const char *prop_name)
+{
+	ofnode node;
+
+	log_debug("%s\n", prop_name);
+	node = ofnode_get_config_node();
+
+	return ofnode_read_bool(node, prop_name);
+}
+
+const char *ofnode_read_config_string(const char *prop_name)
+{
+	ofnode node;
+
+	log_debug("%s\n", prop_name);
+	node = ofnode_get_config_node();
+
+	return ofnode_read_string(node, prop_name);
+}
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c
index a67a237b88..4c33234d1d 100644
--- a/drivers/core/regmap.c
+++ b/drivers/core/regmap.c
@@ -37,8 +37,7 @@ static struct regmap *regmap_alloc(int count)
 }
 
 #if CONFIG_IS_ENABLED(OF_PLATDATA)
-int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
-			     struct regmap **mapp)
+int regmap_init_mem_platdata(fdt_val_t *reg, int count, struct regmap **mapp)
 {
 	struct regmap_range *range;
 	struct regmap *map;
diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c
index b5cd763b6b..ec91964a32 100644
--- a/drivers/core/syscon-uclass.c
+++ b/drivers/core/syscon-uclass.c
@@ -16,6 +16,17 @@
 #include <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
  *
diff --git a/include/dm/of_extra.h b/include/dm/of_extra.h
index ca15df21b0..ccc58b228b 100644
--- a/include/dm/of_extra.h
+++ b/include/dm/of_extra.h
@@ -86,4 +86,10 @@ int ofnode_decode_memory_region(ofnode config_node, const char *mem_type,
 				const char *suffix, fdt_addr_t *basep,
 				fdt_size_t *sizep);
 
+ofnode ofnode_get_config_node(void);
+
+int ofnode_read_config_int(const char *prop_name, int default_val);
+int ofnode_read_config_bool(const char *prop_name);
+const char *ofnode_read_config_string(const char *prop_name);
+
 #endif
diff --git a/include/dm/platdata.h b/include/dm/platdata.h
index cab93b071b..f75f7d9cee 100644
--- a/include/dm/platdata.h
+++ b/include/dm/platdata.h
@@ -45,7 +45,22 @@ struct driver_info {
 #define U_BOOT_DEVICES(__name)						\
 	ll_entry_declare_list(struct driver_info, __name, driver_info)
 
-/* Get a pointer to a given driver */
+/**
+ * Get a pointer to a given device info given its name
+ *
+ * With the declaration U_BOOT_DEVICE(name), DM_GET_DEVICE(name) will return a
+ * pointer to the struct driver_info created by that declaration.
+ *
+ * if OF_PLATDATA is enabled, from this it is possible to use the @dev member of
+ * struct driver_info to find the device pointer itself.
+ *
+ * TODO(sjg at chromium.org): U_BOOT_DEVICE() tells U-Boot to create a device, so
+ * the naming seems sensible, but DM_GET_DEVICE() is a bit of misnomer, since it
+ * finds the driver_info record, not the device.
+ *
+ * @__name: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000)
+ * @return struct driver_info * to the driver that created the device
+ */
 #define DM_GET_DEVICE(__name)						\
 	ll_entry_get(struct driver_info, __name, driver_info)
 
@@ -57,4 +72,7 @@ struct driver_info {
  * by dtoc when parsing dtb.
  */
 void dm_populate_phandle_data(void);
+
+#define IF_OF_PLATDATA(x) CONFIG_IS_ENABLED(OF_PLATDATA, (x))
+
 #endif
diff --git a/include/dm/tiny_struct.h b/include/dm/tiny_struct.h
new file mode 100644
index 0000000000..3de52d83ec
--- /dev/null
+++ b/include/dm/tiny_struct.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Structures for inclusion in global_data
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <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

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-02 21:10 [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Simon Glass
                   ` (2 preceding siblings ...)
  2020-07-02 21:10 ` [RFC PATCH v2 3/3] dm: Core " Simon Glass
@ 2020-07-03  2:19 ` Heinrich Schuchardt
  2020-07-03  3:03   ` Simon Glass
  2020-07-10  4:12 ` Walter Lozano
  4 siblings, 1 reply; 11+ messages in thread
From: Heinrich Schuchardt @ 2020-07-03  2:19 UTC (permalink / raw)
  To: u-boot



Am 2. Juli 2020 23:10:01 MESZ schrieb Simon Glass <sjg@chromium.org>:
>This series provides a proposed enhancement to driver model to reduce
>overhead in SPL.
>
>These patches should not be reviewed other than to comment on the
>approach. The code is all lumped together in a few patches and so
>cannot
>be applied as is.
>
>For now, the source tree is available at:
>
> https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
>
>Comments welcome!
>
>Benefits (good news)
>--------------------
>
>As an example of the impact of tiny-dm, chromebook_jerry is converted
>to
>use it. This shows approximately a 30% reduction in code and data size
>and
>a 85% reduction in malloc() space over of-platdata:
>
>   text	   data	    bss	    dec	    hex	filename
>25248	   1836	     12	  27096	   69d8	spl/u-boot-spl (original with DT)
>  19727	   3436	     12	  23175	   5a87	spl/u-boot-spl (OF_PLATDATA)
>    78%    187%    100%     86%         as %age of original
>
>  13784	   1408	     12	  15204	   3b64	spl/u-boot-spl (SPL_TINY)
>    70%     41%    100%     66%         as %age of platdata
>    55%     77%    100%     56%         as %age of original
>
>SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116
>(SPL_TINY).
>
>Overall the 'overhead' of tiny-dm is much less than the full driver
>model. Code size is currently about 600 bytes for these functions on
>Thumb2:
>
>   00000054 T tiny_dev_probe
>   00000034 T tiny_dev_get_by_drvdata
>   00000024 T tiny_dev_find
>   0000001a T tiny_dev_get
>   0000003c T tinydev_alloc_data
>   0000002a t tinydev_lookup_data
>   00000022 T tinydev_ensure_data
>   00000014 T tinydev_get_data
>   00000004 T tinydev_get_parent
>
>Effort (bad news)
>-----------------
>
>Unfortunately it is quite a bit of work to convert drivers over to
>tiny-dm. First, the of-platdata conversion must be done. But on top of
>that, tiny-dm needs entirely separate code for dealing with devices.
>This
>means that instead of 'struct udevice' and 'struct uclass' there is
>just
>'struct tinydev'. Each driver and uclass must be modified to support
>both, pulling common code into internal static functions.


The code size reductions sound quite impressive. 

Somehow I have missed the start of your investigation. Could you, please, summarize what the main differences between tiny DM and the current DM are and explain why tiny DM cannot be used in main U-Boot.

Best regards

Heinrich

>
>Another option
>--------------
>
>Note: It is assumed that any board that is space-contrained should use
>of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
>reduce device-tree overhead by approximately 4KB.
>
>Designing tiny-dm has suggested a number of things that could be
>changed
>in the current driver model to make it more space-efficient for TPL and
>SPL. The ones with least impact on driver code are (CS=reduces code
>size,
>DS=reduces data size):
>
>   CS - drop driver_bind() and create devices (struct udevice) at
>        build-time
>   CS - allocate all device- and uclass-private data at build-time
>   CS - remove all but the basic operations for each uclass (e.g. SPI
>        flash only supports reading)
>   DS - use 8-bit indexes instead of 32/64-bit pointers for device
>        pointers possible since these are created at build-time)
>   DS - use singly-linked lists
> DS - use 16-bit offsets to private data, instead of 32/64-bit pointers
>        (possible since it is all in SRAM relative to malloc() base,
>        presumably word-aligned and < 256KB)
>DS - move private pointers into a separate data structure so that NULLs
>        are not stored
>   CS / DS - Combine req_seq and seq and calculate the new value at
>        build-time
>
>More difficult are:
>
>   DS - drop some of the lesser-used driver and uclass methods
>   DS - drop all uclass methods except init()
>   DS - drop all driver methods except probe()
>   CS / DS - drop uclasses and require drivers to manually call uclass
>        functions
>
>Even with all of this we would not reach tiny-dm and it would muddy up
>the
>driver-model datas structures. But we might come close to tiny-dm on
>size
>and there are some advantages:
>
>- much of the benefit would apply to all boards that use of-platdata
>(i.e.
>  with very little effort on behalf of board maintainers)
>- the impact on the code base is much less (we keep a single, unified
>  driver mode in SPL and U-Boot proper)
>
>Overall I think it is worth looking at this option. While it doesn't
>have
>the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
>driver code as much and it is easier to learn.
>
>Changes in v2:
>- Various updates, and ported to chromebook_jerry (rockchip)
>
>Simon Glass (3):
>  dm: Driver and uclass changes for tiny-dm
>  dm: Arch-specific changes for tiny-dm
>  dm: Core changes for tiny-dm
>
> arch/arm/dts/rk3288-u-boot.dtsi               |  17 +-
> arch/arm/dts/rk3288-veyron.dtsi               |  26 +-
> arch/arm/dts/rk3288.dtsi                      |   3 +
> arch/arm/include/asm/arch-rockchip/clock.h    |   9 +
> .../include/asm/arch-rockchip/sdram_rk3288.h  |  21 ++
> arch/arm/include/asm/arch-rockchip/spi.h      |   1 +
> arch/arm/include/asm/io.h                     |  18 +
> arch/arm/mach-rockchip/rk3288/clk_rk3288.c    |  46 +++
> arch/arm/mach-rockchip/rk3288/rk3288.c        |   2 +
> arch/arm/mach-rockchip/rk3288/syscon_rk3288.c |  45 +--
> arch/arm/mach-rockchip/sdram.c                |  33 +-
> arch/sandbox/cpu/u-boot-spl.lds               |  12 +
> arch/sandbox/dts/sandbox.dts                  |   3 +-
> arch/sandbox/dts/sandbox.dtsi                 |   2 +-
> arch/x86/cpu/apollolake/cpu_spl.c             |   5 +-
> arch/x86/cpu/apollolake/uart.c                |  56 ++++
> arch/x86/dts/chromebook_coral.dts             |   1 +
> arch/x86/lib/tpl.c                            |   4 +-
> 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 +++--
> configs/chromebook_coral_defconfig            |   1 +
> configs/chromebook_jerry_defconfig            |  11 +
> configs/rock2_defconfig                       |   3 +
> doc/develop/debugging.rst                     |  35 ++
> doc/driver-model/tiny-dm.rst                  | 315 +++++++++++++++++
> drivers/clk/Kconfig                           |  54 +++
> drivers/clk/Makefile                          |   4 +-
> drivers/clk/clk-uclass.c                      |  53 ++-
> drivers/clk/rockchip/clk_rk3288.c             | 106 ++++--
> drivers/core/Kconfig                          | 106 ++++++
> drivers/core/Makefile                         |   3 +
> drivers/core/of_extra.c                       |  49 ++-
> drivers/core/regmap.c                         |   3 +-
> drivers/core/syscon-uclass.c                  |  68 +++-
> drivers/core/tiny.c                           | 249 ++++++++++++++
> 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/dm/device.h                           | 121 +++++++
> include/dm/of_extra.h                         |   6 +
> include/dm/platdata.h                         |  20 +-
> include/dm/tiny_struct.h                      |  42 +++
> include/linker_lists.h                        |   6 +
> 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 +
> 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 +-
> 92 files changed, 3454 insertions(+), 540 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

-- 
Diese Nachricht wurde von meinem Android-Ger?t mit K-9 Mail gesendet.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-03  2:19 ` [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Heinrich Schuchardt
@ 2020-07-03  3:03   ` Simon Glass
  2020-07-10  1:12     ` Simon Glass
  0 siblings, 1 reply; 11+ messages in thread
From: Simon Glass @ 2020-07-03  3:03 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

On Thu, 2 Jul 2020 at 20:24, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
>
>
> Am 2. Juli 2020 23:10:01 MESZ schrieb Simon Glass <sjg@chromium.org>:
> >This series provides a proposed enhancement to driver model to reduce
> >overhead in SPL.
> >
> >These patches should not be reviewed other than to comment on the
> >approach. The code is all lumped together in a few patches and so
> >cannot
> >be applied as is.
> >
> >For now, the source tree is available at:
> >
> > https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
> >
> >Comments welcome!
> >
> >Benefits (good news)
> >--------------------
> >
> >As an example of the impact of tiny-dm, chromebook_jerry is converted
> >to
> >use it. This shows approximately a 30% reduction in code and data size
> >and
> >a 85% reduction in malloc() space over of-platdata:
> >
> >   text           data     bss     dec     hex filename
> >25248     1836      12   27096    69d8 spl/u-boot-spl (original with DT)
> >  19727           3436      12   23175    5a87 spl/u-boot-spl (OF_PLATDATA)
> >    78%    187%    100%     86%         as %age of original
> >
> >  13784           1408      12   15204    3b64 spl/u-boot-spl (SPL_TINY)
> >    70%     41%    100%     66%         as %age of platdata
> >    55%     77%    100%     56%         as %age of original
> >
> >SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116
> >(SPL_TINY).
> >
> >Overall the 'overhead' of tiny-dm is much less than the full driver
> >model. Code size is currently about 600 bytes for these functions on
> >Thumb2:
> >
> >   00000054 T tiny_dev_probe
> >   00000034 T tiny_dev_get_by_drvdata
> >   00000024 T tiny_dev_find
> >   0000001a T tiny_dev_get
> >   0000003c T tinydev_alloc_data
> >   0000002a t tinydev_lookup_data
> >   00000022 T tinydev_ensure_data
> >   00000014 T tinydev_get_data
> >   00000004 T tinydev_get_parent
> >
> >Effort (bad news)
> >-----------------
> >
> >Unfortunately it is quite a bit of work to convert drivers over to
> >tiny-dm. First, the of-platdata conversion must be done. But on top of
> >that, tiny-dm needs entirely separate code for dealing with devices.
> >This
> >means that instead of 'struct udevice' and 'struct uclass' there is
> >just
> >'struct tinydev'. Each driver and uclass must be modified to support
> >both, pulling common code into internal static functions.
>
>
> The code size reductions sound quite impressive.
>
> Somehow I have missed the start of your investigation. Could you, please, summarize what the main differences between tiny DM and the current DM are and explain why tiny DM cannot be used in main U-Boot.

See patch 2 (doc/driver-model/tiny-dm.rst). I probably didn't send out
v1 widely enough, but it was only a starting point then.

It is quite limited and it would be fiddly to use in U-Boot proper.
Things like automatic power domains, uclass features like setting up a
bus and full devicetree access are not available with tiny DM.

Regards,
Simon

>
> Best regards
>
> Heinrich
>
> >
> >Another option
> >--------------
> >
> >Note: It is assumed that any board that is space-contrained should use
> >of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
> >reduce device-tree overhead by approximately 4KB.
> >
> >Designing tiny-dm has suggested a number of things that could be
> >changed
> >in the current driver model to make it more space-efficient for TPL and
> >SPL. The ones with least impact on driver code are (CS=reduces code
> >size,
> >DS=reduces data size):
> >
> >   CS - drop driver_bind() and create devices (struct udevice) at
> >        build-time
> >   CS - allocate all device- and uclass-private data at build-time
> >   CS - remove all but the basic operations for each uclass (e.g. SPI
> >        flash only supports reading)
> >   DS - use 8-bit indexes instead of 32/64-bit pointers for device
> >        pointers possible since these are created at build-time)
> >   DS - use singly-linked lists
> > DS - use 16-bit offsets to private data, instead of 32/64-bit pointers
> >        (possible since it is all in SRAM relative to malloc() base,
> >        presumably word-aligned and < 256KB)
> >DS - move private pointers into a separate data structure so that NULLs
> >        are not stored
> >   CS / DS - Combine req_seq and seq and calculate the new value at
> >        build-time
> >
> >More difficult are:
> >
> >   DS - drop some of the lesser-used driver and uclass methods
> >   DS - drop all uclass methods except init()
> >   DS - drop all driver methods except probe()
> >   CS / DS - drop uclasses and require drivers to manually call uclass
> >        functions
> >
> >Even with all of this we would not reach tiny-dm and it would muddy up
> >the
> >driver-model datas structures. But we might come close to tiny-dm on
> >size
> >and there are some advantages:
> >
> >- much of the benefit would apply to all boards that use of-platdata
> >(i.e.
> >  with very little effort on behalf of board maintainers)
> >- the impact on the code base is much less (we keep a single, unified
> >  driver mode in SPL and U-Boot proper)
> >
> >Overall I think it is worth looking at this option. While it doesn't
> >have
> >the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
> >driver code as much and it is easier to learn.
> >
> >Changes in v2:
> >- Various updates, and ported to chromebook_jerry (rockchip)
> >
> >Simon Glass (3):
> >  dm: Driver and uclass changes for tiny-dm
> >  dm: Arch-specific changes for tiny-dm
> >  dm: Core changes for tiny-dm
> >
> > arch/arm/dts/rk3288-u-boot.dtsi               |  17 +-
> > arch/arm/dts/rk3288-veyron.dtsi               |  26 +-
> > arch/arm/dts/rk3288.dtsi                      |   3 +
> > arch/arm/include/asm/arch-rockchip/clock.h    |   9 +
> > .../include/asm/arch-rockchip/sdram_rk3288.h  |  21 ++
> > arch/arm/include/asm/arch-rockchip/spi.h      |   1 +
> > arch/arm/include/asm/io.h                     |  18 +
> > arch/arm/mach-rockchip/rk3288/clk_rk3288.c    |  46 +++
> > arch/arm/mach-rockchip/rk3288/rk3288.c        |   2 +
> > arch/arm/mach-rockchip/rk3288/syscon_rk3288.c |  45 +--
> > arch/arm/mach-rockchip/sdram.c                |  33 +-
> > arch/sandbox/cpu/u-boot-spl.lds               |  12 +
> > arch/sandbox/dts/sandbox.dts                  |   3 +-
> > arch/sandbox/dts/sandbox.dtsi                 |   2 +-
> > arch/x86/cpu/apollolake/cpu_spl.c             |   5 +-
> > arch/x86/cpu/apollolake/uart.c                |  56 ++++
> > arch/x86/dts/chromebook_coral.dts             |   1 +
> > arch/x86/lib/tpl.c                            |   4 +-
> > 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 +++--
> > configs/chromebook_coral_defconfig            |   1 +
> > configs/chromebook_jerry_defconfig            |  11 +
> > configs/rock2_defconfig                       |   3 +
> > doc/develop/debugging.rst                     |  35 ++
> > doc/driver-model/tiny-dm.rst                  | 315 +++++++++++++++++
> > drivers/clk/Kconfig                           |  54 +++
> > drivers/clk/Makefile                          |   4 +-
> > drivers/clk/clk-uclass.c                      |  53 ++-
> > drivers/clk/rockchip/clk_rk3288.c             | 106 ++++--
> > drivers/core/Kconfig                          | 106 ++++++
> > drivers/core/Makefile                         |   3 +
> > drivers/core/of_extra.c                       |  49 ++-
> > drivers/core/regmap.c                         |   3 +-
> > drivers/core/syscon-uclass.c                  |  68 +++-
> > drivers/core/tiny.c                           | 249 ++++++++++++++
> > 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/dm/device.h                           | 121 +++++++
> > include/dm/of_extra.h                         |   6 +
> > include/dm/platdata.h                         |  20 +-
> > include/dm/tiny_struct.h                      |  42 +++
> > include/linker_lists.h                        |   6 +
> > 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 +
> > 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 +-
> > 92 files changed, 3454 insertions(+), 540 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
>
> --
> Diese Nachricht wurde von meinem Android-Ger?t mit K-9 Mail gesendet.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-03  3:03   ` Simon Glass
@ 2020-07-10  1:12     ` Simon Glass
  0 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2020-07-10  1:12 UTC (permalink / raw)
  To: u-boot

Hi,

On Thu, 2 Jul 2020 at 21:03, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Heinrich,
>
> On Thu, 2 Jul 2020 at 20:24, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> >
> >
> >
> > Am 2. Juli 2020 23:10:01 MESZ schrieb Simon Glass <sjg@chromium.org>:
> > >This series provides a proposed enhancement to driver model to reduce
> > >overhead in SPL.
> > >
> > >These patches should not be reviewed other than to comment on the
> > >approach. The code is all lumped together in a few patches and so
> > >cannot
> > >be applied as is.
> > >
> > >For now, the source tree is available at:
> > >
> > > https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
> > >
> > >Comments welcome!

Are there any comments on this one? I am still not sure which is the
best approach.

Regards,
Simon

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-02 21:10 [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL Simon Glass
                   ` (3 preceding siblings ...)
  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
  2020-07-27  2:44   ` Walter Lozano
  4 siblings, 1 reply; 11+ messages in thread
From: Walter Lozano @ 2020-07-10  4:12 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On 2/7/20 18:10, Simon Glass wrote:
> This series provides a proposed enhancement to driver model to reduce
> overhead in SPL.
>
> These patches should not be reviewed other than to comment on the
> approach. The code is all lumped together in a few patches and so cannot
> be applied as is.
>
> For now, the source tree is available at:
>
>     https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
>
> Comments welcome!
>
> Benefits (good news)
> --------------------
>
> As an example of the impact of tiny-dm, chromebook_jerry is converted to
> use it. This shows approximately a 30% reduction in code and data size and
> a 85% reduction in malloc() space over of-platdata:
>
>     text	   data	    bss	    dec	    hex	filename
>    25248	   1836	     12	  27096	   69d8	spl/u-boot-spl (original with DT)
>    19727	   3436	     12	  23175	   5a87	spl/u-boot-spl (OF_PLATDATA)
>      78%    187%    100%     86%         as %age of original
>
>    13784	   1408	     12	  15204	   3b64	spl/u-boot-spl (SPL_TINY)
>      70%     41%    100%     66%         as %age of platdata
>      55%     77%    100%     56%         as %age of original
>
> SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116 (SPL_TINY).
>
> Overall the 'overhead' of tiny-dm is much less than the full driver
> model. Code size is currently about 600 bytes for these functions on
> Thumb2:
>
>     00000054 T tiny_dev_probe
>     00000034 T tiny_dev_get_by_drvdata
>     00000024 T tiny_dev_find
>     0000001a T tiny_dev_get
>     0000003c T tinydev_alloc_data
>     0000002a t tinydev_lookup_data
>     00000022 T tinydev_ensure_data
>     00000014 T tinydev_get_data
>     00000004 T tinydev_get_parent
>
> Effort (bad news)
> -----------------
>
> Unfortunately it is quite a bit of work to convert drivers over to
> tiny-dm. First, the of-platdata conversion must be done. But on top of
> that, tiny-dm needs entirely separate code for dealing with devices. This
> means that instead of 'struct udevice' and 'struct uclass' there is just
> 'struct tinydev'. Each driver and uclass must be modified to support
> both, pulling common code into internal static functions.
>
> Another option
> --------------
>
> Note: It is assumed that any board that is space-contrained should use
> of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
> reduce device-tree overhead by approximately 4KB.
>
> Designing tiny-dm has suggested a number of things that could be changed
> in the current driver model to make it more space-efficient for TPL and
> SPL. The ones with least impact on driver code are (CS=reduces code size,
> DS=reduces data size):
>
>     CS - drop driver_bind() and create devices (struct udevice) at
>          build-time
>     CS - allocate all device- and uclass-private data at build-time
>     CS - remove all but the basic operations for each uclass (e.g. SPI
>          flash only supports reading)
>     DS - use 8-bit indexes instead of 32/64-bit pointers for device
>          pointers possible since these are created at build-time)
>     DS - use singly-linked lists
>     DS - use 16-bit offsets to private data, instead of 32/64-bit pointers
>          (possible since it is all in SRAM relative to malloc() base,
>          presumably word-aligned and < 256KB)
>     DS - move private pointers into a separate data structure so that NULLs
>          are not stored
>     CS / DS - Combine req_seq and seq and calculate the new value at
>          build-time
>
> More difficult are:
>
>     DS - drop some of the lesser-used driver and uclass methods
>     DS - drop all uclass methods except init()
>     DS - drop all driver methods except probe()
>     CS / DS - drop uclasses and require drivers to manually call uclass
>          functions
>
> Even with all of this we would not reach tiny-dm and it would muddy up the
> driver-model datas structures. But we might come close to tiny-dm on size
> and there are some advantages:
>
> - much of the benefit would apply to all boards that use of-platdata (i.e.
>    with very little effort on behalf of board maintainers)
> - the impact on the code base is much less (we keep a single, unified
>    driver mode in SPL and U-Boot proper)
>
> Overall I think it is worth looking at this option. While it doesn't have
> the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
> driver code as much and it is easier to learn.

Thanks for your hard work on this topic.

I think that there is great value in this research and in this 
conclusion. It is clear that there two different approaches, but I feel 
that the improvement to? the current DM implementation would have a 
higher impact in the community.

Since the first version of this proposal I have been thinking in a 
solution that takes some of the advantages of tiny-dm idea but that does 
not require so much effort. This seems to be aligned with what you have 
been explaining in this section.

I found interesting your proposal about simplification some data 
structures. In this sense one of my ideas, a bit biased by some of the 
improvements in dtoc, is to change the the definition of struct driver 
based on if OF_PLATDATA is enabled, and in this case remove some 
properties.

struct driver {
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
         char *name;
#endif
         enum uclass_id id;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
         const struct udevice_id *of_match;
#endif
         int (*bind)(struct udevice *dev);
         int (*probe)(struct udevice *dev);
         int (*remove)(struct udevice *dev);
         int (*unbind)(struct udevice *dev);
         int (*ofdata_to_platdata)(struct udevice *dev);
         int (*child_post_bind)(struct udevice *dev);
         int (*child_pre_probe)(struct udevice *dev);
         int (*child_post_remove)(struct udevice *dev);
         int priv_auto_alloc_size;
         int platdata_auto_alloc_size;
         int per_child_auto_alloc_size;
         int per_child_platdata_auto_alloc_size;
         const void *ops;        /* driver-specific operations */
         uint32_t flags;
#if CONFIG_IS_ENABLED(ACPIGEN)
         struct acpi_ops *acpi_ops;
#endif
};

By just removing those properties, we save some bytes as we get rid of 
several strings. Also maybe it would be nice to add some macros to make 
it cleaner in drivers to use or not those properties, instead of adding 
lots of #if.

I feel, as you clearly expressed, that some additional refactotring can 
be made to make the logic be more similar to the tiny-dm one. I also 
found interesting that several of your proposals will have impact in 
U-Boot, not only in TPL/SPL.

> Changes in v2:
> - Various updates, and ported to chromebook_jerry (rockchip)
>
> Simon Glass (3):
>    dm: Driver and uclass changes for tiny-dm
>    dm: Arch-specific changes for tiny-dm
>    dm: Core changes for tiny-dm
>
>   arch/arm/dts/rk3288-u-boot.dtsi               |  17 +-
>   arch/arm/dts/rk3288-veyron.dtsi               |  26 +-
>   arch/arm/dts/rk3288.dtsi                      |   3 +
>   arch/arm/include/asm/arch-rockchip/clock.h    |   9 +
>   .../include/asm/arch-rockchip/sdram_rk3288.h  |  21 ++
>   arch/arm/include/asm/arch-rockchip/spi.h      |   1 +
>   arch/arm/include/asm/io.h                     |  18 +
>   arch/arm/mach-rockchip/rk3288/clk_rk3288.c    |  46 +++
>   arch/arm/mach-rockchip/rk3288/rk3288.c        |   2 +
>   arch/arm/mach-rockchip/rk3288/syscon_rk3288.c |  45 +--
>   arch/arm/mach-rockchip/sdram.c                |  33 +-
>   arch/sandbox/cpu/u-boot-spl.lds               |  12 +
>   arch/sandbox/dts/sandbox.dts                  |   3 +-
>   arch/sandbox/dts/sandbox.dtsi                 |   2 +-
>   arch/x86/cpu/apollolake/cpu_spl.c             |   5 +-
>   arch/x86/cpu/apollolake/uart.c                |  56 ++++
>   arch/x86/dts/chromebook_coral.dts             |   1 +
>   arch/x86/lib/tpl.c                            |   4 +-
>   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 +++--
>   configs/chromebook_coral_defconfig            |   1 +
>   configs/chromebook_jerry_defconfig            |  11 +
>   configs/rock2_defconfig                       |   3 +
>   doc/develop/debugging.rst                     |  35 ++
>   doc/driver-model/tiny-dm.rst                  | 315 +++++++++++++++++
>   drivers/clk/Kconfig                           |  54 +++
>   drivers/clk/Makefile                          |   4 +-
>   drivers/clk/clk-uclass.c                      |  53 ++-
>   drivers/clk/rockchip/clk_rk3288.c             | 106 ++++--
>   drivers/core/Kconfig                          | 106 ++++++
>   drivers/core/Makefile                         |   3 +
>   drivers/core/of_extra.c                       |  49 ++-
>   drivers/core/regmap.c                         |   3 +-
>   drivers/core/syscon-uclass.c                  |  68 +++-
>   drivers/core/tiny.c                           | 249 ++++++++++++++
>   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/dm/device.h                           | 121 +++++++
>   include/dm/of_extra.h                         |   6 +
>   include/dm/platdata.h                         |  20 +-
>   include/dm/tiny_struct.h                      |  42 +++
>   include/linker_lists.h                        |   6 +
>   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 +
>   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 +-
>   92 files changed, 3454 insertions(+), 540 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
>
Regards,

Walter

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-10  4:12 ` Walter Lozano
@ 2020-07-27  2:44   ` Walter Lozano
  2020-08-16  3:06     ` Simon Glass
  0 siblings, 1 reply; 11+ messages in thread
From: Walter Lozano @ 2020-07-27  2:44 UTC (permalink / raw)
  To: u-boot

Hi Simon,


On 10/7/20 01:12, Walter Lozano wrote:
> Hi Simon,
>
> On 2/7/20 18:10, Simon Glass wrote:
>> This series provides a proposed enhancement to driver model to reduce
>> overhead in SPL.
>>
>> These patches should not be reviewed other than to comment on the
>> approach. The code is all lumped together in a few patches and so cannot
>> be applied as is.
>>
>> For now, the source tree is available at:
>>
>> https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
>>
>> Comments welcome!
>>
>> Benefits (good news)
>> --------------------
>>
>> As an example of the impact of tiny-dm, chromebook_jerry is converted to
>> use it. This shows approximately a 30% reduction in code and data 
>> size and
>> a 85% reduction in malloc() space over of-platdata:
>>
>> ??? text?????? data??????? bss??????? dec??????? hex??? filename
>> ?? 25248?????? 1836???????? 12????? 27096?????? 69d8 spl/u-boot-spl 
>> (original with DT)
>> ?? 19727?????? 3436???????? 12????? 23175?????? 5a87 spl/u-boot-spl 
>> (OF_PLATDATA)
>> ???? 78%??? 187%??? 100%???? 86%???????? as %age of original
>>
>> ?? 13784?????? 1408???????? 12????? 15204?????? 3b64 spl/u-boot-spl 
>> (SPL_TINY)
>> ???? 70%???? 41%??? 100%???? 66%???????? as %age of platdata
>> ???? 55%???? 77%??? 100%???? 56%???????? as %age of original
>>
>> SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116 (SPL_TINY).
>>
>> Overall the 'overhead' of tiny-dm is much less than the full driver
>> model. Code size is currently about 600 bytes for these functions on
>> Thumb2:
>>
>> ??? 00000054 T tiny_dev_probe
>> ??? 00000034 T tiny_dev_get_by_drvdata
>> ??? 00000024 T tiny_dev_find
>> ??? 0000001a T tiny_dev_get
>> ??? 0000003c T tinydev_alloc_data
>> ??? 0000002a t tinydev_lookup_data
>> ??? 00000022 T tinydev_ensure_data
>> ??? 00000014 T tinydev_get_data
>> ??? 00000004 T tinydev_get_parent
>>
>> Effort (bad news)
>> -----------------
>>
>> Unfortunately it is quite a bit of work to convert drivers over to
>> tiny-dm. First, the of-platdata conversion must be done. But on top of
>> that, tiny-dm needs entirely separate code for dealing with devices. 
>> This
>> means that instead of 'struct udevice' and 'struct uclass' there is just
>> 'struct tinydev'. Each driver and uclass must be modified to support
>> both, pulling common code into internal static functions.
>>
>> Another option
>> --------------
>>
>> Note: It is assumed that any board that is space-contrained should use
>> of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
>> reduce device-tree overhead by approximately 4KB.
>>
>> Designing tiny-dm has suggested a number of things that could be changed
>> in the current driver model to make it more space-efficient for TPL and
>> SPL. The ones with least impact on driver code are (CS=reduces code 
>> size,
>> DS=reduces data size):
>>
>> ??? CS - drop driver_bind() and create devices (struct udevice) at
>> ???????? build-time
>> ??? CS - allocate all device- and uclass-private data at build-time
>> ??? CS - remove all but the basic operations for each uclass (e.g. SPI
>> ???????? flash only supports reading)
>> ??? DS - use 8-bit indexes instead of 32/64-bit pointers for device
>> ???????? pointers possible since these are created at build-time)
>> ??? DS - use singly-linked lists
>> ??? DS - use 16-bit offsets to private data, instead of 32/64-bit 
>> pointers
>> ???????? (possible since it is all in SRAM relative to malloc() base,
>> ???????? presumably word-aligned and < 256KB)
>> ??? DS - move private pointers into a separate data structure so that 
>> NULLs
>> ???????? are not stored
>> ??? CS / DS - Combine req_seq and seq and calculate the new value at
>> ???????? build-time
>>
>> More difficult are:
>>
>> ??? DS - drop some of the lesser-used driver and uclass methods
>> ??? DS - drop all uclass methods except init()
>> ??? DS - drop all driver methods except probe()
>> ??? CS / DS - drop uclasses and require drivers to manually call uclass
>> ???????? functions
>>
>> Even with all of this we would not reach tiny-dm and it would muddy 
>> up the
>> driver-model datas structures. But we might come close to tiny-dm on 
>> size
>> and there are some advantages:
>>
>> - much of the benefit would apply to all boards that use of-platdata 
>> (i.e.
>> ?? with very little effort on behalf of board maintainers)
>> - the impact on the code base is much less (we keep a single, unified
>> ?? driver mode in SPL and U-Boot proper)
>>
>> Overall I think it is worth looking at this option. While it doesn't 
>> have
>> the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
>> driver code as much and it is easier to learn.
>
> Thanks for your hard work on this topic.
>
> I think that there is great value in this research and in this 
> conclusion. It is clear that there two different approaches, but I 
> feel that the improvement to? the current DM implementation would have 
> a higher impact in the community.
>
> Since the first version of this proposal I have been thinking in a 
> solution that takes some of the advantages of tiny-dm idea but that 
> does not require so much effort. This seems to be aligned with what 
> you have been explaining in this section.
>
> I found interesting your proposal about simplification some data 
> structures. In this sense one of my ideas, a bit biased by some of the 
> improvements in dtoc, is to change the the definition of struct driver 
> based on if OF_PLATDATA is enabled, and in this case remove some 
> properties.
>
> struct driver {
> #if !CONFIG_IS_ENABLED(OF_PLATDATA)
> ??????? char *name;
> #endif
> ??????? enum uclass_id id;
> #if !CONFIG_IS_ENABLED(OF_PLATDATA)
> ??????? const struct udevice_id *of_match;
> #endif
> ??????? int (*bind)(struct udevice *dev);
> ??????? int (*probe)(struct udevice *dev);
> ??????? int (*remove)(struct udevice *dev);
> ??????? int (*unbind)(struct udevice *dev);
> ??????? int (*ofdata_to_platdata)(struct udevice *dev);
> ??????? int (*child_post_bind)(struct udevice *dev);
> ??????? int (*child_pre_probe)(struct udevice *dev);
> ??????? int (*child_post_remove)(struct udevice *dev);
> ??????? int priv_auto_alloc_size;
> ??????? int platdata_auto_alloc_size;
> ??????? int per_child_auto_alloc_size;
> ??????? int per_child_platdata_auto_alloc_size;
> ??????? const void *ops;??????? /* driver-specific operations */
> ??????? uint32_t flags;
> #if CONFIG_IS_ENABLED(ACPIGEN)
> ??????? struct acpi_ops *acpi_ops;
> #endif
> };
>
> By just removing those properties, we save some bytes as we get rid of 
> several strings. Also maybe it would be nice to add some macros to 
> make it cleaner in drivers to use or not those properties, instead of 
> adding lots of #if.
>
> I feel, as you clearly expressed, that some additional refactotring 
> can be made to make the logic be more similar to the tiny-dm one. I 
> also found interesting that several of your proposals will have impact 
> in U-Boot, not only in TPL/SPL.


Just to be a bit more clear, I was thinking in something like


diff --git a/include/dm/device.h b/include/dm/device.h
index f5738a0cee..0ee239be8f 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -203,6 +203,16 @@ struct udevice_id {
  #define of_match_ptr(_ptr)     NULL
  #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
  
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+#undef OF_PLATDATA_TINY
+#define STRUCT_FIELD(x) x;
+#define STRUCT_VALUE(x) x,
+#else
+#define OF_PLATDATA_TINY
+#define STRUCT_FIELD(x)
+#define STRUCT_VALUE(x)
+#endif
+
  /**
   * struct driver - A driver for a feature or peripheral
   *
@@ -252,9 +262,9 @@ struct udevice_id {
   * allowing the device to add things to the ACPI tables passed to Linux
   */
  struct driver {
-       char *name;
+       STRUCT_FIELD(char *name)
         enum uclass_id id;
-       const struct udevice_id *of_match;
+       STRUCT_FIELD(const struct udevice_id *of_match)
         int (*bind)(struct udevice *dev);
         int (*probe)(struct udevice *dev);
         int (*remove)(struct udevice *dev);
diff --git a/drivers/core/simple-bus.c b/drivers/core/simple-bus.c
index 7cc1d46009..e303d59daf 100644
--- a/drivers/core/simple-bus.c
+++ b/drivers/core/simple-bus.c
@@ -57,8 +57,8 @@ static const struct udevice_id generic_simple_bus_ids[] = {
  };
  
  U_BOOT_DRIVER(simple_bus) = {
-       .name   = "simple_bus",
+       STRUCT_VALUE(.name      = "simple_bus")
         .id     = UCLASS_SIMPLE_BUS,
-       .of_match = generic_simple_bus_ids,
+       STRUCT_VALUE(.of_match = generic_simple_bus_ids)
         .flags  = DM_FLAG_PRE_RELOC,
  };


Please don't pay attention to how OF_PLATDATA_TINY is implemented, it is 
just part of the test. I think it should be a configuration option that 
removes some functionality from OF_PLATDATA, like having the overhead of 
the strings. On top of this you could add additional improvements, like 
the binding implementation in tiny-dm which uses DM_REF_TINY_DRIVER.


>
>> Changes in v2:
>> - Various updates, and ported to chromebook_jerry (rockchip)
>>
>> Simon Glass (3):
>> ?? dm: Driver and uclass changes for tiny-dm
>> ?? dm: Arch-specific changes for tiny-dm
>> ?? dm: Core changes for tiny-dm
>>
>> ? arch/arm/dts/rk3288-u-boot.dtsi?????????????? |? 17 +-
>> ? arch/arm/dts/rk3288-veyron.dtsi?????????????? |? 26 +-
>> ? arch/arm/dts/rk3288.dtsi????????????????????? |?? 3 +
>> ? arch/arm/include/asm/arch-rockchip/clock.h??? |?? 9 +
>> ? .../include/asm/arch-rockchip/sdram_rk3288.h? |? 21 ++
>> ? arch/arm/include/asm/arch-rockchip/spi.h????? |?? 1 +
>> ? arch/arm/include/asm/io.h???????????????????? |? 18 +
>> ? arch/arm/mach-rockchip/rk3288/clk_rk3288.c??? |? 46 +++
>> ? arch/arm/mach-rockchip/rk3288/rk3288.c??????? |?? 2 +
>> ? arch/arm/mach-rockchip/rk3288/syscon_rk3288.c |? 45 +--
>> ? arch/arm/mach-rockchip/sdram.c??????????????? |? 33 +-
>> ? arch/sandbox/cpu/u-boot-spl.lds?????????????? |? 12 +
>> ? arch/sandbox/dts/sandbox.dts????????????????? |?? 3 +-
>> ? arch/sandbox/dts/sandbox.dtsi???????????????? |?? 2 +-
>> ? arch/x86/cpu/apollolake/cpu_spl.c???????????? |?? 5 +-
>> ? arch/x86/cpu/apollolake/uart.c??????????????? |? 56 ++++
>> ? arch/x86/dts/chromebook_coral.dts???????????? |?? 1 +
>> ? arch/x86/lib/tpl.c??????????????????????????? |?? 4 +-
>> ? 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 +++--
>> ? configs/chromebook_coral_defconfig??????????? |?? 1 +
>> ? configs/chromebook_jerry_defconfig??????????? |? 11 +
>> ? configs/rock2_defconfig?????????????????????? |?? 3 +
>> ? doc/develop/debugging.rst???????????????????? |? 35 ++
>> ? doc/driver-model/tiny-dm.rst????????????????? | 315 +++++++++++++++++
>> ? drivers/clk/Kconfig?????????????????????????? |? 54 +++
>> ? drivers/clk/Makefile????????????????????????? |?? 4 +-
>> ? drivers/clk/clk-uclass.c????????????????????? |? 53 ++-
>> ? drivers/clk/rockchip/clk_rk3288.c???????????? | 106 ++++--
>> ? drivers/core/Kconfig????????????????????????? | 106 ++++++
>> ? drivers/core/Makefile???????????????????????? |?? 3 +
>> ? drivers/core/of_extra.c?????????????????????? |? 49 ++-
>> ? drivers/core/regmap.c???????????????????????? |?? 3 +-
>> ? drivers/core/syscon-uclass.c????????????????? |? 68 +++-
>> ? drivers/core/tiny.c?????????????????????????? | 249 ++++++++++++++
>> ? 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/dm/device.h?????????????????????????? | 121 +++++++
>> ? include/dm/of_extra.h???????????????????????? |?? 6 +
>> ? include/dm/platdata.h???????????????????????? |? 20 +-
>> ? include/dm/tiny_struct.h????????????????????? |? 42 +++
>> ? include/linker_lists.h??????????????????????? |?? 6 +
>> ? 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 +
>> ? 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 +-
>> ? 92 files changed, 3454 insertions(+), 540 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
>>
>
Regards,

Walter

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-07-27  2:44   ` Walter Lozano
@ 2020-08-16  3:06     ` Simon Glass
  2020-08-16  3:17       ` Walter Lozano
  0 siblings, 1 reply; 11+ messages in thread
From: Simon Glass @ 2020-08-16  3:06 UTC (permalink / raw)
  To: u-boot

Hi Walter,

On Sun, 26 Jul 2020 at 20:45, Walter Lozano <walter.lozano@collabora.com> wrote:
>
> Hi Simon,
>
>
> On 10/7/20 01:12, Walter Lozano wrote:
> > Hi Simon,
> >
> > On 2/7/20 18:10, Simon Glass wrote:
> >> This series provides a proposed enhancement to driver model to reduce
> >> overhead in SPL.
> >>
> >> These patches should not be reviewed other than to comment on the
> >> approach. The code is all lumped together in a few patches and so cannot
> >> be applied as is.
> >>
> >> For now, the source tree is available at:
> >>
> >> https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
> >>
> >> Comments welcome!
> >>
> >> Benefits (good news)
> >> --------------------
> >>
> >> As an example of the impact of tiny-dm, chromebook_jerry is converted to
> >> use it. This shows approximately a 30% reduction in code and data
> >> size and
> >> a 85% reduction in malloc() space over of-platdata:
> >>
> >>     text       data        bss        dec        hex    filename
> >>    25248       1836         12      27096       69d8 spl/u-boot-spl
> >> (original with DT)
> >>    19727       3436         12      23175       5a87 spl/u-boot-spl
> >> (OF_PLATDATA)
> >>      78%    187%    100%     86%         as %age of original
> >>
> >>    13784       1408         12      15204       3b64 spl/u-boot-spl
> >> (SPL_TINY)
> >>      70%     41%    100%     66%         as %age of platdata
> >>      55%     77%    100%     56%         as %age of original
> >>
> >> SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116 (SPL_TINY).
> >>
> >> Overall the 'overhead' of tiny-dm is much less than the full driver
> >> model. Code size is currently about 600 bytes for these functions on
> >> Thumb2:
> >>
> >>     00000054 T tiny_dev_probe
> >>     00000034 T tiny_dev_get_by_drvdata
> >>     00000024 T tiny_dev_find
> >>     0000001a T tiny_dev_get
> >>     0000003c T tinydev_alloc_data
> >>     0000002a t tinydev_lookup_data
> >>     00000022 T tinydev_ensure_data
> >>     00000014 T tinydev_get_data
> >>     00000004 T tinydev_get_parent
> >>
> >> Effort (bad news)
> >> -----------------
> >>
> >> Unfortunately it is quite a bit of work to convert drivers over to
> >> tiny-dm. First, the of-platdata conversion must be done. But on top of
> >> that, tiny-dm needs entirely separate code for dealing with devices.
> >> This
> >> means that instead of 'struct udevice' and 'struct uclass' there is just
> >> 'struct tinydev'. Each driver and uclass must be modified to support
> >> both, pulling common code into internal static functions.
> >>
> >> Another option
> >> --------------
> >>
> >> Note: It is assumed that any board that is space-contrained should use
> >> of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
> >> reduce device-tree overhead by approximately 4KB.
> >>
> >> Designing tiny-dm has suggested a number of things that could be changed
> >> in the current driver model to make it more space-efficient for TPL and
> >> SPL. The ones with least impact on driver code are (CS=reduces code
> >> size,
> >> DS=reduces data size):
> >>
> >>     CS - drop driver_bind() and create devices (struct udevice) at
> >>          build-time
> >>     CS - allocate all device- and uclass-private data at build-time
> >>     CS - remove all but the basic operations for each uclass (e.g. SPI
> >>          flash only supports reading)
> >>     DS - use 8-bit indexes instead of 32/64-bit pointers for device
> >>          pointers possible since these are created at build-time)
> >>     DS - use singly-linked lists
> >>     DS - use 16-bit offsets to private data, instead of 32/64-bit
> >> pointers
> >>          (possible since it is all in SRAM relative to malloc() base,
> >>          presumably word-aligned and < 256KB)
> >>     DS - move private pointers into a separate data structure so that
> >> NULLs
> >>          are not stored
> >>     CS / DS - Combine req_seq and seq and calculate the new value at
> >>          build-time
> >>
> >> More difficult are:
> >>
> >>     DS - drop some of the lesser-used driver and uclass methods
> >>     DS - drop all uclass methods except init()
> >>     DS - drop all driver methods except probe()
> >>     CS / DS - drop uclasses and require drivers to manually call uclass
> >>          functions
> >>
> >> Even with all of this we would not reach tiny-dm and it would muddy
> >> up the
> >> driver-model datas structures. But we might come close to tiny-dm on
> >> size
> >> and there are some advantages:
> >>
> >> - much of the benefit would apply to all boards that use of-platdata
> >> (i.e.
> >>    with very little effort on behalf of board maintainers)
> >> - the impact on the code base is much less (we keep a single, unified
> >>    driver mode in SPL and U-Boot proper)
> >>
> >> Overall I think it is worth looking at this option. While it doesn't
> >> have
> >> the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
> >> driver code as much and it is easier to learn.
> >
> > Thanks for your hard work on this topic.
> >
> > I think that there is great value in this research and in this
> > conclusion. It is clear that there two different approaches, but I
> > feel that the improvement to  the current DM implementation would have
> > a higher impact in the community.
> >
> > Since the first version of this proposal I have been thinking in a
> > solution that takes some of the advantages of tiny-dm idea but that
> > does not require so much effort. This seems to be aligned with what
> > you have been explaining in this section.
> >
> > I found interesting your proposal about simplification some data
> > structures. In this sense one of my ideas, a bit biased by some of the
> > improvements in dtoc, is to change the the definition of struct driver
> > based on if OF_PLATDATA is enabled, and in this case remove some
> > properties.
> >
> > struct driver {
> > #if !CONFIG_IS_ENABLED(OF_PLATDATA)
> >         char *name;
> > #endif
> >         enum uclass_id id;
> > #if !CONFIG_IS_ENABLED(OF_PLATDATA)
> >         const struct udevice_id *of_match;
> > #endif
> >         int (*bind)(struct udevice *dev);
> >         int (*probe)(struct udevice *dev);
> >         int (*remove)(struct udevice *dev);
> >         int (*unbind)(struct udevice *dev);
> >         int (*ofdata_to_platdata)(struct udevice *dev);
> >         int (*child_post_bind)(struct udevice *dev);
> >         int (*child_pre_probe)(struct udevice *dev);
> >         int (*child_post_remove)(struct udevice *dev);
> >         int priv_auto_alloc_size;
> >         int platdata_auto_alloc_size;
> >         int per_child_auto_alloc_size;
> >         int per_child_platdata_auto_alloc_size;
> >         const void *ops;        /* driver-specific operations */
> >         uint32_t flags;
> > #if CONFIG_IS_ENABLED(ACPIGEN)
> >         struct acpi_ops *acpi_ops;
> > #endif
> > };
> >
> > By just removing those properties, we save some bytes as we get rid of
> > several strings. Also maybe it would be nice to add some macros to
> > make it cleaner in drivers to use or not those properties, instead of
> > adding lots of #if.
> >
> > I feel, as you clearly expressed, that some additional refactotring
> > can be made to make the logic be more similar to the tiny-dm one. I
> > also found interesting that several of your proposals will have impact
> > in U-Boot, not only in TPL/SPL.
>
>
> Just to be a bit more clear, I was thinking in something like
>
>
> diff --git a/include/dm/device.h b/include/dm/device.h
> index f5738a0cee..0ee239be8f 100644
> --- a/include/dm/device.h
> +++ b/include/dm/device.h
> @@ -203,6 +203,16 @@ struct udevice_id {
>   #define of_match_ptr(_ptr)     NULL
>   #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
>
> +#if !CONFIG_IS_ENABLED(OF_PLATDATA)
> +#undef OF_PLATDATA_TINY
> +#define STRUCT_FIELD(x) x;
> +#define STRUCT_VALUE(x) x,
> +#else
> +#define OF_PLATDATA_TINY
> +#define STRUCT_FIELD(x)
> +#define STRUCT_VALUE(x)
> +#endif
> +
>   /**
>    * struct driver - A driver for a feature or peripheral
>    *
> @@ -252,9 +262,9 @@ struct udevice_id {
>    * allowing the device to add things to the ACPI tables passed to Linux
>    */
>   struct driver {
> -       char *name;
> +       STRUCT_FIELD(char *name)
>          enum uclass_id id;
> -       const struct udevice_id *of_match;
> +       STRUCT_FIELD(const struct udevice_id *of_match)

I didn't actually reply to this, but I think this makes sense. Perhaps
have a DM_ prefix on your #defines?
(

>          int (*bind)(struct udevice *dev);
>          int (*probe)(struct udevice *dev);
>          int (*remove)(struct udevice *dev);
> diff --git a/drivers/core/simple-bus.c b/drivers/core/simple-bus.c
> index 7cc1d46009..e303d59daf 100644
> --- a/drivers/core/simple-bus.c
> +++ b/drivers/core/simple-bus.c
> @@ -57,8 +57,8 @@ static const struct udevice_id generic_simple_bus_ids[] = {
>   };
>
>   U_BOOT_DRIVER(simple_bus) = {
> -       .name   = "simple_bus",
> +       STRUCT_VALUE(.name      = "simple_bus")
>          .id     = UCLASS_SIMPLE_BUS,
> -       .of_match = generic_simple_bus_ids,
> +       STRUCT_VALUE(.of_match = generic_simple_bus_ids)
>          .flags  = DM_FLAG_PRE_RELOC,
>   };
>
>
> Please don't pay attention to how OF_PLATDATA_TINY is implemented, it is
> just part of the test. I think it should be a configuration option that
> removes some functionality from OF_PLATDATA, like having the overhead of
> the strings. On top of this you could add additional improvements, like
> the binding implementation in tiny-dm which uses DM_REF_TINY_DRIVER.
>

Yes OK. I am thinking of starting with just adding a parent. But time
is not my friend at present so it will probably be a month or so.

Regards,
SImon

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] RFC: tiny-dm: Proposal for using driver model in SPL
  2020-08-16  3:06     ` Simon Glass
@ 2020-08-16  3:17       ` Walter Lozano
  0 siblings, 0 replies; 11+ messages in thread
From: Walter Lozano @ 2020-08-16  3:17 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On 16/8/20 00:06, Simon Glass wrote:
> Hi Walter,
>
> On Sun, 26 Jul 2020 at 20:45, Walter Lozano <walter.lozano@collabora.com> wrote:
>> Hi Simon,
>>
>>
>> On 10/7/20 01:12, Walter Lozano wrote:
>>> Hi Simon,
>>>
>>> On 2/7/20 18:10, Simon Glass wrote:
>>>> This series provides a proposed enhancement to driver model to reduce
>>>> overhead in SPL.
>>>>
>>>> These patches should not be reviewed other than to comment on the
>>>> approach. The code is all lumped together in a few patches and so cannot
>>>> be applied as is.
>>>>
>>>> For now, the source tree is available at:
>>>>
>>>> https://gitlab.denx.de/u-boot/custodians/u-boot-dm/-/tree/dtoc-working
>>>>
>>>> Comments welcome!
>>>>
>>>> Benefits (good news)
>>>> --------------------
>>>>
>>>> As an example of the impact of tiny-dm, chromebook_jerry is converted to
>>>> use it. This shows approximately a 30% reduction in code and data
>>>> size and
>>>> a 85% reduction in malloc() space over of-platdata:
>>>>
>>>>      text       data        bss        dec        hex    filename
>>>>     25248       1836         12      27096       69d8 spl/u-boot-spl
>>>> (original with DT)
>>>>     19727       3436         12      23175       5a87 spl/u-boot-spl
>>>> (OF_PLATDATA)
>>>>       78%    187%    100%     86%         as %age of original
>>>>
>>>>     13784       1408         12      15204       3b64 spl/u-boot-spl
>>>> (SPL_TINY)
>>>>       70%     41%    100%     66%         as %age of platdata
>>>>       55%     77%    100%     56%         as %age of original
>>>>
>>>> SPL malloc() usage drops from 944 bytes (OF_PLATDATA) to 116 (SPL_TINY).
>>>>
>>>> Overall the 'overhead' of tiny-dm is much less than the full driver
>>>> model. Code size is currently about 600 bytes for these functions on
>>>> Thumb2:
>>>>
>>>>      00000054 T tiny_dev_probe
>>>>      00000034 T tiny_dev_get_by_drvdata
>>>>      00000024 T tiny_dev_find
>>>>      0000001a T tiny_dev_get
>>>>      0000003c T tinydev_alloc_data
>>>>      0000002a t tinydev_lookup_data
>>>>      00000022 T tinydev_ensure_data
>>>>      00000014 T tinydev_get_data
>>>>      00000004 T tinydev_get_parent
>>>>
>>>> Effort (bad news)
>>>> -----------------
>>>>
>>>> Unfortunately it is quite a bit of work to convert drivers over to
>>>> tiny-dm. First, the of-platdata conversion must be done. But on top of
>>>> that, tiny-dm needs entirely separate code for dealing with devices.
>>>> This
>>>> means that instead of 'struct udevice' and 'struct uclass' there is just
>>>> 'struct tinydev'. Each driver and uclass must be modified to support
>>>> both, pulling common code into internal static functions.
>>>>
>>>> Another option
>>>> --------------
>>>>
>>>> Note: It is assumed that any board that is space-contrained should use
>>>> of-platdata in SPL (see doc/driver-model/of-plat.rst). This is shown to
>>>> reduce device-tree overhead by approximately 4KB.
>>>>
>>>> Designing tiny-dm has suggested a number of things that could be changed
>>>> in the current driver model to make it more space-efficient for TPL and
>>>> SPL. The ones with least impact on driver code are (CS=reduces code
>>>> size,
>>>> DS=reduces data size):
>>>>
>>>>      CS - drop driver_bind() and create devices (struct udevice) at
>>>>           build-time
>>>>      CS - allocate all device- and uclass-private data at build-time
>>>>      CS - remove all but the basic operations for each uclass (e.g. SPI
>>>>           flash only supports reading)
>>>>      DS - use 8-bit indexes instead of 32/64-bit pointers for device
>>>>           pointers possible since these are created at build-time)
>>>>      DS - use singly-linked lists
>>>>      DS - use 16-bit offsets to private data, instead of 32/64-bit
>>>> pointers
>>>>           (possible since it is all in SRAM relative to malloc() base,
>>>>           presumably word-aligned and < 256KB)
>>>>      DS - move private pointers into a separate data structure so that
>>>> NULLs
>>>>           are not stored
>>>>      CS / DS - Combine req_seq and seq and calculate the new value at
>>>>           build-time
>>>>
>>>> More difficult are:
>>>>
>>>>      DS - drop some of the lesser-used driver and uclass methods
>>>>      DS - drop all uclass methods except init()
>>>>      DS - drop all driver methods except probe()
>>>>      CS / DS - drop uclasses and require drivers to manually call uclass
>>>>           functions
>>>>
>>>> Even with all of this we would not reach tiny-dm and it would muddy
>>>> up the
>>>> driver-model datas structures. But we might come close to tiny-dm on
>>>> size
>>>> and there are some advantages:
>>>>
>>>> - much of the benefit would apply to all boards that use of-platdata
>>>> (i.e.
>>>>     with very little effort on behalf of board maintainers)
>>>> - the impact on the code base is much less (we keep a single, unified
>>>>     driver mode in SPL and U-Boot proper)
>>>>
>>>> Overall I think it is worth looking at this option. While it doesn't
>>>> have
>>>> the 'nuclear' impact of tiny-dm, neither does it mess with the U-Boot
>>>> driver code as much and it is easier to learn.
>>> Thanks for your hard work on this topic.
>>>
>>> I think that there is great value in this research and in this
>>> conclusion. It is clear that there two different approaches, but I
>>> feel that the improvement to  the current DM implementation would have
>>> a higher impact in the community.
>>>
>>> Since the first version of this proposal I have been thinking in a
>>> solution that takes some of the advantages of tiny-dm idea but that
>>> does not require so much effort. This seems to be aligned with what
>>> you have been explaining in this section.
>>>
>>> I found interesting your proposal about simplification some data
>>> structures. In this sense one of my ideas, a bit biased by some of the
>>> improvements in dtoc, is to change the the definition of struct driver
>>> based on if OF_PLATDATA is enabled, and in this case remove some
>>> properties.
>>>
>>> struct driver {
>>> #if !CONFIG_IS_ENABLED(OF_PLATDATA)
>>>          char *name;
>>> #endif
>>>          enum uclass_id id;
>>> #if !CONFIG_IS_ENABLED(OF_PLATDATA)
>>>          const struct udevice_id *of_match;
>>> #endif
>>>          int (*bind)(struct udevice *dev);
>>>          int (*probe)(struct udevice *dev);
>>>          int (*remove)(struct udevice *dev);
>>>          int (*unbind)(struct udevice *dev);
>>>          int (*ofdata_to_platdata)(struct udevice *dev);
>>>          int (*child_post_bind)(struct udevice *dev);
>>>          int (*child_pre_probe)(struct udevice *dev);
>>>          int (*child_post_remove)(struct udevice *dev);
>>>          int priv_auto_alloc_size;
>>>          int platdata_auto_alloc_size;
>>>          int per_child_auto_alloc_size;
>>>          int per_child_platdata_auto_alloc_size;
>>>          const void *ops;        /* driver-specific operations */
>>>          uint32_t flags;
>>> #if CONFIG_IS_ENABLED(ACPIGEN)
>>>          struct acpi_ops *acpi_ops;
>>> #endif
>>> };
>>>
>>> By just removing those properties, we save some bytes as we get rid of
>>> several strings. Also maybe it would be nice to add some macros to
>>> make it cleaner in drivers to use or not those properties, instead of
>>> adding lots of #if.
>>>
>>> I feel, as you clearly expressed, that some additional refactotring
>>> can be made to make the logic be more similar to the tiny-dm one. I
>>> also found interesting that several of your proposals will have impact
>>> in U-Boot, not only in TPL/SPL.
>>
>> Just to be a bit more clear, I was thinking in something like
>>
>>
>> diff --git a/include/dm/device.h b/include/dm/device.h
>> index f5738a0cee..0ee239be8f 100644
>> --- a/include/dm/device.h
>> +++ b/include/dm/device.h
>> @@ -203,6 +203,16 @@ struct udevice_id {
>>    #define of_match_ptr(_ptr)     NULL
>>    #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
>>
>> +#if !CONFIG_IS_ENABLED(OF_PLATDATA)
>> +#undef OF_PLATDATA_TINY
>> +#define STRUCT_FIELD(x) x;
>> +#define STRUCT_VALUE(x) x,
>> +#else
>> +#define OF_PLATDATA_TINY
>> +#define STRUCT_FIELD(x)
>> +#define STRUCT_VALUE(x)
>> +#endif
>> +
>>    /**
>>     * struct driver - A driver for a feature or peripheral
>>     *
>> @@ -252,9 +262,9 @@ struct udevice_id {
>>     * allowing the device to add things to the ACPI tables passed to Linux
>>     */
>>    struct driver {
>> -       char *name;
>> +       STRUCT_FIELD(char *name)
>>           enum uclass_id id;
>> -       const struct udevice_id *of_match;
>> +       STRUCT_FIELD(const struct udevice_id *of_match)
> I didn't actually reply to this, but I think this makes sense. Perhaps
> have a DM_ prefix on your #defines?
> (

Thanks for your reply and paying attention to my comments. I totally 
agree that if you implement this idea you should rename #defines to make 
them more readable.

>>           int (*bind)(struct udevice *dev);
>>           int (*probe)(struct udevice *dev);
>>           int (*remove)(struct udevice *dev);
>> diff --git a/drivers/core/simple-bus.c b/drivers/core/simple-bus.c
>> index 7cc1d46009..e303d59daf 100644
>> --- a/drivers/core/simple-bus.c
>> +++ b/drivers/core/simple-bus.c
>> @@ -57,8 +57,8 @@ static const struct udevice_id generic_simple_bus_ids[] = {
>>    };
>>
>>    U_BOOT_DRIVER(simple_bus) = {
>> -       .name   = "simple_bus",
>> +       STRUCT_VALUE(.name      = "simple_bus")
>>           .id     = UCLASS_SIMPLE_BUS,
>> -       .of_match = generic_simple_bus_ids,
>> +       STRUCT_VALUE(.of_match = generic_simple_bus_ids)
>>           .flags  = DM_FLAG_PRE_RELOC,
>>    };
>>
>>
>> Please don't pay attention to how OF_PLATDATA_TINY is implemented, it is
>> just part of the test. I think it should be a configuration option that
>> removes some functionality from OF_PLATDATA, like having the overhead of
>> the strings. On top of this you could add additional improvements, like
>> the binding implementation in tiny-dm which uses DM_REF_TINY_DRIVER.
>>
> Yes OK. I am thinking of starting with just adding a parent. But time
> is not my friend at present so it will probably be a month or so.


Thanks for sharing your plans. Time is always an issue, but I think we 
are in the right track, and that all the research you have done with 
tiny-dm will give great benefits.

Regards,

Walter

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-08-16  3:17 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [RFC PATCH v2 3/3] dm: Core " 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-03  3:03   ` Simon Glass
2020-07-10  1:12     ` Simon Glass
2020-07-10  4:12 ` Walter Lozano
2020-07-27  2:44   ` Walter Lozano
2020-08-16  3:06     ` Simon Glass
2020-08-16  3:17       ` Walter Lozano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.