This includes various changes to support tiny-dm in serial, ram, clock,
spi, SPI flash, syscon and sysreset drivers.

Signed-off-by: Simon Glass <s...@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....@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

Reply via email to