diff --git a/arch/arm/dts/exynos4210-pinctrl-uboot.dtsi b/arch/arm/dts/exynos4210-pinctrl-uboot.dtsi
index f9b61ba8e8fe525e57e4ca985afd9b540390d04d..0ff41d00287d2d23702f39bdf7a411831c0ceab6 100644
--- a/arch/arm/dts/exynos4210-pinctrl-uboot.dtsi
+++ b/arch/arm/dts/exynos4210-pinctrl-uboot.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 /{
diff --git a/arch/arm/dts/exynos4x12-pinctrl-uboot.dtsi b/arch/arm/dts/exynos4x12-pinctrl-uboot.dtsi
index c41d07b65fc56abd07b890a369951713d94c5a33..8e5a6c61180a758adc33f1fccd32e8cc78de73b6 100644
--- a/arch/arm/dts/exynos4x12-pinctrl-uboot.dtsi
+++ b/arch/arm/dts/exynos4x12-pinctrl-uboot.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 /{
diff --git a/arch/arm/dts/exynos5250-pinctrl-uboot.dtsi b/arch/arm/dts/exynos5250-pinctrl-uboot.dtsi
index 7edb0ca29003294750a1c9c9fa88b5150a8ff53b..068c5f696fed5e1e1e09a346efacbbcfaa50a04c 100644
--- a/arch/arm/dts/exynos5250-pinctrl-uboot.dtsi
+++ b/arch/arm/dts/exynos5250-pinctrl-uboot.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 /{
diff --git a/arch/arm/dts/exynos54xx-pinctrl-uboot.dtsi b/arch/arm/dts/exynos54xx-pinctrl-uboot.dtsi
index 5a86211d4a01516c991e90ff4d7943a900d4c5c7..635a1b0d3a50157bc94e81e01712c6dd6f1e5ffe 100644
--- a/arch/arm/dts/exynos54xx-pinctrl-uboot.dtsi
+++ b/arch/arm/dts/exynos54xx-pinctrl-uboot.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 /{
diff --git a/arch/arm/dts/s5pc100-pinctrl.dtsi b/arch/arm/dts/s5pc100-pinctrl.dtsi
index bd9f97c97ba8a12ad87f6b6cb3e0abfad93c0b94..975386969e16ec3c0342da39e3e289fcf9fc4872 100644
--- a/arch/arm/dts/s5pc100-pinctrl.dtsi
+++ b/arch/arm/dts/s5pc100-pinctrl.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 / {
diff --git a/arch/arm/dts/s5pc110-pinctrl.dtsi b/arch/arm/dts/s5pc110-pinctrl.dtsi
index d21b6ab756a91a9b8e9e26f43a750d76980d3969..2e9d552daaf5fe088b51e6aafc4f00d4a624e40c 100644
--- a/arch/arm/dts/s5pc110-pinctrl.dtsi
+++ b/arch/arm/dts/s5pc110-pinctrl.dtsi
@@ -2,6 +2,8 @@
  * U-Boot additions to enable a generic Exynos GPIO driver
  *
  * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 / {
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c
index f0dafedc254fb5d0ca3973946539babf7a75d267..168f2efa33e5fe4f5eaab52503dbb724fd2f5528 100644
--- a/arch/sandbox/cpu/cpu.c
+++ b/arch/sandbox/cpu/cpu.c
@@ -6,6 +6,7 @@
 #include <common.h>
 #include <dm/root.h>
 #include <os.h>
+#include <asm/io.h>
 #include <asm/state.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -97,3 +98,43 @@ phys_addr_t map_to_sysmem(const void *ptr)
 void flush_dcache_range(unsigned long start, unsigned long stop)
 {
 }
+
+int sandbox_read_fdt_from_file(void)
+{
+	struct sandbox_state *state = state_get_current();
+	const char *fname = state->fdt_fname;
+	void *blob;
+	loff_t size;
+	int err;
+	int fd;
+
+	blob = map_sysmem(CONFIG_SYS_FDT_LOAD_ADDR, 0);
+	if (!state->fdt_fname) {
+		err = fdt_create_empty_tree(blob, 256);
+		if (!err)
+			goto done;
+		printf("Unable to create empty FDT: %s\n", fdt_strerror(err));
+		return -EINVAL;
+	}
+
+	err = os_get_filesize(fname, &size);
+	if (err < 0) {
+		printf("Failed to file FDT file '%s'\n", fname);
+		return err;
+	}
+	fd = os_open(fname, OS_O_RDONLY);
+	if (fd < 0) {
+		printf("Failed to open FDT file '%s'\n", fname);
+		return -EACCES;
+	}
+	if (os_read(fd, blob, size) != size) {
+		os_close(fd);
+		return -EIO;
+	}
+	os_close(fd);
+
+done:
+	gd->fdt_blob = blob;
+
+	return 0;
+}
diff --git a/arch/sandbox/include/asm/bitops.h b/arch/sandbox/include/asm/bitops.h
index e807c4ef34bda06b5f1d555bb248ea02fccf4390..f1a7aeee938f75e5e14dd1173f9453cae641a094 100644
--- a/arch/sandbox/include/asm/bitops.h
+++ b/arch/sandbox/include/asm/bitops.h
@@ -1,6 +1,8 @@
 /*
  * Copyright (c) 2011 The Chromium OS Authors.
  *
+ * Modified from Linux arch/arm/include/asm/bitops.h
+ *
  * Copyright 1995, Russell King.
  * Various bits and pieces copyrights include:
  *  Linus Torvalds (test_bit).
diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h
index d5b93616839d7e0def937090f334e90b6517fe13..da87cc304067f85ac4e829ef3111901d49de1999 100644
--- a/arch/sandbox/include/asm/u-boot-sandbox.h
+++ b/arch/sandbox/include/asm/u-boot-sandbox.h
@@ -75,4 +75,12 @@ int pci_unmap_physmem(const void *addr, unsigned long len,
  */
 void sandbox_set_enable_pci_map(int enable);
 
+/**
+ * sandbox_read_fdt_from_file() - Read a device tree from a file
+ *
+ * Read a device tree file from a host file and set it up for use as the
+ * control FDT.
+ */
+int sandbox_read_fdt_from_file(void);
+
 #endif	/* _U_BOOT_SANDBOX_H_ */
diff --git a/common/board_f.c b/common/board_f.c
index 775df1419e6c990b66213c148bef7d2a96c7af9d..322e0700d7384eee1ff1a23845054e13c3ea0a7b 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -23,6 +23,7 @@
 #include <i2c.h>
 #include <initcall.h>
 #include <logbuff.h>
+#include <malloc.h>
 #include <mapmem.h>
 
 /* TODO: Can we move these into arch/ headers? */
@@ -282,49 +283,6 @@ __weak int arch_cpu_init(void)
 	return 0;
 }
 
-#ifdef CONFIG_OF_HOSTFILE
-
-static int read_fdt_from_file(void)
-{
-	struct sandbox_state *state = state_get_current();
-	const char *fname = state->fdt_fname;
-	void *blob;
-	loff_t size;
-	int err;
-	int fd;
-
-	blob = map_sysmem(CONFIG_SYS_FDT_LOAD_ADDR, 0);
-	if (!state->fdt_fname) {
-		err = fdt_create_empty_tree(blob, 256);
-		if (!err)
-			goto done;
-		printf("Unable to create empty FDT: %s\n", fdt_strerror(err));
-		return -EINVAL;
-	}
-
-	err = os_get_filesize(fname, &size);
-	if (err < 0) {
-		printf("Failed to file FDT file '%s'\n", fname);
-		return err;
-	}
-	fd = os_open(fname, OS_O_RDONLY);
-	if (fd < 0) {
-		printf("Failed to open FDT file '%s'\n", fname);
-		return -EACCES;
-	}
-	if (os_read(fd, blob, size) != size) {
-		os_close(fd);
-		return -EIO;
-	}
-	os_close(fd);
-
-done:
-	gd->fdt_blob = blob;
-
-	return 0;
-}
-#endif
-
 #ifdef CONFIG_SANDBOX
 static int setup_ram_buf(void)
 {
@@ -337,28 +295,6 @@ static int setup_ram_buf(void)
 }
 #endif
 
-static int setup_fdt(void)
-{
-#ifdef CONFIG_OF_CONTROL
-# ifdef CONFIG_OF_EMBED
-	/* Get a pointer to the FDT */
-	gd->fdt_blob = __dtb_dt_begin;
-# elif defined CONFIG_OF_SEPARATE
-	/* FDT is at end of image */
-	gd->fdt_blob = (ulong *)&_end;
-# elif defined(CONFIG_OF_HOSTFILE)
-	if (read_fdt_from_file()) {
-		puts("Failed to read control FDT\n");
-		return -1;
-	}
-# endif
-	/* Allow the early environment to override the fdt address */
-	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
-						(uintptr_t)gd->fdt_blob);
-#endif
-	return 0;
-}
-
 /* Get the top of usable RAM */
 __weak ulong board_get_usable_ram_top(ulong total_size)
 {
@@ -786,17 +722,6 @@ static int mark_bootstage(void)
 	return 0;
 }
 
-static int initf_malloc(void)
-{
-#ifdef CONFIG_SYS_MALLOC_F_LEN
-	assert(gd->malloc_base);	/* Set up by crt0.S */
-	gd->malloc_limit = gd->malloc_base + CONFIG_SYS_MALLOC_F_LEN;
-	gd->malloc_ptr = 0;
-#endif
-
-	return 0;
-}
-
 static int initf_dm(void)
 {
 #if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN)
@@ -826,7 +751,9 @@ static init_fnc_t init_sequence_f[] = {
 	setup_ram_buf,
 #endif
 	setup_mon_len,
-	setup_fdt,
+#ifdef CONFIG_OF_CONTROL
+	fdtdec_setup,
+#endif
 #ifdef CONFIG_TRACE
 	trace_early_init,
 #endif
@@ -837,9 +764,6 @@ static init_fnc_t init_sequence_f[] = {
 #endif
 	arch_cpu_init,		/* basic arch cpu dependent setup */
 	mark_bootstage,
-#ifdef CONFIG_OF_CONTROL
-	fdtdec_check_fdt,
-#endif
 	initf_dm,
 	arch_cpu_init_dm,
 #if defined(CONFIG_BOARD_EARLY_INIT_F)
diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index b2ce063c5f40cff0d8b64b90011a375a3a01205a..b5bb05191c240a46d0fb944356418c45f3f8ea4e 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -3261,6 +3261,17 @@ int mALLOPt(param_number, value) int param_number; int value;
   }
 }
 
+int initf_malloc(void)
+{
+#ifdef CONFIG_SYS_MALLOC_F_LEN
+	assert(gd->malloc_base);	/* Set up by crt0.S */
+	gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
+	gd->malloc_ptr = 0;
+#endif
+
+	return 0;
+}
+
 /*
 
 History:
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 8e1fb40c47f12990c58bb36ab13e7e1596a80fb9..6a02c9ed96a3f167646e4175c31a2c24b180ce90 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -151,6 +151,8 @@ static void spl_ram_load_image(void)
 void board_init_r(gd_t *dummy1, ulong dummy2)
 {
 	u32 boot_device;
+	int ret;
+
 	debug(">>spl:board_init_r()\n");
 
 #if defined(CONFIG_SYS_SPL_MALLOC_START)
@@ -158,12 +160,24 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
 			CONFIG_SYS_SPL_MALLOC_SIZE);
 	gd->flags |= GD_FLG_FULL_MALLOC_INIT;
 #elif defined(CONFIG_SYS_MALLOC_F_LEN)
-	gd->malloc_limit = gd->malloc_base + CONFIG_SYS_MALLOC_F_LEN;
+	gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
 	gd->malloc_ptr = 0;
 #endif
-#ifdef CONFIG_SPL_DM
-	dm_init_and_scan(true);
-#endif
+	if (IS_ENABLED(CONFIG_OF_CONTROL) &&
+			!IS_ENABLED(CONFIG_SPL_DISABLE_OF_CONTROL)) {
+		ret = fdtdec_setup();
+		if (ret) {
+			debug("fdtdec_setup() returned error %d\n", ret);
+			hang();
+		}
+	}
+	if (IS_ENABLED(CONFIG_SPL_DM)) {
+		ret = dm_init_and_scan(true);
+		if (ret) {
+			debug("dm_init_and_scan() returned error %d\n", ret);
+			hang();
+		}
+	}
 
 #ifndef CONFIG_PPC
 	/*
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
index f83264d615da5390d34ed52b0a2394f6d2a6fcf6..f0276b1b46fe6135f549cf37f7fe5e28c4efd4fe 100644
--- a/doc/driver-model/README.txt
+++ b/doc/driver-model/README.txt
@@ -95,43 +95,82 @@ are provided in test/dm. To run them, try:
 You should see something like this:
 
     <...U-Boot banner...>
-    Running 29 driver model tests
+    Running 53 driver model tests
     Test: dm_test_autobind
     Test: dm_test_autoprobe
+    Test: dm_test_bus_child_post_bind
+    Test: dm_test_bus_child_post_bind_uclass
+    Test: dm_test_bus_child_pre_probe_uclass
     Test: dm_test_bus_children
-    Device 'd-test': seq 3 is in use by 'b-test'
-    Device 'c-test@0': seq 0 is in use by 'a-test'
-    Device 'c-test@1': seq 1 is in use by 'd-test'
+    Device 'c-test@0': seq 0 is in use by 'd-test'
+    Device 'c-test@1': seq 1 is in use by 'f-test'
     Test: dm_test_bus_children_funcs
     Test: dm_test_bus_children_iterators
     Test: dm_test_bus_parent_data
+    Test: dm_test_bus_parent_data_uclass
     Test: dm_test_bus_parent_ops
+    Test: dm_test_bus_parent_platdata
+    Test: dm_test_bus_parent_platdata_uclass
     Test: dm_test_children
+    Test: dm_test_device_get_uclass_id
+    Test: dm_test_eth
+    Using eth@10002000 device
+    Using eth@10003000 device
+    Using eth@10004000 device
+    Test: dm_test_eth_alias
+    Using eth@10002000 device
+    Using eth@10004000 device
+    Using eth@10002000 device
+    Using eth@10003000 device
+    Test: dm_test_eth_prime
+    Using eth@10003000 device
+    Using eth@10002000 device
+    Test: dm_test_eth_rotate
+
+    Error: eth@10004000 address not set.
+
+    Error: eth@10004000 address not set.
+    Using eth@10002000 device
+
+    Error: eth@10004000 address not set.
+
+    Error: eth@10004000 address not set.
+    Using eth@10004000 device
     Test: dm_test_fdt
-    Device 'd-test': seq 3 is in use by 'b-test'
     Test: dm_test_fdt_offset
     Test: dm_test_fdt_pre_reloc
     Test: dm_test_fdt_uclass_seq
-    Device 'd-test': seq 3 is in use by 'b-test'
-    Device 'a-test': seq 0 is in use by 'd-test'
     Test: dm_test_gpio
     extra-gpios: get_value: error: gpio b5 not reserved
     Test: dm_test_gpio_anon
     Test: dm_test_gpio_copy
     Test: dm_test_gpio_leak
     extra-gpios: get_value: error: gpio b5 not reserved
+    Test: dm_test_gpio_phandles
     Test: dm_test_gpio_requestf
+    Test: dm_test_i2c_bytewise
+    Test: dm_test_i2c_find
+    Test: dm_test_i2c_offset
+    Test: dm_test_i2c_offset_len
+    Test: dm_test_i2c_probe_empty
+    Test: dm_test_i2c_read_write
+    Test: dm_test_i2c_speed
     Test: dm_test_leak
     Test: dm_test_lifecycle
+    Test: dm_test_net_retry
+    Using eth@10004000 device
+    Using eth@10002000 device
+    Using eth@10004000 device
     Test: dm_test_operations
     Test: dm_test_ordering
+    Test: dm_test_pci_base
+    Test: dm_test_pci_swapcase
     Test: dm_test_platdata
     Test: dm_test_pre_reloc
     Test: dm_test_remove
     Test: dm_test_spi_find
     Invalid chip select 0:0 (err=-19)
     SF: Failed to get idcodes
-    Device 'name-emul': seq 0 is in use by 'name-emul'
     SF: Detected M25P16 with page size 256 Bytes, erase size 64 KiB, total 2 MiB
     Test: dm_test_spi_flash
     2097152 bytes written in 0 ms
@@ -150,6 +189,9 @@ You should see something like this:
     SF: Detected M25P16 with page size 256 Bytes, erase size 64 KiB, total 2 MiB
     Test: dm_test_uclass
     Test: dm_test_uclass_before_ready
+    Test: dm_test_usb_base
+    Test: dm_test_usb_flash
+    USB-1:   scanning bus 1 for devices... 2 USB Device(s) found
     Failures: 0
 
 
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index 75d182d27f7e14bf41a962337d70cf9df5f03dee..2861b4307955854ef1b0b5f51b7beedffd7e4fa9 100644
--- a/drivers/core/Kconfig
+++ b/drivers/core/Kconfig
@@ -46,3 +46,12 @@ config DM_STDIO
 	  Normally serial drivers register with stdio so that they can be used
 	  as normal output devices. In SPL we don't normally use stdio, so
 	  we can omit this feature.
+
+config DM_SEQ_ALIAS
+	bool "Support numbered aliases in device tree"
+	depends on DM
+	default y
+	help
+	  Most boards will have a '/aliases' node containing the path to
+	  numbered devices (e.g. serial0 = &serial0). This feature can be
+	  disabled if it is not required, to save code space in SPL.
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
index 7fee1c001e4310d8b84355a518a69f93118b4d4d..6a16b4f690fd9392eb7637799ac777b3befc2e1f 100644
--- a/drivers/core/device-remove.c
+++ b/drivers/core/device-remove.c
@@ -92,6 +92,10 @@ int device_unbind(struct udevice *dev)
 		free(dev->platdata);
 		dev->platdata = NULL;
 	}
+	if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
+		free(dev->uclass_platdata);
+		dev->uclass_platdata = NULL;
+	}
 	if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
 		free(dev->parent_platdata);
 		dev->parent_platdata = NULL;
diff --git a/drivers/core/device.c b/drivers/core/device.c
index ccaa99ca63fa8176ab2601f4902310c95fd03a87..3b77d231d344d05d24047593f77bf9bbcfb49bc0 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -30,7 +30,7 @@ int device_bind(struct udevice *parent, const struct driver *drv,
 {
 	struct udevice *dev;
 	struct uclass *uc;
-	int ret = 0;
+	int size, ret = 0;
 
 	*devp = NULL;
 	if (!name)
@@ -56,21 +56,23 @@ int device_bind(struct udevice *parent, const struct driver *drv,
 
 	dev->seq = -1;
 	dev->req_seq = -1;
-#ifdef CONFIG_OF_CONTROL
-	/*
-	 * Some devices, such as a SPI bus, I2C bus and serial ports are
-	 * numbered using aliases.
-	 *
-	 * This is just a 'requested' sequence, and will be
-	 * resolved (and ->seq updated) when the device is probed.
-	 */
-	if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
-		if (uc->uc_drv->name && of_offset != -1) {
-			fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name,
-					     of_offset, &dev->req_seq);
+	if (IS_ENABLED(CONFIG_OF_CONTROL) && IS_ENABLED(CONFIG_DM_SEQ_ALIAS)) {
+		/*
+		* Some devices, such as a SPI bus, I2C bus and serial ports
+		* are numbered using aliases.
+		*
+		* This is just a 'requested' sequence, and will be
+		* resolved (and ->seq updated) when the device is probed.
+		*/
+		if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
+			if (uc->uc_drv->name && of_offset != -1) {
+				fdtdec_get_alias_seq(gd->fdt_blob,
+						uc->uc_drv->name, of_offset,
+						&dev->req_seq);
+			}
 		}
 	}
-#endif
+
 	if (!dev->platdata && drv->platdata_auto_alloc_size) {
 		dev->flags |= DM_FLAG_ALLOC_PDATA;
 		dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
@@ -79,9 +81,19 @@ int device_bind(struct udevice *parent, const struct driver *drv,
 			goto fail_alloc1;
 		}
 	}
-	if (parent) {
-		int size = parent->driver->per_child_platdata_auto_alloc_size;
 
+	size = uc->uc_drv->per_device_platdata_auto_alloc_size;
+	if (size) {
+		dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA;
+		dev->uclass_platdata = calloc(1, size);
+		if (!dev->uclass_platdata) {
+			ret = -ENOMEM;
+			goto fail_alloc2;
+		}
+	}
+
+	if (parent) {
+		size = parent->driver->per_child_platdata_auto_alloc_size;
 		if (!size) {
 			size = parent->uclass->uc_drv->
 					per_child_platdata_auto_alloc_size;
@@ -91,7 +103,7 @@ int device_bind(struct udevice *parent, const struct driver *drv,
 			dev->parent_platdata = calloc(1, size);
 			if (!dev->parent_platdata) {
 				ret = -ENOMEM;
-				goto fail_alloc2;
+				goto fail_alloc3;
 			}
 		}
 	}
@@ -123,21 +135,32 @@ int device_bind(struct udevice *parent, const struct driver *drv,
 	return 0;
 
 fail_child_post_bind:
-	if (drv->unbind && drv->unbind(dev)) {
-		dm_warn("unbind() method failed on dev '%s' on error path\n",
-			dev->name);
+	if (IS_ENABLED(DM_DEVICE_REMOVE)) {
+		if (drv->unbind && drv->unbind(dev)) {
+			dm_warn("unbind() method failed on dev '%s' on error path\n",
+				dev->name);
+		}
 	}
 
 fail_bind:
-	if (uclass_unbind_device(dev)) {
-		dm_warn("Failed to unbind dev '%s' on error path\n",
-			dev->name);
+	if (IS_ENABLED(DM_DEVICE_REMOVE)) {
+		if (uclass_unbind_device(dev)) {
+			dm_warn("Failed to unbind dev '%s' on error path\n",
+				dev->name);
+		}
 	}
 fail_uclass_bind:
-	list_del(&dev->sibling_node);
-	if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
-		free(dev->parent_platdata);
-		dev->parent_platdata = NULL;
+	if (IS_ENABLED(DM_DEVICE_REMOVE)) {
+		list_del(&dev->sibling_node);
+		if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
+			free(dev->parent_platdata);
+			dev->parent_platdata = NULL;
+		}
+	}
+fail_alloc3:
+	if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
+		free(dev->uclass_platdata);
+		dev->uclass_platdata = NULL;
 	}
 fail_alloc2:
 	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
@@ -314,6 +337,16 @@ void *dev_get_parent_platdata(struct udevice *dev)
 	return dev->parent_platdata;
 }
 
+void *dev_get_uclass_platdata(struct udevice *dev)
+{
+	if (!dev) {
+		dm_warn("%s: null device", __func__);
+		return NULL;
+	}
+
+	return dev->uclass_platdata;
+}
+
 void *dev_get_priv(struct udevice *dev)
 {
 	if (!dev) {
@@ -474,11 +507,27 @@ ulong dev_get_driver_data(struct udevice *dev)
 	return dev->driver_data;
 }
 
+const void *dev_get_driver_ops(struct udevice *dev)
+{
+	if (!dev || !dev->driver->ops)
+		return NULL;
+
+	return dev->driver->ops;
+}
+
 enum uclass_id device_get_uclass_id(struct udevice *dev)
 {
 	return dev->uclass->uc_drv->id;
 }
 
+const char *dev_get_uclass_name(struct udevice *dev)
+{
+	if (!dev)
+		return NULL;
+
+	return dev->uclass->uc_drv->name;
+}
+
 #ifdef CONFIG_OF_CONTROL
 fdt_addr_t dev_get_addr(struct udevice *dev)
 {
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 9b5c6bb10cb13d1648c344970838d967494e26a4..12d046051fdb3af8065adcd18d5bc017d6dc25b9 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -197,13 +197,15 @@ int dm_init_and_scan(bool pre_reloc_only)
 		debug("dm_scan_platdata() failed: %d\n", ret);
 		return ret;
 	}
-#ifdef CONFIG_OF_CONTROL
-	ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);
-	if (ret) {
-		debug("dm_scan_fdt() failed: %d\n", ret);
-		return ret;
+
+	if (OF_CONTROL) {
+		ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);
+		if (ret) {
+			debug("dm_scan_fdt() failed: %d\n", ret);
+			return ret;
+		}
 	}
-#endif
+
 	ret = dm_scan_other(pre_reloc_only);
 	if (ret)
 		return ret;
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 98c15e585dfa81d36313899ffacdcaee9ea57ca6..04e939d6c13bc216ec31ae311a9ceb659d027a25 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -99,10 +99,18 @@ fail_mem:
 int uclass_destroy(struct uclass *uc)
 {
 	struct uclass_driver *uc_drv;
-	struct udevice *dev, *tmp;
+	struct udevice *dev;
 	int ret;
 
-	list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) {
+	/*
+	 * We cannot use list_for_each_entry_safe() here. If a device in this
+	 * uclass has a child device also in this uclass, it will be also be
+	 * unbound (by the recursion in the call to device_unbind() below).
+	 * We can loop until the list is empty.
+	 */
+	while (!list_empty(&uc->dev_head)) {
+		dev = list_first_entry(&uc->dev_head, struct udevice,
+				       uclass_node);
 		ret = device_remove(dev);
 		if (ret)
 			return ret;
@@ -156,6 +164,60 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
 	return -ENODEV;
 }
 
+int uclass_find_first_device(enum uclass_id id, struct udevice **devp)
+{
+	struct uclass *uc;
+	int ret;
+
+	*devp = NULL;
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+	if (list_empty(&uc->dev_head))
+		return 0;
+
+	*devp = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
+
+	return 0;
+}
+
+int uclass_find_next_device(struct udevice **devp)
+{
+	struct udevice *dev = *devp;
+
+	*devp = NULL;
+	if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
+		return 0;
+
+	*devp = list_entry(dev->uclass_node.next, struct udevice, uclass_node);
+
+	return 0;
+}
+
+int uclass_find_device_by_name(enum uclass_id id, const char *name,
+			       struct udevice **devp)
+{
+	struct uclass *uc;
+	struct udevice *dev;
+	int ret;
+
+	*devp = NULL;
+	if (!name)
+		return -EINVAL;
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+
+	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+		if (!strncmp(dev->name, name, strlen(name))) {
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
 int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
 			      bool find_req_seq, struct udevice **devp)
 {
@@ -209,17 +271,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node,
 	return -ENODEV;
 }
 
-/**
- * uclass_get_device_tail() - handle the end of a get_device call
- *
- * This handles returning an error or probing a device as needed.
- *
- * @dev: Device that needs to be probed
- * @ret: Error to return. If non-zero then the device is not probed
- * @devp: Returns the value of 'dev' if there is no error
- * @return ret, if non-zero, else the result of the device_probe() call
- */
-static int uclass_get_device_tail(struct udevice *dev, int ret,
+int uclass_get_device_tail(struct udevice *dev, int ret,
 				  struct udevice **devp)
 {
 	if (ret)
@@ -244,6 +296,17 @@ int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
 	return uclass_get_device_tail(dev, ret, devp);
 }
 
+int uclass_get_device_by_name(enum uclass_id id, const char *name,
+			      struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret;
+
+	*devp = NULL;
+	ret = uclass_find_device_by_name(id, name, &dev);
+	return uclass_get_device_tail(dev, ret, devp);
+}
+
 int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
 {
 	struct udevice *dev;
@@ -274,24 +337,12 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node,
 
 int uclass_first_device(enum uclass_id id, struct udevice **devp)
 {
-	struct uclass *uc;
 	struct udevice *dev;
 	int ret;
 
 	*devp = NULL;
-	ret = uclass_get(id, &uc);
-	if (ret)
-		return ret;
-	if (list_empty(&uc->dev_head))
-		return 0;
-
-	dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
-	ret = device_probe(dev);
-	if (ret)
-		return ret;
-	*devp = dev;
-
-	return 0;
+	ret = uclass_find_first_device(id, &dev);
+	return uclass_get_device_tail(dev, ret, devp);
 }
 
 int uclass_next_device(struct udevice **devp)
@@ -300,17 +351,8 @@ int uclass_next_device(struct udevice **devp)
 	int ret;
 
 	*devp = NULL;
-	if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
-		return 0;
-
-	dev = list_entry(dev->uclass_node.next, struct udevice,
-			 uclass_node);
-	ret = device_probe(dev);
-	if (ret)
-		return ret;
-	*devp = dev;
-
-	return 0;
+	ret = uclass_find_next_device(&dev);
+	return uclass_get_device_tail(dev, ret, devp);
 }
 
 int uclass_bind_device(struct udevice *dev)
@@ -344,6 +386,7 @@ err:
 	return ret;
 }
 
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int uclass_unbind_device(struct udevice *dev)
 {
 	struct uclass *uc;
@@ -359,6 +402,7 @@ int uclass_unbind_device(struct udevice *dev)
 	list_del(&dev->uclass_node);
 	return 0;
 }
+#endif
 
 int uclass_resolve_seq(struct udevice *dev)
 {
@@ -422,6 +466,7 @@ int uclass_post_probe_device(struct udevice *dev)
 	return 0;
 }
 
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int uclass_pre_remove_device(struct udevice *dev)
 {
 	struct uclass_driver *uc_drv;
@@ -443,3 +488,4 @@ int uclass_pre_remove_device(struct udevice *dev)
 
 	return 0;
 }
+#endif
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 1686a1f951e860757bbc656678fbfdf6088b39b9..54e6f26d38d00a9a5b6781148f9fc1cd2d03b4b2 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -66,6 +66,16 @@ config DEBUG_UART_CLOCK
 	  A default should be provided by your board, but if not you will need
 	  to use the correct value here.
 
+config DEBUG_UART_SHIFT
+	int "UART register shift"
+	depends on DEBUG_UART
+	default 0 if DEBUG_UART
+	help
+	  Some UARTs (notably ns16550) support different register layouts
+	  where the registers are spaced either as bytes, words or some other
+	  value. Use this value to specify the shift to use, where 0=byte
+	  registers, 2=32-bit word registers, etc.
+
 config UNIPHIER_SERIAL
 	bool "UniPhier on-chip UART support"
 	depends on ARCH_UNIPHIER && DM_SERIAL
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index 67b1d60171abcaa0418dbde582d28a702a285db6..fd110b3ddcdf1514a738294ebf4f423bfc254437 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -57,7 +57,7 @@ DECLARE_GLOBAL_DATA_PTR;
 
 #ifdef CONFIG_DM_SERIAL
 
-static inline void serial_out_shift(unsigned char *addr, int shift, int value)
+static inline void serial_out_shift(void *addr, int shift, int value)
 {
 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
 	outb(value, (ulong)addr);
@@ -72,7 +72,7 @@ static inline void serial_out_shift(unsigned char *addr, int shift, int value)
 #endif
 }
 
-static inline int serial_in_shift(unsigned char *addr, int shift)
+static inline int serial_in_shift(void *addr, int shift)
 {
 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
 	return inb((ulong)addr);
@@ -114,9 +114,11 @@ static int ns16550_readb(NS16550_t port, int offset)
 
 /* We can clean these up once everything is moved to driver model */
 #define serial_out(value, addr)	\
-	ns16550_writeb(com_port, addr - (unsigned char *)com_port, value)
+	ns16550_writeb(com_port, \
+		(unsigned char *)addr - (unsigned char *)com_port, value)
 #define serial_in(addr) \
-	ns16550_readb(com_port, addr - (unsigned char *)com_port)
+	ns16550_readb(com_port, \
+		(unsigned char *)addr - (unsigned char *)com_port)
 #endif
 
 static inline int calc_divisor(NS16550_t port, int clock, int baudrate)
@@ -173,7 +175,6 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
 			defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
 	serial_out(0x7, &com_port->mdr1);	/* mode select reset TL16C750*/
 #endif
-	NS16550_setbrg(com_port, 0);
 	serial_out(UART_MCRVAL, &com_port->mcr);
 	serial_out(UART_FCRVAL, &com_port->fcr);
 	if (baud_divisor != -1)
@@ -254,15 +255,20 @@ void debug_uart_init(void)
 	 */
 	baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK,
 				    CONFIG_BAUDRATE);
-
-	serial_out_shift(&com_port->ier, 0, CONFIG_SYS_NS16550_IER);
-	serial_out_shift(&com_port->mcr, 0, UART_MCRVAL);
-	serial_out_shift(&com_port->fcr, 0, UART_FCRVAL);
-
-	serial_out_shift(&com_port->lcr, 0, UART_LCR_BKSE | UART_LCRVAL);
-	serial_out_shift(&com_port->dll, 0, baud_divisor & 0xff);
-	serial_out_shift(&com_port->dlm, 0, (baud_divisor >> 8) & 0xff);
-	serial_out_shift(&com_port->lcr, 0, UART_LCRVAL);
+	baud_divisor = 13;
+	serial_out_shift(&com_port->ier, CONFIG_DEBUG_UART_SHIFT,
+			 CONFIG_SYS_NS16550_IER);
+	serial_out_shift(&com_port->mcr, CONFIG_DEBUG_UART_SHIFT, UART_MCRVAL);
+	serial_out_shift(&com_port->fcr, CONFIG_DEBUG_UART_SHIFT, UART_FCRVAL);
+
+	serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT,
+			 UART_LCR_BKSE | UART_LCRVAL);
+	serial_out_shift(&com_port->dll, CONFIG_DEBUG_UART_SHIFT,
+			 baud_divisor & 0xff);
+	serial_out_shift(&com_port->dlm, CONFIG_DEBUG_UART_SHIFT,
+			 (baud_divisor >> 8) & 0xff);
+	serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT,
+			 UART_LCRVAL);
 }
 
 static inline void _debug_uart_putc(int ch)
@@ -271,7 +277,7 @@ static inline void _debug_uart_putc(int ch)
 
 	while (!(serial_in_shift(&com_port->lsr, 0) & UART_LSR_THRE))
 		;
-	serial_out_shift(&com_port->thr, 0, ch);
+	serial_out_shift(&com_port->thr, CONFIG_DEBUG_UART_SHIFT, ch);
 }
 
 DEBUG_UART_FUNCS
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index b239691efe945ba77d02429243a81186ded32454..b8c2f482288913475af367869b0ab086e6fd5943 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -70,7 +70,7 @@ static void serial_find_console_or_panic(void)
 	if (uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &dev) &&
 	    uclass_get_device(UCLASS_SERIAL, INDEX, &dev) &&
 	    (uclass_first_device(UCLASS_SERIAL, &dev) || !dev))
-		panic("No serial driver found");
+		panic_str("No serial driver found");
 #undef INDEX
 	gd->cur_serial_dev = dev;
 }
diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c
index 280c7080f2aecba6deb8b60c79ca3fc4dd8d8979..baf8bdc857b6452315af9c7efec983dd2a1bc06d 100644
--- a/drivers/usb/emul/sandbox_hub.c
+++ b/drivers/usb/emul/sandbox_hub.c
@@ -32,6 +32,7 @@ static struct usb_string hub_strings[] = {
 	{STRING_MANUFACTURER,	"sandbox"},
 	{STRING_PRODUCT,	"hub"},
 	{STRING_SERIAL,		"2345"},
+	{},
 };
 
 static struct usb_device_descriptor hub_device_desc = {
diff --git a/dts/Kconfig b/dts/Kconfig
index ca5bd6fb46626eafb7b0f15d023d243b79783f93..957f5c7ffad2a2ab6637024169f84bfdc6489f75 100644
--- a/dts/Kconfig
+++ b/dts/Kconfig
@@ -1,9 +1,6 @@
 #
 # Device Tree Control
 #
-# TODO:
-#   This feature is not currently supported for SPL,
-#    but this restriction should be removed in the future.
 
 config SUPPORT_OF_CONTROL
 	bool
@@ -17,6 +14,14 @@ config OF_CONTROL
 	  This feature provides for run-time configuration of U-Boot
 	  via a flattened device tree.
 
+config SPL_DISABLE_OF_CONTROL
+	bool "Disable run-time configuration via Device Tree in SPL"
+	depends on OF_CONTROL
+	help
+	  Some boards use device tree in U-Boot but only have 4KB of SRAM
+	  which is not enough to support device tree. Enable this option to
+	  allow such boards to be supported by U-Boot SPL.
+
 choice
 	prompt "Provider of DTB for DT control"
 	depends on OF_CONTROL
diff --git a/include/config_uncmd_spl.h b/include/config_uncmd_spl.h
index a9106f4f3b7eb072239dbf2545bd4ac2f057a03d..38cb0e8abac9378c375cb4162454b6cc6923b2c8 100644
--- a/include/config_uncmd_spl.h
+++ b/include/config_uncmd_spl.h
@@ -31,6 +31,7 @@
 
 #undef CONFIG_DM_WARN
 #undef CONFIG_DM_DEVICE_REMOVE
+#undef CONFIG_DM_SEQ_ALIAS
 #undef CONFIG_DM_STDIO
 
 #endif /* CONFIG_SPL_BUILD */
diff --git a/include/dm/device.h b/include/dm/device.h
index c11342c33b288f2a2033a9f2178caa26306122fc..18296bb68614b9aeae25c46b5ff3c8306e8a36d1 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -30,8 +30,11 @@ struct driver_info;
 /* DM is responsible for allocating and freeing parent_platdata */
 #define DM_FLAG_ALLOC_PARENT_PDATA	(1 << 3)
 
+/* DM is responsible for allocating and freeing uclass_platdata */
+#define DM_FLAG_ALLOC_UCLASS_PDATA	(1 << 4)
+
 /* Allocate driver private data on a DMA boundary */
-#define DM_FLAG_ALLOC_PRIV_DMA	(1 << 4)
+#define DM_FLAG_ALLOC_PRIV_DMA	(1 << 5)
 
 /**
  * struct udevice - An instance of a driver
@@ -54,6 +57,7 @@ struct driver_info;
  * @name: Name of device, typically the FDT node name
  * @platdata: Configuration data for this device
  * @parent_platdata: The parent bus's configuration data for this device
+ * @uclass_platdata: The uclass's configuration data for this device
  * @of_offset: Device tree node offset for this device (- for none)
  * @driver_data: Driver data word for the entry that matched this device with
  *		its driver
@@ -75,6 +79,7 @@ struct udevice {
 	const char *name;
 	void *platdata;
 	void *parent_platdata;
+	void *uclass_platdata;
 	int of_offset;
 	ulong driver_data;
 	struct udevice *parent;
@@ -209,6 +214,16 @@ void *dev_get_platdata(struct udevice *dev);
  */
 void *dev_get_parent_platdata(struct udevice *dev);
 
+/**
+ * dev_get_uclass_platdata() - Get the uclass platform data for a device
+ *
+ * This checks that dev is not NULL, but no other checks for now
+ *
+ * @dev		Device to check
+ * @return uclass's platform data, or NULL if none
+ */
+void *dev_get_uclass_platdata(struct udevice *dev);
+
 /**
  * dev_get_parentdata() - Get the parent data for a device
  *
@@ -265,6 +280,17 @@ void *dev_get_uclass_priv(struct udevice *dev);
  */
 ulong dev_get_driver_data(struct udevice *dev);
 
+/**
+ * dev_get_driver_ops() - get the device's driver's operations
+ *
+ * This checks that dev is not NULL, and returns the pointer to device's
+ * driver's operations.
+ *
+ * @dev:	Device to check
+ * @return void pointer to driver's operations or NULL for NULL-dev or NULL-ops
+ */
+const void *dev_get_driver_ops(struct udevice *dev);
+
 /*
  * device_get_uclass_id() - return the uclass ID of a device
  *
@@ -273,6 +299,16 @@ ulong dev_get_driver_data(struct udevice *dev);
  */
 enum uclass_id device_get_uclass_id(struct udevice *dev);
 
+/*
+ * dev_get_uclass_name() - return the uclass name of a device
+ *
+ * This checks that dev is not NULL.
+ *
+ * @dev:	Device to check
+ * @return  pointer to the uclass name for the device
+ */
+const char *dev_get_uclass_name(struct udevice *dev);
+
 /**
  * device_get_child() - Get the child of a device by index
  *
diff --git a/include/dm/test.h b/include/dm/test.h
index 9c4b8d3e5737ca9717f07559f360a5d821796a9e..f03fbcb1cdc0b1a1485fc2951ba1bad93dbdf5a0 100644
--- a/include/dm/test.h
+++ b/include/dm/test.h
@@ -98,6 +98,26 @@ struct dm_test_parent_data {
 	int flag;
 };
 
+/* Test values for test device's uclass platform data */
+enum {
+	TEST_UC_PDATA_INTVAL1 = 2,
+	TEST_UC_PDATA_INTVAL2 = 334,
+	TEST_UC_PDATA_INTVAL3 = 789452,
+};
+
+/**
+ * struct dm_test_uclass_platda - uclass's information on each device
+ *
+ * @intval1: set to TEST_UC_PDATA_INTVAL1 in .post_bind method of test uclass
+ * @intval2: set to TEST_UC_PDATA_INTVAL2 in .post_bind method of test uclass
+ * @intval3: set to TEST_UC_PDATA_INTVAL3 in .post_bind method of test uclass
+ */
+struct dm_test_perdev_uc_pdata {
+	int intval1;
+	int intval2;
+	int intval3;
+};
+
 /*
  * Operation counts for the test driver, used to check that each method is
  * called correctly
diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h
index ae2a93d7d4da7a806826843296d8c2d0d67ce20c..9b68508667123e0261691a1d4dbbf4ec7c91dffd 100644
--- a/include/dm/uclass-internal.h
+++ b/include/dm/uclass-internal.h
@@ -10,19 +10,94 @@
 #ifndef _DM_UCLASS_INTERNAL_H
 #define _DM_UCLASS_INTERNAL_H
 
+/**
+ * uclass_get_device_tail() - handle the end of a get_device call
+ *
+ * This handles returning an error or probing a device as needed.
+ *
+ * @dev: Device that needs to be probed
+ * @ret: Error to return. If non-zero then the device is not probed
+ * @devp: Returns the value of 'dev' if there is no error
+ * @return ret, if non-zero, else the result of the device_probe() call
+ */
+int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp);
+
 /**
  * uclass_find_device() - Return n-th child of uclass
  * @id:		Id number of the uclass
  * @index:	Position of the child in uclass's list
  * #devp:	Returns pointer to device, or NULL on error
  *
- * The device is not prepared for use - this is an internal function
+ * The device is not prepared for use - this is an internal function.
+ * The function uclass_get_device_tail() can be used to probe the device.
  *
  * @return the uclass pointer of a child at the given index or
  * return NULL on error.
  */
 int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);
 
+/**
+ * uclass_find_first_device() - Return the first device in a uclass
+ * @id:		Id number of the uclass
+ * #devp:	Returns pointer to device, or NULL on error
+ *
+ * The device is not prepared for use - this is an internal function.
+ * The function uclass_get_device_tail() can be used to probe the device.
+ *
+ * @return 0 if OK (found or not found), -1 on error
+ */
+int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
+
+/**
+ * uclass_find_next_device() - Return the next device in a uclass
+ * @devp: On entry, pointer to device to lookup. On exit, returns pointer
+ * to the next device in the same uclass, or NULL if none
+ *
+ * The device is not prepared for use - this is an internal function.
+ * The function uclass_get_device_tail() can be used to probe the device.
+ *
+ * @return 0 if OK (found or not found), -1 on error
+ */
+int uclass_find_next_device(struct udevice **devp);
+
+/**
+ * uclass_find_device_by_name() - Find uclass device based on ID and name
+ *
+ * This searches for a device with the exactly given name.
+ *
+ * The device is NOT probed, it is merely returned.
+ *
+ * @id: ID to look up
+ * @name: name of a device to find
+ * @devp: Returns pointer to device (the first one with the name)
+ * @return 0 if OK, -ve on error
+ */
+int uclass_find_device_by_name(enum uclass_id id, const char *name,
+			       struct udevice **devp);
+
+/**
+ * uclass_find_device_by_seq() - Find uclass device based on ID and sequence
+ *
+ * This searches for a device with the given seq or req_seq.
+ *
+ * For seq, if an active device has this sequence it will be returned.
+ * If there is no such device then this will return -ENODEV.
+ *
+ * For req_seq, if a device (whether activated or not) has this req_seq
+ * value, that device will be returned. This is a strong indication that
+ * the device will receive that sequence when activated.
+ *
+ * The device is NOT probed, it is merely returned.
+ *
+ * @id: ID to look up
+ * @seq_or_req_seq: Sequence number to find (0=first)
+ * @find_req_seq: true to find req_seq, false to find seq
+ * @devp: Returns pointer to device (there is only one per for each seq)
+ * @return 0 if OK, -ve on error
+ */
+int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
+			      bool find_req_seq, struct udevice **devp);
+
 /**
  * uclass_bind_device() - Associate device with a uclass
  *
@@ -41,7 +116,11 @@ int uclass_bind_device(struct udevice *dev);
  * @dev:	Pointer to the device
  * #return 0 on success, -ve on error
  */
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int uclass_unbind_device(struct udevice *dev);
+#else
+static inline int uclass_unbind_device(struct udevice *dev) { return 0; }
+#endif
 
 /**
  * uclass_pre_probe_device() - Deal with a device that is about to be probed
@@ -74,7 +153,11 @@ int uclass_post_probe_device(struct udevice *dev);
  * @dev:	Pointer to the device
  * #return 0 on success, -ve on error
  */
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int uclass_pre_remove_device(struct udevice *dev);
+#else
+static inline int uclass_pre_remove_device(struct udevice *dev) { return 0; }
+#endif
 
 /**
  * uclass_find() - Find uclass by its id
@@ -94,27 +177,4 @@ struct uclass *uclass_find(enum uclass_id key);
  */
 int uclass_destroy(struct uclass *uc);
 
-/**
- * uclass_find_device_by_seq() - Find uclass device based on ID and sequence
- *
- * This searches for a device with the given seq or req_seq.
- *
- * For seq, if an active device has this sequence it will be returned.
- * If there is no such device then this will return -ENODEV.
- *
- * For req_seq, if a device (whether activated or not) has this req_seq
- * value, that device will be returned. This is a strong indication that
- * the device will receive that sequence when activated.
- *
- * The device is NOT probed, it is merely returned.
- *
- * @id: ID to look up
- * @seq_or_req_seq: Sequence number to find (0=first)
- * @find_req_seq: true to find req_seq, false to find seq
- * @devp: Returns pointer to device (there is only one per for each seq)
- * @return 0 if OK, -ve on error
- */
-int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
-			      bool find_req_seq, struct udevice **devp);
-
 #endif
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index d57d80425986f3c993a42cc8a8cdf35e301b7bf6..4cfc0df84c21bac942c633279ea358e968527f53 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -65,6 +65,9 @@ struct udevice;
  * @per_device_auto_alloc_size: Each device can hold private data owned
  * by the uclass. If required this will be automatically allocated if this
  * value is non-zero.
+ * @per_device_platdata_auto_alloc_size: Each device can hold platform data
+ * owned by the uclass as 'dev->uclass_platdata'. If the value is non-zero,
+ * then this will be automatically allocated.
  * @per_child_auto_alloc_size: Each child device (of a parent in this
  * uclass) can hold parent data for the device/uclass. This value is only
  * used as a falback if this member is 0 in the driver.
@@ -90,6 +93,7 @@ struct uclass_driver {
 	int (*destroy)(struct uclass *class);
 	int priv_auto_alloc_size;
 	int per_device_auto_alloc_size;
+	int per_device_platdata_auto_alloc_size;
 	int per_child_auto_alloc_size;
 	int per_child_platdata_auto_alloc_size;
 	const void *ops;
@@ -125,6 +129,21 @@ int uclass_get(enum uclass_id key, struct uclass **ucp);
  */
 int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);
 
+/**
+ * uclass_get_device_by_name() - Get a uclass device by it's name
+ *
+ * This searches the devices in the uclass for one with the exactly given name.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @id: ID to look up
+ * @name: name of a device to get
+ * @devp: Returns pointer to device (the first one with the name)
+ * @return 0 if OK, -ve on error
+ */
+int uclass_get_device_by_name(enum uclass_id id, const char *name,
+			      struct udevice **devp);
+
 /**
  * uclass_get_device_by_seq() - Get a uclass device based on an ID and sequence
  *
diff --git a/include/fdtdec.h b/include/fdtdec.h
index d14e06ababd543e2132c35f2414a9b037a47229c..659047097a1001ca5f007e55b16343ddc214ca78 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -41,6 +41,16 @@ struct fdt_memory {
 	fdt_addr_t end;
 };
 
+#ifdef CONFIG_OF_CONTROL
+# if defined(CONFIG_SPL_BUILD) && defined(SPL_DISABLE_OF_CONTROL)
+#  define OF_CONTROL 0
+# else
+#  define OF_CONTROL 1
+# endif
+#else
+# define OF_CONTROL 0
+#endif
+
 /*
  * Information about a resource. start is the first address of the resource
  * and end is the last address (inclusive). The length of the resource will
@@ -793,4 +803,10 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property,
 int fdtdec_decode_memory_region(const void *blob, int node,
 				const char *mem_type, const char *suffix,
 				fdt_addr_t *basep, fdt_size_t *sizep);
+
+/**
+ * Set up the device tree ready for use
+ */
+int fdtdec_setup(void);
+
 #endif
diff --git a/include/malloc.h b/include/malloc.h
index 5df634873f147f93fb444b9621abf07e1e6e21e1..f4da9e6dddb67e2ccb78464558caf5aa85f5ce74 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -906,6 +906,9 @@ void *realloc_simple(void *ptr, size_t size);
 
 #endif
 
+/* Set up pre-relocation malloc() ready for use */
+int initf_malloc(void);
+
 /* Public routines */
 
 /* Simple versions which can be used when space is tight */
diff --git a/include/vsprintf.h b/include/vsprintf.h
index 5624482d573b947fb3d533562bda755d75995853..09c8abd951d09c48ea8b937542549c25efb8ad79 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -39,9 +39,32 @@ int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
 unsigned long long simple_strtoull(const char *cp, char **endp,
 					unsigned int base);
 long simple_strtol(const char *cp, char **endp, unsigned int base);
+
+/**
+ * panic() - Print a message and reset/hang
+ *
+ * Prints a message on the console(s) and then resets. If CONFIG_PANIC_HANG is
+ * defined, then it will hang instead of reseting.
+ *
+ * @param fmt:	printf() format string for message, which should not include
+ *		\n, followed by arguments
+ */
 void panic(const char *fmt, ...)
 		__attribute__ ((format (__printf__, 1, 2), noreturn));
 
+/**
+ * panic_str() - Print a message and reset/hang
+ *
+ * Prints a message on the console(s) and then resets. If CONFIG_PANIC_HANG is
+ * defined, then it will hang instead of reseting.
+ *
+ * This function can be used instead of panic() when your board does not
+ * already use printf(), * to keep code size small.
+ *
+ * @param fmt:	string to display, which should not include \n
+ */
+void panic_str(const char *str) __attribute__ ((noreturn));
+
 /**
  * Format a string and place it in a buffer
  *
diff --git a/lib/Makefile b/lib/Makefile
index 07d175f45e87987c659404aba8e5774bccaf2860..97ed398ade42f1b0eb6ef233202b56fa3068a668 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -44,6 +44,12 @@ obj-$(CONFIG_BITREVERSE) += bitrev.o
 obj-y += list_sort.o
 endif
 
+ifndef CONFIG_SPL_DISABLE_OF_CONTROL
+obj-$(CONFIG_OF_LIBFDT) += libfdt/
+obj-$(CONFIG_OF_CONTROL) += fdtdec_common.o
+obj-$(CONFIG_OF_CONTROL) += fdtdec.o
+endif
+
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16.o
 obj-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 331eae2ce1e093f04efa6ba055c485efc9a3c1f1..80b897a21cd69a88ce5d3fe1398d23da11339f67 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -9,6 +9,7 @@
 #include <serial.h>
 #include <libfdt.h>
 #include <fdtdec.h>
+#include <asm/sections.h>
 #include <linux/ctype.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -565,9 +566,11 @@ int fdtdec_prepare_fdt(void)
 {
 	if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
 	    fdt_check_header(gd->fdt_blob)) {
-		printf("No valid FDT found - please append one to U-Boot "
-			"binary, use u-boot-dtb.bin or define "
-			"CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
+#ifdef CONFIG_SPL_BUILD
+		puts("Missing DTB\n");
+#else
+		puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
+#endif
 		return -1;
 	}
 	return 0;
@@ -1035,4 +1038,34 @@ int fdtdec_decode_memory_region(const void *blob, int config_node,
 
 	return 0;
 }
+
+int fdtdec_setup(void)
+{
+#ifdef CONFIG_OF_CONTROL
+# ifdef CONFIG_OF_EMBED
+	/* Get a pointer to the FDT */
+	gd->fdt_blob = __dtb_dt_begin;
+# elif defined CONFIG_OF_SEPARATE
+#  ifdef CONFIG_SPL_BUILD
+	/* FDT is at end of BSS */
+	gd->fdt_blob = (ulong *)&__bss_end;
+#  else
+	/* FDT is at end of image */
+	gd->fdt_blob = (ulong *)&_end;
+#endif
+# elif defined(CONFIG_OF_HOSTFILE)
+	if (sandbox_read_fdt_from_file()) {
+		puts("Failed to read control FDT\n");
+		return -1;
+	}
+# endif
+# ifndef CONFIG_SPL_BUILD
+	/* Allow the early environment to override the fdt address */
+	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
+						(uintptr_t)gd->fdt_blob);
+# endif
 #endif
+	return fdtdec_prepare_fdt();
+}
+
+#endif /* !USE_HOSTCC */
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index e0f264850f7fa75bc6cbeaad2537d3fffc4a318d..bedc865240de0dd7a2d50fa9be5a73dce5084b7b 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -842,13 +842,11 @@ int sprintf(char *buf, const char *fmt, ...)
 	return i;
 }
 
-void panic(const char *fmt, ...)
+static void panic_finish(void) __attribute__ ((noreturn));
+
+static void panic_finish(void)
 {
-	va_list args;
-	va_start(args, fmt);
-	vprintf(fmt, args);
 	putc('\n');
-	va_end(args);
 #if defined(CONFIG_PANIC_HANG)
 	hang();
 #else
@@ -859,6 +857,21 @@ void panic(const char *fmt, ...)
 		;
 }
 
+void panic_str(const char *str)
+{
+	puts(str);
+	panic_finish();
+}
+
+void panic(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+	panic_finish();
+}
+
 void __assert_fail(const char *assertion, const char *file, unsigned line,
 		   const char *function)
 {
diff --git a/test/dm/core.c b/test/dm/core.c
index 990d390d0150da5edb8b52a6e5887f1d94e68dc4..91be1e5d4382d827cf1be47214f5e9d580bc19e9 100644
--- a/test/dm/core.c
+++ b/test/dm/core.c
@@ -129,6 +129,61 @@ static int dm_test_autobind(struct dm_test_state *dms)
 }
 DM_TEST(dm_test_autobind, 0);
 
+/* Test that binding with uclass platdata allocation occurs correctly */
+static int dm_test_autobind_uclass_pdata_alloc(struct dm_test_state *dms)
+{
+	struct dm_test_perdev_uc_pdata *uc_pdata;
+	struct udevice *dev;
+	struct uclass *uc;
+
+	ut_assertok(uclass_get(UCLASS_TEST, &uc));
+	ut_assert(uc);
+
+	/**
+	 * Test if test uclass driver requires allocation for the uclass
+	 * platform data and then check the dev->uclass_platdata pointer.
+	 */
+	ut_assert(uc->uc_drv->per_device_platdata_auto_alloc_size);
+
+	for (uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		ut_assert(dev);
+
+		uc_pdata = dev_get_uclass_platdata(dev);
+		ut_assert(uc_pdata);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_autobind_uclass_pdata_alloc, DM_TESTF_SCAN_PDATA);
+
+/* Test that binding with uclass platdata setting occurs correctly */
+static int dm_test_autobind_uclass_pdata_valid(struct dm_test_state *dms)
+{
+	struct dm_test_perdev_uc_pdata *uc_pdata;
+	struct udevice *dev;
+
+	/**
+	 * In the test_postbind() method of test uclass driver, the uclass
+	 * platform data should be set to three test int values - test it.
+	 */
+	for (uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		ut_assert(dev);
+
+		uc_pdata = dev_get_uclass_platdata(dev);
+		ut_assert(uc_pdata);
+		ut_assert(uc_pdata->intval1 == TEST_UC_PDATA_INTVAL1);
+		ut_assert(uc_pdata->intval2 == TEST_UC_PDATA_INTVAL2);
+		ut_assert(uc_pdata->intval3 == TEST_UC_PDATA_INTVAL3);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_autobind_uclass_pdata_valid, DM_TESTF_SCAN_PDATA);
+
 /* Test that autoprobe finds all the expected devices */
 static int dm_test_autoprobe(struct dm_test_state *dms)
 {
@@ -596,14 +651,130 @@ static int dm_test_uclass_before_ready(struct dm_test_state *dms)
 
 	ut_assertok(uclass_get(UCLASS_TEST, &uc));
 
-	memset(gd, '\0', sizeof(*gd));
+	gd->dm_root = NULL;
+	gd->dm_root_f = NULL;
+	memset(&gd->uclass_root, '\0', sizeof(gd->uclass_root));
+
 	ut_asserteq_ptr(NULL, uclass_find(UCLASS_TEST));
 
 	return 0;
 }
-
 DM_TEST(dm_test_uclass_before_ready, 0);
 
+static int dm_test_uclass_devices_find(struct dm_test_state *dms)
+{
+	struct udevice *dev;
+	int ret;
+
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assert(dev);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_uclass_devices_find, DM_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_devices_find_by_name(struct dm_test_state *dms)
+{
+	struct udevice *finddev;
+	struct udevice *testdev;
+	int findret, ret;
+
+	/*
+	 * For each test device found in fdt like: "a-test", "b-test", etc.,
+	 * use its name and try to find it by uclass_find_device_by_name().
+	 * Then, on success check if:
+	 * - current 'testdev' name is equal to the returned 'finddev' name
+	 * - current 'testdev' pointer is equal to the returned 'finddev'
+	 *
+	 * We assume that, each uclass's device name is unique, so if not, then
+	 * this will fail on checking condition: testdev == finddev, since the
+	 * uclass_find_device_by_name(), returns the first device by given name.
+	*/
+	for (ret = uclass_find_first_device(UCLASS_TEST_FDT, &testdev);
+	     testdev;
+	     ret = uclass_find_next_device(&testdev)) {
+		ut_assertok(ret);
+		ut_assert(testdev);
+
+		findret = uclass_find_device_by_name(UCLASS_TEST_FDT,
+						     testdev->name,
+						     &finddev);
+
+		ut_assertok(findret);
+		ut_assert(testdev);
+		ut_asserteq_str(testdev->name, finddev->name);
+		ut_asserteq_ptr(testdev, finddev);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_uclass_devices_find_by_name, DM_TESTF_SCAN_FDT);
+
+static int dm_test_uclass_devices_get(struct dm_test_state *dms)
+{
+	struct udevice *dev;
+	int ret;
+
+	for (ret = uclass_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assert(dev);
+		ut_assert(device_active(dev));
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_uclass_devices_get, DM_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_devices_get_by_name(struct dm_test_state *dms)
+{
+	struct udevice *finddev;
+	struct udevice *testdev;
+	int ret, findret;
+
+	/*
+	 * For each test device found in fdt like: "a-test", "b-test", etc.,
+	 * use its name and try to get it by uclass_get_device_by_name().
+	 * On success check if:
+	 * - returned finddev' is active
+	 * - current 'testdev' name is equal to the returned 'finddev' name
+	 * - current 'testdev' pointer is equal to the returned 'finddev'
+	 *
+	 * We asserts that the 'testdev' is active on each loop entry, so we
+	 * could be sure that the 'finddev' is activated too, but for sure
+	 * we check it again.
+	 *
+	 * We assume that, each uclass's device name is unique, so if not, then
+	 * this will fail on checking condition: testdev == finddev, since the
+	 * uclass_get_device_by_name(), returns the first device by given name.
+	*/
+	for (ret = uclass_first_device(UCLASS_TEST_FDT, &testdev);
+	     testdev;
+	     ret = uclass_next_device(&testdev)) {
+		ut_assertok(ret);
+		ut_assert(testdev);
+		ut_assert(device_active(testdev));
+
+		findret = uclass_get_device_by_name(UCLASS_TEST_FDT,
+						    testdev->name,
+						    &finddev);
+
+		ut_assertok(findret);
+		ut_assert(finddev);
+		ut_assert(device_active(finddev));
+		ut_asserteq_str(testdev->name, finddev->name);
+		ut_asserteq_ptr(testdev, finddev);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_uclass_devices_get_by_name, DM_TESTF_SCAN_FDT);
+
 static int dm_test_device_get_uclass_id(struct dm_test_state *dms)
 {
 	struct udevice *dev;
diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c
index 7cb37f70c785c9df2e7f238f67532eeb43d4ec5c..4ae75ef7640c4382958805c31657f3c43850c6e4 100644
--- a/test/dm/test-uclass.c
+++ b/test/dm/test-uclass.c
@@ -30,9 +30,18 @@ int test_ping(struct udevice *dev, int pingval, int *pingret)
 
 static int test_post_bind(struct udevice *dev)
 {
+	struct dm_test_perdev_uc_pdata *uc_pdata;
+
 	dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++;
 	ut_assert(!device_active(dev));
 
+	uc_pdata = dev_get_uclass_platdata(dev);
+	ut_assert(uc_pdata);
+
+	uc_pdata->intval1 = TEST_UC_PDATA_INTVAL1;
+	uc_pdata->intval2 = TEST_UC_PDATA_INTVAL2;
+	uc_pdata->intval3 = TEST_UC_PDATA_INTVAL3;
+
 	return 0;
 }
 
@@ -115,4 +124,6 @@ UCLASS_DRIVER(test) = {
 	.destroy	= test_destroy,
 	.priv_auto_alloc_size	= sizeof(struct dm_test_uclass_priv),
 	.per_device_auto_alloc_size = sizeof(struct dm_test_uclass_perdev_priv),
+	.per_device_platdata_auto_alloc_size =
+					sizeof(struct dm_test_perdev_uc_pdata),
 };