diff --git a/README b/README
index f52a5f7f64d78bb87aed645598860843154701de..e7cd1bcb436dff45feb2be387eaee30323cc15ae 100644
--- a/README
+++ b/README
@@ -623,6 +623,120 @@ The following options need to be configured:
 		exists, unlike the similar options in the Linux kernel. Do not
 		set these options unless they apply!
 
+- Driver Model
+		Driver model is a new framework for devices in U-Boot
+		introduced in early 2014. U-Boot is being progressively
+		moved over to this. It offers a consistent device structure,
+		supports grouping devices into classes and has built-in
+		handling of platform data and device tree.
+
+		To enable transition to driver model in a relatively
+		painful fashion, each subsystem can be independently
+		switched between the legacy/ad-hoc approach and the new
+		driver model using the options below. Also, many uclass
+		interfaces include compatibility features which may be
+		removed once the conversion of that subsystem is complete.
+		As a result, the API provided by the subsystem may in fact
+		not change with driver model.
+
+		See doc/driver-model/README.txt for more information.
+
+		CONFIG_DM
+
+		Enable driver model. This brings in the core support,
+		including scanning of platform data on start-up. If
+		CONFIG_OF_CONTROL is enabled, the device tree will be
+		scanned also when available.
+
+		CONFIG_CMD_DM
+
+		Enable driver model test commands. These allow you to print
+		out the driver model tree and the uclasses.
+
+		CONFIG_DM_DEMO
+
+		Enable some demo devices and the 'demo' command. These are
+		really only useful for playing around while trying to
+		understand driver model in sandbox.
+
+		CONFIG_SPL_DM
+
+		Enable driver model in SPL. You will need to provide a
+		suitable malloc() implementation. If you are not using the
+		full malloc() enabled by CONFIG_SYS_SPL_MALLOC_START,
+		consider using CONFIG_SYS_MALLOC_SIMPLE. In that case you
+		must provide CONFIG_SYS_MALLOC_F_LEN to set the size.
+		In most cases driver model will only allocate a few uclasses
+		and devices in SPL, so 1KB should be enable. See
+		CONFIG_SYS_MALLOC_F_LEN for more details on how to enable
+		it.
+
+		CONFIG_DM_SERIAL
+
+		Enable driver model for serial. This replaces
+		drivers/serial/serial.c with the serial uclass, which
+		implements serial_putc() etc. The uclass interface is
+		defined in include/serial.h.
+
+		CONFIG_DM_GPIO
+
+		Enable driver model for GPIO access. The standard GPIO
+		interface (gpio_get_value(), etc.) is then implemented by
+		the GPIO uclass. Drivers provide methods to query the
+		particular GPIOs that they provide. The uclass interface
+		is defined in include/asm-generic/gpio.h.
+
+		CONFIG_DM_SPI
+
+		Enable driver model for SPI. The SPI slave interface
+		(spi_setup_slave(), spi_xfer(), etc.) is then implemented by
+		the SPI uclass. Drivers provide methods to access the SPI
+		buses that they control. The uclass interface is defined in
+		include/spi.h. The existing spi_slave structure is attached
+		as 'parent data' to every slave on each bus. Slaves
+		typically use driver-private data instead of extending the
+		spi_slave structure.
+
+		CONFIG_DM_SPI_FLASH
+
+		Enable driver model for SPI flash. This SPI flash interface
+		(spi_flash_probe(), spi_flash_write(), etc.) is then
+		implemented by the SPI flash uclass. There is one standard
+		SPI flash driver which knows how to probe most chips
+		supported by U-Boot. The uclass interface is defined in
+		include/spi_flash.h, but is currently fully compatible
+		with the old interface to avoid confusion and duplication
+		during the transition parent. SPI and SPI flash must be
+		enabled together (it is not possible to use driver model
+		for one and not the other).
+
+		CONFIG_DM_CROS_EC
+
+		Enable driver model for the Chrome OS EC interface. This
+		allows the cros_ec SPI driver to operate with CONFIG_DM_SPI
+		but otherwise makes few changes. Since cros_ec also supports
+		I2C and LPC (which don't support driver model yet), a full
+		conversion is not yet possible.
+
+
+		** Code size options: The following options are enabled by
+		default except in SPL. Enable them explicitly to get these
+		features in SPL.
+
+		CONFIG_DM_WARN
+
+		Enable the dm_warn() function. This can use up quite a bit
+		of space for its strings.
+
+		CONFIG_DM_STDIO
+
+		Enable registering a serial device with the stdio library.
+
+		CONFIG_DM_DEVICE_REMOVE
+
+		Enable removing of devices.
+
+
 - Linux Kernel Interface:
 		CONFIG_CLOCKS_IN_MHZ
 
@@ -3870,6 +3984,11 @@ Configuration Settings:
 		Pre-relocation malloc() is only supported on ARM and sandbox
 		at present but is fairly easy to enable for other archs.
 
+- CONFIG_SYS_MALLOC_SIMPLE
+		Provides a simple and small malloc() and calloc() for those
+		boards which do not use the full malloc in SPL (which is
+		enabled with CONFIG_SYS_SPL_MALLOC_START).
+
 - CONFIG_SYS_BOOTM_LEN:
 		Normally compressed uImages are limited to an
 		uncompressed size of 8 MBytes. If this is not enough,
diff --git a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
index 7a7fd7dfb223f598795a23728c745183bb4037ef..efb53d673f4ee137f68b3edfa94b707de4e1b257 100644
--- a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
+++ b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
@@ -7,6 +7,7 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <asm/io.h>
 #include <asm/arch/at91sam9260_matrix.h>
 #include <asm/arch/at91_common.h>
@@ -229,3 +230,16 @@ void at91_sdram_hw_init(void)
 	at91_set_a_periph(AT91_PIO_PORTC, 30, 0);
 	at91_set_a_periph(AT91_PIO_PORTC, 31, 0);
 }
+
+/* Platform data for the GPIOs */
+static const struct at91_port_platdata at91sam9260_plat[] = {
+	{ ATMEL_BASE_PIOA, "PA" },
+	{ ATMEL_BASE_PIOB, "PB" },
+	{ ATMEL_BASE_PIOC, "PC" },
+};
+
+U_BOOT_DEVICES(at91sam9260_gpios) = {
+	{ "gpio_at91", &at91sam9260_plat[0] },
+	{ "gpio_at91", &at91sam9260_plat[1] },
+	{ "gpio_at91", &at91sam9260_plat[2] },
+};
diff --git a/arch/arm/cpu/u-boot-spl.lds b/arch/arm/cpu/u-boot-spl.lds
index 4beddf08e76722251b9d77182c7fc9bef508c314..a69b0061d21942df2dc04923a0009da55ae970dd 100644
--- a/arch/arm/cpu/u-boot-spl.lds
+++ b/arch/arm/cpu/u-boot-spl.lds
@@ -34,6 +34,13 @@ SECTIONS
 	. = ALIGN(4);
 
 	. = .;
+#ifdef CONFIG_SPL_DM
+	.u_boot_list : {
+		KEEP(*(SORT(.u_boot_list_*_driver_*)));
+		KEEP(*(SORT(.u_boot_list_*_uclass_*)));
+	}
+#endif
+	. = ALIGN(4);
 
 	__image_copy_end = .;
 
diff --git a/arch/arm/include/asm/arch-at91/at91sam9260.h b/arch/arm/include/asm/arch-at91/at91sam9260.h
index a30d569f6b1b32f5ecab6aa3b53457814e8dba9f..8950d674093a09a1663d8643fec4385dc2b16197 100644
--- a/arch/arm/include/asm/arch-at91/at91sam9260.h
+++ b/arch/arm/include/asm/arch-at91/at91sam9260.h
@@ -136,9 +136,11 @@
 /*
  * Other misc defines
  */
+#ifndef CONFIG_DM_GPIO
 #define ATMEL_PIO_PORTS		3		/* these SoCs have 3 PIO */
-#define ATMEL_PMC_UHP		AT91SAM926x_PMC_UHP
 #define ATMEL_BASE_PIO		ATMEL_BASE_PIOA
+#endif
+#define ATMEL_PMC_UHP		AT91SAM926x_PMC_UHP
 
 /*
  * SoC specific defines
diff --git a/arch/arm/include/asm/arch-at91/atmel_serial.h b/arch/arm/include/asm/arch-at91/atmel_serial.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bc094b355948c872fe59abf4cace93c5c0d780c
--- /dev/null
+++ b/arch/arm/include/asm/arch-at91/atmel_serial.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _ATMEL_SERIAL_H
+#define _ATMEL_SERIAL_H
+
+/* Information about a serial port */
+struct atmel_serial_platdata {
+	uint32_t base_addr;
+};
+
+#endif
diff --git a/arch/arm/include/asm/arch-at91/gpio.h b/arch/arm/include/asm/arch-at91/gpio.h
index 71213883d7b7098c52bc22564a41552967932e94..6d2a7b72ff3c98ca0d090569f9707e39f042ecbe 100644
--- a/arch/arm/include/asm/arch-at91/gpio.h
+++ b/arch/arm/include/asm/arch-at91/gpio.h
@@ -253,4 +253,10 @@ static inline unsigned at91_gpio_to_pin(unsigned gpio)
 	return gpio % 32;
 }
 
+/* Platform data for each GPIO port */
+struct at91_port_platdata {
+	uint32_t base_addr;
+	const char *bank_name;
+};
+
 #endif /* __ASM_ARCH_AT91_GPIO_H */
diff --git a/arch/arm/lib/crt0.S b/arch/arm/lib/crt0.S
index 29cdad0f70716a85e260a124b73f63746b484b80..823b233bc861ef4749417d6b344a2658d6d2ebe2 100644
--- a/arch/arm/lib/crt0.S
+++ b/arch/arm/lib/crt0.S
@@ -78,7 +78,7 @@ clr_gd:
 	strlo	r0, [r1]		/* clear 32-bit GD word */
 	addlo	r1, r1, #4		/* move to next */
 	blo	clr_gd
-#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SPL_BUILD)
+#if defined(CONFIG_SYS_MALLOC_F_LEN)
 	sub	sp, sp, #CONFIG_SYS_MALLOC_F_LEN
 	str	sp, [r9, #GD_MALLOC_BASE]
 #endif
diff --git a/board/bluewater/snapper9260/MAINTAINERS b/board/bluewater/snapper9260/MAINTAINERS
index ff1e9972cbdbfd89ccea533b5818b4d0db5d7b8e..1f8f4d6988f540c62ae3db875e81c3ee460a5cc4 100644
--- a/board/bluewater/snapper9260/MAINTAINERS
+++ b/board/bluewater/snapper9260/MAINTAINERS
@@ -1,5 +1,5 @@
 SNAPPER9260 BOARD
-M:	Ryan Mallon <ryan@bluewatersys.com>
+M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
 F:	board/bluewater/snapper9260/
 F:	include/configs/snapper9260.h
diff --git a/board/bluewater/snapper9260/snapper9260.c b/board/bluewater/snapper9260/snapper9260.c
index bfde1291a59c8138740a76ae2c6b21e949b6a23e..95633b0d2e87074fd07d6f3ddb09852ac93622c5 100644
--- a/board/bluewater/snapper9260/snapper9260.c
+++ b/board/bluewater/snapper9260/snapper9260.c
@@ -9,12 +9,15 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <asm/io.h>
+#include <asm/gpio.h>
 #include <asm/arch/at91sam9260_matrix.h>
 #include <asm/arch/at91sam9_smc.h>
 #include <asm/arch/at91_common.h>
 #include <asm/arch/at91_pmc.h>
 #include <asm/arch/gpio.h>
+#include <asm/arch/atmel_serial.h>
 #include <net.h>
 #include <netdev.h>
 #include <i2c.h>
@@ -95,10 +98,12 @@ static void nand_hw_init(void)
 	       &smc->cs[3].mode);
 
 	/* Configure RDY/BSY */
-	at91_set_gpio_input(CONFIG_SYS_NAND_READY_PIN, 1);
+	gpio_request(CONFIG_SYS_NAND_READY_PIN, "nand_rdy");
+	gpio_direction_input(CONFIG_SYS_NAND_READY_PIN);
 
 	/* Enable NandFlash */
-	at91_set_gpio_output(CONFIG_SYS_NAND_ENABLE_PIN, 1);
+	gpio_request(CONFIG_SYS_NAND_ENABLE_PIN, "nand_ce");
+	gpio_direction_output(CONFIG_SYS_NAND_ENABLE_PIN, 1);
 }
 
 int board_init(void)
@@ -140,3 +145,12 @@ int dram_init(void)
 void reset_phy(void)
 {
 }
+
+static struct atmel_serial_platdata at91sam9260_serial_plat = {
+	.base_addr = ATMEL_BASE_DBGU,
+};
+
+U_BOOT_DEVICE(at91sam9260_serial) = {
+	.name	= "serial_atmel",
+	.platdata = &at91sam9260_serial_plat,
+};
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 51125df34f018b79a511b44dc49e73a817944a43..0e4a65ad059424ac6bea8aff10821e259ba18432 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -6,6 +6,7 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <ns16550.h>
 #include <linux/compiler.h>
 #include <asm/io.h>
@@ -43,6 +44,13 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#ifdef CONFIG_SPL_BUILD
+/* TODO(sjg@chromium.org): Remove once SPL supports device tree */
+U_BOOT_DEVICE(tegra_gpios) = {
+	"gpio_tegra"
+};
+#endif
+
 const struct tegra_sysinfo sysinfo = {
 	CONFIG_TEGRA_BOARD_STRING
 };
diff --git a/common/Makefile b/common/Makefile
index 508a0b28f5ad325ad4059d539a1fad3257884040..9c47e20c6d19f477170d96b29eadf6a365159d8e 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -252,6 +252,9 @@ obj-$(CONFIG_BOUNCE_BUFFER) += bouncebuf.o
 obj-y += console.o
 obj-$(CONFIG_CROS_EC) += cros_ec.o
 obj-y += dlmalloc.o
+ifdef CONFIG_SYS_MALLOC_F_LEN
+obj-y += malloc_simple.o
+endif
 obj-y += image.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
 obj-$(CONFIG_OF_LIBFDT) += image-fdt.o
diff --git a/common/board_r.c b/common/board_r.c
index 7c339008ed2a46b91677d4c4c9e5e97fd58f4a1f..19c64271ab9d151be1d7e289af50973c62427875 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -99,7 +99,8 @@ static int initr_trace(void)
 
 static int initr_reloc(void)
 {
-	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */
+	/* tell others: relocation done */
+	gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
 	bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");
 
 	return 0;
diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c
index 3a75f94ea1ff50c14846998b38282b37c42da09a..c266b88e5b272a1d4e728a362194b17c1993ea8b 100644
--- a/common/cmd_i2c.c
+++ b/common/cmd_i2c.c
@@ -198,6 +198,19 @@ static uint get_alen(char *arg)
 	return alen;
 }
 
+enum i2c_err_op {
+	I2C_ERR_READ,
+	I2C_ERR_WRITE,
+};
+
+static int i2c_report_err(int ret, enum i2c_err_op op)
+{
+	printf("Error %s the chip: %d\n",
+	       op == I2C_ERR_READ ? "reading" : "writing", ret);
+
+	return CMD_RET_FAILURE;
+}
+
 /**
  * do_i2c_read() - Handle the "i2c read" command-line command
  * @cmdtp:	Command data struct pointer
@@ -245,7 +258,7 @@ static int do_i2c_read ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv
 	memaddr = (u_char *)simple_strtoul(argv[4], NULL, 16);
 
 	if (i2c_read(chip, devaddr, alen, memaddr, length) != 0) {
-		puts ("Error reading the chip.\n");
+		i2c_report_err(-1, I2C_ERR_READ);
 		return 1;
 	}
 	return 0;
@@ -286,8 +299,7 @@ static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[
 
 	while (length-- > 0) {
 		if (i2c_write(chip, devaddr++, alen, memaddr++, 1) != 0) {
-			puts("Error writing to the chip.\n");
-			return 1;
+			return i2c_report_err(-1, I2C_ERR_WRITE);
 		}
 /*
  * No write delay with FRAM devices.
@@ -370,7 +382,7 @@ static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]
 		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
 
 		if (i2c_read(chip, addr, alen, linebuf, linebytes) != 0)
-			puts ("Error reading the chip.\n");
+			i2c_report_err(-1, I2C_ERR_READ);
 		else {
 			printf("%04x:", addr);
 			cp = linebuf;
@@ -452,7 +464,7 @@ static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]
 
 	while (count-- > 0) {
 		if (i2c_write(chip, addr++, alen, &byte, 1) != 0)
-			puts ("Error writing the chip.\n");
+			i2c_report_err(-1, I2C_ERR_WRITE);
 		/*
 		 * Wait for the write to complete.  The write can take
 		 * up to 10mSec (we allow a little more time).
@@ -528,7 +540,7 @@ static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]
 		addr++;
 	}
 	if (err > 0)
-		puts ("Error reading the chip,\n");
+		i2c_report_err(-1, I2C_ERR_READ);
 	else
 		printf ("%08lx\n", crc);
 
@@ -601,7 +613,7 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg
 	do {
 		printf("%08lx:", addr);
 		if (i2c_read(chip, addr, alen, (uchar *)&data, size) != 0)
-			puts ("\nError reading the chip,\n");
+			i2c_report_err(-1, I2C_ERR_READ);
 		else {
 			data = cpu_to_be32(data);
 			if (size == 1)
@@ -644,7 +656,7 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg
 				 */
 				bootretry_reset_cmd_timeout();
 				if (i2c_write(chip, addr, alen, (uchar *)&data, size) != 0)
-					puts ("Error writing the chip.\n");
+					i2c_report_err(-1, I2C_ERR_WRITE);
 #ifdef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS
 				udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
 #endif
@@ -783,7 +795,7 @@ static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]
 	 */
 	while (1) {
 		if (i2c_read(chip, addr, alen, bytes, length) != 0)
-			puts ("Error reading the chip.\n");
+			i2c_report_err(-1, I2C_ERR_READ);
 		udelay(delay);
 	}
 
@@ -1341,7 +1353,7 @@ int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
 
 	chip = simple_strtoul(argv[1], NULL, 16);
 	if (i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid)) != 0) {
-		puts("Error reading EDID content.\n");
+		i2c_report_err(-1, I2C_ERR_READ);
 		return 1;
 	}
 
diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index 991229d5f788f6d85b77833122b8fa2b65102ae1..6453ee9c259fcde7bfbe63099255b9823d5580c2 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -2184,17 +2184,8 @@ Void_t* mALLOc(bytes) size_t bytes;
   INTERNAL_SIZE_T nb;
 
 #ifdef CONFIG_SYS_MALLOC_F_LEN
-	if (gd && !(gd->flags & GD_FLG_RELOC)) {
-		ulong new_ptr;
-		void *ptr;
-
-		new_ptr = gd->malloc_ptr + bytes;
-		if (new_ptr > gd->malloc_limit)
-			panic("Out of pre-reloc memory");
-		ptr = map_sysmem(gd->malloc_base + gd->malloc_ptr, bytes);
-		gd->malloc_ptr = ALIGN(new_ptr, sizeof(new_ptr));
-		return ptr;
-	}
+	if (gd && !(gd->flags & GD_FLG_FULL_MALLOC_INIT))
+		return malloc_simple(bytes);
 #endif
 
   /* check if mem_malloc_init() was run */
@@ -2462,7 +2453,7 @@ void fREe(mem) Void_t* mem;
 
 #ifdef CONFIG_SYS_MALLOC_F_LEN
 	/* free() is a no-op - all the memory will be freed on relocation */
-	if (!(gd->flags & GD_FLG_RELOC))
+	if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT))
 		return;
 #endif
 
@@ -2618,7 +2609,7 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
   if (oldmem == NULL) return mALLOc(bytes);
 
 #ifdef CONFIG_SYS_MALLOC_F_LEN
-	if (!(gd->flags & GD_FLG_RELOC)) {
+	if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) {
 		/* This is harder to support and should not be needed */
 		panic("pre-reloc realloc() is not supported");
 	}
@@ -2970,7 +2961,7 @@ Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size;
   else
   {
 #ifdef CONFIG_SYS_MALLOC_F_LEN
-	if (!(gd->flags & GD_FLG_RELOC)) {
+	if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) {
 		MALLOC_ZERO(mem, sz);
 		return mem;
 	}
diff --git a/common/malloc_simple.c b/common/malloc_simple.c
new file mode 100644
index 0000000000000000000000000000000000000000..afdacff80d8e1fc411cba7eab59286ef28a8a1e5
--- /dev/null
+++ b/common/malloc_simple.c
@@ -0,0 +1,39 @@
+/*
+ * Simple malloc implementation
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void *malloc_simple(size_t bytes)
+{
+	ulong new_ptr;
+	void *ptr;
+
+	new_ptr = gd->malloc_ptr + bytes;
+	if (new_ptr > gd->malloc_limit)
+		panic("Out of pre-reloc memory");
+	ptr = map_sysmem(gd->malloc_base + gd->malloc_ptr, bytes);
+	gd->malloc_ptr = ALIGN(new_ptr, sizeof(new_ptr));
+	return ptr;
+}
+
+#ifdef CONFIG_SYS_MALLOC_SIMPLE
+void *calloc(size_t nmemb, size_t elem_size)
+{
+	size_t size = nmemb * elem_size;
+	void *ptr;
+
+	ptr = malloc(size);
+	memset(ptr, '\0', size);
+
+	return ptr;
+}
+#endif
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 97eb8eb987d97ed6b0c128742642ecca1a169477..1826c47a99c464bd0023f3c173cab788de49acb0 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -7,6 +7,7 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 #include <common.h>
+#include <dm.h>
 #include <spl.h>
 #include <asm/u-boot.h>
 #include <nand.h>
@@ -15,6 +16,7 @@
 #include <i2c.h>
 #include <image.h>
 #include <malloc.h>
+#include <dm/root.h>
 #include <linux/compiler.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -139,9 +141,16 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
 	u32 boot_device;
 	debug(">>spl:board_init_r()\n");
 
-#ifdef CONFIG_SYS_SPL_MALLOC_START
+#if defined(CONFIG_SYS_SPL_MALLOC_START)
 	mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
 			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_ptr = 0;
+#endif
+#ifdef CONFIG_SPL_DM
+	dm_init_and_scan(true);
 #endif
 
 #ifndef CONFIG_PPC
@@ -240,6 +249,11 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
 	default:
 		debug("Unsupported OS image.. Jumping nevertheless..\n");
 	}
+#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
+	debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
+	      gd->malloc_ptr / 1024);
+#endif
+
 	jump_to_image_no_args(&spl_image);
 }
 
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
index 0278dda4d77045a7dbcc8400b1d5593252b4f514..3e2f62265793d09dc4d2277636ffffec0f62b494 100644
--- a/doc/driver-model/README.txt
+++ b/doc/driver-model/README.txt
@@ -750,19 +750,43 @@ device pointers, but this is not currently implemented (the root device
 pointer is saved but not made available through the driver model API).
 
 
-Things to punt for later
-------------------------
+SPL Support
+-----------
+
+Driver model can operate in SPL. Its efficient implementation and small code
+size provide for a small overhead which is acceptable for all but the most
+constrained systems.
+
+To enable driver model in SPL, define CONFIG_SPL_DM. You might want to
+consider the following option also. See the main README for more details.
+
+   - CONFIG_SYS_MALLOC_SIMPLE
+   - CONFIG_DM_WARN
+   - CONFIG_DM_DEVICE_REMOVE
+   - CONFIG_DM_STDIO
 
-- SPL support - this will have to be present before many drivers can be
-converted, but it seems like we can add it once we are happy with the
-core implementation.
 
-That is not to say that no thinking has gone into this - in fact there
-is quite a lot there. However, getting these right is non-trivial and
-there is a high cost associated with going down the wrong path.
+Enabling Driver Model
+---------------------
 
-For SPL, it may be possible to fit in a simplified driver model with only
-bind and probe methods, to reduce size.
+Driver model is being brought into U-Boot gradually. As each subsystems gets
+support, a uclass is created and a CONFIG to enable use of driver model for
+that subsystem.
+
+For example CONFIG_DM_SERIAL enables driver model for serial. With that
+defined, the old serial support is not enabled, and your serial driver must
+conform to driver model. With that undefined, the old serial support is
+enabled and driver model is not available for serial. This means that when
+you convert a driver, you must either convert all its boards, or provide for
+the driver to be compiled both with and without driver model (generally this
+is not very hard).
+
+See the main README for full details of the available driver model CONFIG
+options.
+
+
+Things to punt for later
+------------------------
 
 Uclasses are statically numbered at compile time. It would be possible to
 change this to dynamic numbering, but then we would require some sort of
diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index 151c2398a4d47fce304d43a6ae775befd379baa2..f14695b2d6eae985d366827d69bcedda1ce1e2ce 100644
--- a/drivers/core/Makefile
+++ b/drivers/core/Makefile
@@ -4,5 +4,6 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-obj-y := device.o lists.o root.o uclass.o util.o
+obj-$(CONFIG_DM)	+= device.o lists.o root.o uclass.o util.o
 obj-$(CONFIG_OF_CONTROL) += simple-bus.o
+obj-$(CONFIG_DM_DEVICE_REMOVE)	+= device-remove.o
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
new file mode 100644
index 0000000000000000000000000000000000000000..8fc6b7108427add4a2f8f3a1047eee7e5e440073
--- /dev/null
+++ b/drivers/core/device-remove.c
@@ -0,0 +1,187 @@
+/*
+ * Device manager
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+
+/**
+ * device_chld_unbind() - Unbind all device's children from the device
+ *
+ * On error, the function continues to unbind all children, and reports the
+ * first error.
+ *
+ * @dev:	The device that is to be stripped of its children
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_unbind(struct udevice *dev)
+{
+	struct udevice *pos, *n;
+	int ret, saved_ret = 0;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_unbind(pos);
+		if (ret && !saved_ret)
+			saved_ret = ret;
+	}
+
+	return saved_ret;
+}
+
+/**
+ * device_chld_remove() - Stop all device's children
+ * @dev:	The device whose children are to be removed
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_remove(struct udevice *dev)
+{
+	struct udevice *pos, *n;
+	int ret;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_remove(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int device_unbind(struct udevice *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return -EINVAL;
+
+	drv = dev->driver;
+	assert(drv);
+
+	if (drv->unbind) {
+		ret = drv->unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = device_chld_unbind(dev);
+	if (ret)
+		return ret;
+
+	ret = uclass_unbind_device(dev);
+	if (ret)
+		return ret;
+
+	if (dev->parent)
+		list_del(&dev->sibling_node);
+	free(dev);
+
+	return 0;
+}
+
+/**
+ * device_free() - Free memory buffers allocated by a device
+ * @dev:	Device that is to be started
+ */
+void device_free(struct udevice *dev)
+{
+	int size;
+
+	if (dev->driver->priv_auto_alloc_size) {
+		free(dev->priv);
+		dev->priv = NULL;
+	}
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		free(dev->platdata);
+		dev->platdata = NULL;
+	}
+	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
+	if (size) {
+		free(dev->uclass_priv);
+		dev->uclass_priv = NULL;
+	}
+	if (dev->parent) {
+		size = dev->parent->driver->per_child_auto_alloc_size;
+		if (size) {
+			free(dev->parent_priv);
+			dev->parent_priv = NULL;
+		}
+	}
+}
+
+int device_remove(struct udevice *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!(dev->flags & DM_FLAG_ACTIVATED))
+		return 0;
+
+	drv = dev->driver;
+	assert(drv);
+
+	ret = uclass_pre_remove_device(dev);
+	if (ret)
+		return ret;
+
+	ret = device_chld_remove(dev);
+	if (ret)
+		goto err;
+
+	if (drv->remove) {
+		ret = drv->remove(dev);
+		if (ret)
+			goto err_remove;
+	}
+
+	if (dev->parent && dev->parent->driver->child_post_remove) {
+		ret = dev->parent->driver->child_post_remove(dev);
+		if (ret) {
+			dm_warn("%s: Device '%s' failed child_post_remove()",
+				__func__, dev->name);
+		}
+	}
+
+	device_free(dev);
+
+	dev->seq = -1;
+	dev->flags &= ~DM_FLAG_ACTIVATED;
+
+	return ret;
+
+err_remove:
+	/* We can't put the children back */
+	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
+		__func__, dev->name);
+err:
+	ret = uclass_post_probe_device(dev);
+	if (ret) {
+		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
+			__func__, dev->name);
+	}
+
+	return ret;
+}
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 49faa29dc1a0eecaa84e76371b8301c2eef14d7b..6793e1c4f944b9db600687669a222728f19feafd 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -24,52 +24,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/**
- * device_chld_unbind() - Unbind all device's children from the device
- *
- * On error, the function continues to unbind all children, and reports the
- * first error.
- *
- * @dev:	The device that is to be stripped of its children
- * @return 0 on success, -ve on error
- */
-static int device_chld_unbind(struct udevice *dev)
-{
-	struct udevice *pos, *n;
-	int ret, saved_ret = 0;
-
-	assert(dev);
-
-	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
-		ret = device_unbind(pos);
-		if (ret && !saved_ret)
-			saved_ret = ret;
-	}
-
-	return saved_ret;
-}
-
-/**
- * device_chld_remove() - Stop all device's children
- * @dev:	The device whose children are to be removed
- * @return 0 on success, -ve on error
- */
-static int device_chld_remove(struct udevice *dev)
-{
-	struct udevice *pos, *n;
-	int ret;
-
-	assert(dev);
-
-	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
-		ret = device_remove(pos);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
 int device_bind(struct udevice *parent, struct driver *drv, const char *name,
 		void *platdata, int of_offset, struct udevice **devp)
 {
@@ -167,71 +121,6 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			   -1, devp);
 }
 
-int device_unbind(struct udevice *dev)
-{
-	struct driver *drv;
-	int ret;
-
-	if (!dev)
-		return -EINVAL;
-
-	if (dev->flags & DM_FLAG_ACTIVATED)
-		return -EINVAL;
-
-	drv = dev->driver;
-	assert(drv);
-
-	if (drv->unbind) {
-		ret = drv->unbind(dev);
-		if (ret)
-			return ret;
-	}
-
-	ret = device_chld_unbind(dev);
-	if (ret)
-		return ret;
-
-	ret = uclass_unbind_device(dev);
-	if (ret)
-		return ret;
-
-	if (dev->parent)
-		list_del(&dev->sibling_node);
-	free(dev);
-
-	return 0;
-}
-
-/**
- * device_free() - Free memory buffers allocated by a device
- * @dev:	Device that is to be started
- */
-static void device_free(struct udevice *dev)
-{
-	int size;
-
-	if (dev->driver->priv_auto_alloc_size) {
-		free(dev->priv);
-		dev->priv = NULL;
-	}
-	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
-		free(dev->platdata);
-		dev->platdata = NULL;
-	}
-	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
-	if (size) {
-		free(dev->uclass_priv);
-		dev->uclass_priv = NULL;
-	}
-	if (dev->parent) {
-		size = dev->parent->driver->per_child_auto_alloc_size;
-		if (size) {
-			free(dev->parent_priv);
-			dev->parent_priv = NULL;
-		}
-	}
-}
-
 int device_probe_child(struct udevice *dev, void *parent_priv)
 {
 	struct driver *drv;
@@ -342,63 +231,6 @@ int device_probe(struct udevice *dev)
 	return device_probe_child(dev, NULL);
 }
 
-int device_remove(struct udevice *dev)
-{
-	struct driver *drv;
-	int ret;
-
-	if (!dev)
-		return -EINVAL;
-
-	if (!(dev->flags & DM_FLAG_ACTIVATED))
-		return 0;
-
-	drv = dev->driver;
-	assert(drv);
-
-	ret = uclass_pre_remove_device(dev);
-	if (ret)
-		return ret;
-
-	ret = device_chld_remove(dev);
-	if (ret)
-		goto err;
-
-	if (drv->remove) {
-		ret = drv->remove(dev);
-		if (ret)
-			goto err_remove;
-	}
-
-	if (dev->parent && dev->parent->driver->child_post_remove) {
-		ret = dev->parent->driver->child_post_remove(dev);
-		if (ret) {
-			dm_warn("%s: Device '%s' failed child_post_remove()",
-				__func__, dev->name);
-		}
-	}
-
-	device_free(dev);
-
-	dev->seq = -1;
-	dev->flags &= ~DM_FLAG_ACTIVATED;
-
-	return ret;
-
-err_remove:
-	/* We can't put the children back */
-	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
-		__func__, dev->name);
-err:
-	ret = uclass_post_probe_device(dev);
-	if (ret) {
-		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
-			__func__, dev->name);
-	}
-
-	return ret;
-}
-
 void *dev_get_platdata(struct udevice *dev)
 {
 	if (!dev) {
@@ -548,3 +380,13 @@ int device_find_next_child(struct udevice **devp)
 
 	return 0;
 }
+
+struct udevice *dev_get_parent(struct udevice *child)
+{
+	return child->parent;
+}
+
+ulong dev_get_of_data(struct udevice *dev)
+{
+	return dev->of_id->data;
+}
diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index 3a1ea8565449699a39ba5684dbecfbf71e30780b..ff115c4723e7135986daced0f08dede34379838f 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -25,9 +25,6 @@ struct driver *lists_driver_lookup_name(const char *name)
 	const int n_ents = ll_entry_count(struct driver, driver);
 	struct driver *entry;
 
-	if (!drv || !n_ents)
-		return NULL;
-
 	for (entry = drv; entry != drv + n_ents; entry++) {
 		if (!strcmp(name, entry->name))
 			return entry;
@@ -44,9 +41,6 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
 	const int n_ents = ll_entry_count(struct uclass_driver, uclass);
 	struct uclass_driver *entry;
 
-	if ((id == UCLASS_INVALID) || !uclass)
-		return NULL;
-
 	for (entry = uclass; entry != uclass + n_ents; entry++) {
 		if (entry->id == id)
 			return entry;
@@ -77,34 +71,60 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
 	return result;
 }
 
+int device_bind_driver(struct udevice *parent, const char *drv_name,
+		       const char *dev_name, struct udevice **devp)
+{
+	struct driver *drv;
+	int ret;
+
+	drv = lists_driver_lookup_name(drv_name);
+	if (!drv) {
+		printf("Cannot find driver '%s'\n", drv_name);
+		return -ENOENT;
+	}
+	ret = device_bind(parent, drv, dev_name, NULL, -1, devp);
+	if (ret) {
+		printf("Cannot create device named '%s' (err=%d)\n",
+		       dev_name, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_OF_CONTROL
 /**
  * driver_check_compatible() - Check if a driver is compatible with this node
  *
  * @param blob:		Device tree pointer
  * @param offset:	Offset of node in device tree
- * @param of_matchL	List of compatible strings to match
+ * @param of_match:	List of compatible strings to match
+ * @param of_idp:	Returns the match that was found
  * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node
  * does not have a compatible string, other error <0 if there is a device
  * tree error
  */
 static int driver_check_compatible(const void *blob, int offset,
-				   const struct udevice_id *of_match)
+				   const struct udevice_id *of_match,
+				   const struct udevice_id **of_idp)
 {
 	int ret;
 
+	*of_idp = NULL;
 	if (!of_match)
 		return -ENOENT;
 
 	while (of_match->compatible) {
 		ret = fdt_node_check_compatible(blob, offset,
 						of_match->compatible);
-		if (!ret)
+		if (!ret) {
+			*of_idp = of_match;
 			return 0;
-		else if (ret == -FDT_ERR_NOTFOUND)
+		} else if (ret == -FDT_ERR_NOTFOUND) {
 			return -ENODEV;
-		else if (ret < 0)
+		} else if (ret < 0) {
 			return -EINVAL;
+		}
 		of_match++;
 	}
 
@@ -116,6 +136,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 {
 	struct driver *driver = ll_entry_start(struct driver, driver);
 	const int n_ents = ll_entry_count(struct driver, driver);
+	const struct udevice_id *id;
 	struct driver *entry;
 	struct udevice *dev;
 	bool found = false;
@@ -127,7 +148,8 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 	if (devp)
 		*devp = NULL;
 	for (entry = driver; entry != driver + n_ents; entry++) {
-		ret = driver_check_compatible(blob, offset, entry->of_match);
+		ret = driver_check_compatible(blob, offset, entry->of_match,
+					      &id);
 		name = fdt_get_name(blob, offset, NULL);
 		if (ret == -ENOENT) {
 			continue;
@@ -136,8 +158,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 			break;
 		} else if (ret) {
 			dm_warn("Device tree error at offset %d\n", offset);
-			if (!result || ret != -ENOENT)
-				result = ret;
+			result = ret;
 			break;
 		}
 
@@ -147,6 +168,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 			dm_warn("Error binding driver '%s'\n", entry->name);
 			return ret;
 		} else {
+			dev->of_id = id;
 			found = true;
 			if (devp)
 				*devp = dev;
diff --git a/drivers/core/root.c b/drivers/core/root.c
index a328a4876f13ad68df6d5fddd8817df622d0195d..47b3acfbe981da9fcfe5ca6d7c20e60dd63b0285 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -73,10 +73,8 @@ int dm_scan_platdata(bool pre_reloc_only)
 		dm_warn("Some drivers were not found\n");
 		ret = 0;
 	}
-	if (ret)
-		return ret;
 
-	return 0;
+	return ret;
 }
 
 #ifdef CONFIG_OF_CONTROL
diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c
index 6517af162815ea8e5715dabb390ed46de12f4617..6129c020ea163fb2baa43b211285cad349f29038 100644
--- a/drivers/gpio/at91_gpio.c
+++ b/drivers/gpio/at91_gpio.c
@@ -10,11 +10,14 @@
 
 #include <config.h>
 #include <common.h>
+#include <dm.h>
 #include <asm/io.h>
 #include <linux/sizes.h>
+#include <asm/gpio.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/at91_pio.h>
-#include <asm/arch/gpio.h>
+
+#define GPIO_PER_BANK	32
 
 static struct at91_port *at91_pio_get_port(unsigned port)
 {
@@ -39,19 +42,25 @@ static struct at91_port *at91_pio_get_port(unsigned port)
 	}
 }
 
+static void at91_set_port_pullup(struct at91_port *at91_port, unsigned offset,
+				 int use_pullup)
+{
+	u32 mask;
+
+	mask = 1 << offset;
+	if (use_pullup)
+		writel(mask, &at91_port->puer);
+	else
+		writel(mask, &at91_port->pudr);
+	writel(mask, &at91_port->per);
+}
+
 int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup)
 {
 	struct at91_port *at91_port = at91_pio_get_port(port);
-	u32 mask;
 
-	if (at91_port && (pin < 32)) {
-		mask = 1 << pin;
-		if (use_pullup)
-			writel(1 << pin, &at91_port->puer);
-		else
-			writel(1 << pin, &at91_port->pudr);
-		writel(mask, &at91_port->per);
-	}
+	if (at91_port && (pin < GPIO_PER_BANK))
+		at91_set_port_pullup(at91_port, pin, use_pullup);
 
 	return 0;
 }
@@ -64,7 +73,7 @@ int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->idr);
 		at91_set_pio_pullup(port, pin, use_pullup);
@@ -82,7 +91,7 @@ int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->idr);
 		at91_set_pio_pullup(port, pin, use_pullup);
@@ -108,7 +117,7 @@ int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->idr);
 		at91_set_pio_pullup(port, pin, use_pullup);
@@ -135,7 +144,7 @@ int at91_set_c_periph(unsigned port, unsigned pin, int use_pullup)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->idr);
 		at91_set_pio_pullup(port, pin, use_pullup);
@@ -157,7 +166,7 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->idr);
 		at91_set_pio_pullup(port, pin, use_pullup);
@@ -172,6 +181,29 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup)
 }
 #endif
 
+#ifdef CONFIG_DM_GPIO
+static bool at91_get_port_output(struct at91_port *at91_port, int offset)
+{
+	u32 mask, val;
+
+	mask = 1 << offset;
+	val = readl(&at91_port->osr);
+	return val & mask;
+}
+#endif
+
+static void at91_set_port_input(struct at91_port *at91_port, int offset,
+				int use_pullup)
+{
+	u32 mask;
+
+	mask = 1 << offset;
+	writel(mask, &at91_port->idr);
+	at91_set_port_pullup(at91_port, offset, use_pullup);
+	writel(mask, &at91_port->odr);
+	writel(mask, &at91_port->per);
+}
+
 /*
  * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
  * configure it for an input.
@@ -179,19 +211,29 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup)
 int at91_set_pio_input(unsigned port, u32 pin, int use_pullup)
 {
 	struct at91_port *at91_port = at91_pio_get_port(port);
-	u32 mask;
 
-	if (at91_port && (pin < 32)) {
-		mask = 1 << pin;
-		writel(mask, &at91_port->idr);
-		at91_set_pio_pullup(port, pin, use_pullup);
-		writel(mask, &at91_port->odr);
-		writel(mask, &at91_port->per);
-	}
+	if (at91_port && (pin < GPIO_PER_BANK))
+		at91_set_port_input(at91_port, pin, use_pullup);
 
 	return 0;
 }
 
+static void at91_set_port_output(struct at91_port *at91_port, int offset,
+				 int value)
+{
+	u32 mask;
+
+	mask = 1 << offset;
+	writel(mask, &at91_port->idr);
+	writel(mask, &at91_port->pudr);
+	if (value)
+		writel(mask, &at91_port->sodr);
+	else
+		writel(mask, &at91_port->codr);
+	writel(mask, &at91_port->oer);
+	writel(mask, &at91_port->per);
+}
+
 /*
  * mux the pin to the gpio controller (instead of "A" or "B" peripheral),
  * and configure it for an output.
@@ -199,19 +241,9 @@ int at91_set_pio_input(unsigned port, u32 pin, int use_pullup)
 int at91_set_pio_output(unsigned port, u32 pin, int value)
 {
 	struct at91_port *at91_port = at91_pio_get_port(port);
-	u32 mask;
 
-	if (at91_port && (port < ATMEL_PIO_PORTS) && (pin < 32)) {
-		mask = 1 << pin;
-		writel(mask, &at91_port->idr);
-		writel(mask, &at91_port->pudr);
-		if (value)
-			writel(mask, &at91_port->sodr);
-		else
-			writel(mask, &at91_port->codr);
-		writel(mask, &at91_port->oer);
-		writel(mask, &at91_port->per);
-	}
+	if (at91_port && (pin < GPIO_PER_BANK))
+		at91_set_port_output(at91_port, pin, value);
 
 	return 0;
 }
@@ -224,7 +256,7 @@ int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		if (is_on) {
 #if defined(CPU_HAS_PIO3)
@@ -248,7 +280,7 @@ int at91_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		if (is_on) {
 			writel(mask, &at91_port->ifscer);
@@ -271,7 +303,7 @@ int at91_set_pio_pulldown(unsigned port, unsigned pin, int is_on)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(mask, &at91_port->pudr);
 		if (is_on)
@@ -291,7 +323,7 @@ int at91_set_pio_disable_schmitt_trig(unsigned port, unsigned pin)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		writel(readl(&at91_port->schmitt) | mask,
 		       &at91_port->schmitt);
@@ -310,7 +342,7 @@ int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on)
 	struct at91_port *at91_port = at91_pio_get_port(port);
 	u32 mask;
 
-	if (at91_port && (pin < 32)) {
+	if (at91_port && (pin < GPIO_PER_BANK)) {
 		mask = 1 << pin;
 		if (is_on)
 			writel(mask, &at91_port->mder);
@@ -321,41 +353,54 @@ int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on)
 	return 0;
 }
 
+static void at91_set_port_value(struct at91_port *at91_port, int offset,
+				int value)
+{
+	u32 mask;
+
+	mask = 1 << offset;
+	if (value)
+		writel(mask, &at91_port->sodr);
+	else
+		writel(mask, &at91_port->codr);
+}
+
 /*
  * assuming the pin is muxed as a gpio output, set its value.
  */
 int at91_set_pio_value(unsigned port, unsigned pin, int value)
 {
 	struct at91_port *at91_port = at91_pio_get_port(port);
-	u32 mask;
 
-	if (at91_port && (pin < 32)) {
-		mask = 1 << pin;
-		if (value)
-			writel(mask, &at91_port->sodr);
-		else
-			writel(mask, &at91_port->codr);
-	}
+	if (at91_port && (pin < GPIO_PER_BANK))
+		at91_set_port_value(at91_port, pin, value);
 
 	return 0;
 }
 
+static int at91_get_port_value(struct at91_port *at91_port, int offset)
+{
+	u32 pdsr = 0, mask;
+
+	mask = 1 << offset;
+	pdsr = readl(&at91_port->pdsr) & mask;
+
+	return pdsr != 0;
+}
 /*
  * read the pin's value (works even if it's not muxed as a gpio).
  */
 int at91_get_pio_value(unsigned port, unsigned pin)
 {
 	struct at91_port *at91_port = at91_pio_get_port(port);
-	u32 pdsr = 0, mask;
 
-	if (at91_port && (pin < 32)) {
-		mask = 1 << pin;
-		pdsr = readl(&at91_port->pdsr) & mask;
-	}
+	if (at91_port && (pin < GPIO_PER_BANK))
+		return at91_get_port_value(at91_port, pin);
 
-	return pdsr != 0;
+	return 0;
 }
 
+#ifndef CONFIG_DM_GPIO
 /* Common GPIO API */
 
 int gpio_request(unsigned gpio, const char *label)
@@ -395,3 +440,91 @@ int gpio_set_value(unsigned gpio, int value)
 
 	return 0;
 }
+#endif
+
+#ifdef CONFIG_DM_GPIO
+
+struct at91_port_priv {
+	struct at91_port *regs;
+};
+
+/* set GPIO pin 'gpio' as an input */
+static int at91_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+	struct at91_port_priv *port = dev_get_platdata(dev);
+
+	at91_set_port_input(port->regs, offset, 0);
+
+	return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int at91_gpio_direction_output(struct udevice *dev, unsigned offset,
+				       int value)
+{
+	struct at91_port_priv *port = dev_get_platdata(dev);
+
+	at91_set_port_output(port->regs, offset, value);
+
+	return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int at91_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+	struct at91_port_priv *port = dev_get_platdata(dev);
+
+	return at91_get_port_value(port->regs, offset);
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int at91_gpio_set_value(struct udevice *dev, unsigned offset,
+			       int value)
+{
+	struct at91_port_priv *port = dev_get_platdata(dev);
+
+	at91_set_port_value(port->regs, offset, value);
+
+	return 0;
+}
+
+static int at91_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+	struct at91_port_priv *port = dev_get_platdata(dev);
+
+	/* GPIOF_FUNC is not implemented yet */
+	if (at91_get_port_output(port->regs, offset))
+		return GPIOF_OUTPUT;
+	else
+		return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_at91_ops = {
+	.direction_input	= at91_gpio_direction_input,
+	.direction_output	= at91_gpio_direction_output,
+	.get_value		= at91_gpio_get_value,
+	.set_value		= at91_gpio_set_value,
+	.get_function		= at91_gpio_get_function,
+};
+
+static int at91_gpio_probe(struct udevice *dev)
+{
+	struct at91_port_priv *port = dev_get_priv(dev);
+	struct at91_port_platdata *plat = dev_get_platdata(dev);
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+
+	uc_priv->bank_name = plat->bank_name;
+	uc_priv->gpio_count = GPIO_PER_BANK;
+	port->regs = (struct at91_port *)plat->base_addr;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(gpio_at91) = {
+	.name	= "gpio_at91",
+	.id	= UCLASS_GPIO,
+	.ops	= &gpio_at91_ops,
+	.probe	= at91_gpio_probe,
+	.priv_auto_alloc_size = sizeof(struct at91_port_priv),
+};
+#endif
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
index 521edfd5de5cf5698ff98c1627e30703c3649282..9b4effb2fb56cda2fcba39d1a5ee80fe084384b8 100644
--- a/drivers/misc/cros_ec.c
+++ b/drivers/misc/cros_ec.c
@@ -701,6 +701,7 @@ static int cros_ec_check_version(struct cros_ec_dev *dev)
 
 	/* Try sending a version 3 packet */
 	dev->protocol_version = 3;
+	req.in_data = 0;
 	if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req),
 			     (uint8_t **)&resp, sizeof(*resp)) > 0) {
 		return 0;
diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c
index be6c43ed88ac34ceeab141585a7916b4ce06c17d..3024b988fef904884f4d6e4920d98859714f115c 100644
--- a/drivers/mtd/spi/sandbox.c
+++ b/drivers/mtd/spi/sandbox.c
@@ -602,14 +602,14 @@ static int sandbox_sf_bind_bus_cs(struct sandbox_state *state, int busnum,
 		       spec, ret);
 		return ret;
 	}
-	ret = device_find_child_by_seq(bus, cs, true, &slave);
+	ret = spi_find_chip_select(bus, cs, &slave);
 	if (!ret) {
 		printf("Chip select %d already exists for spec '%s'\n", cs,
 		       spec);
 		return -EEXIST;
 	}
 
-	ret = spi_bind_device(bus, cs, "spi_flash_std", spec, &slave);
+	ret = device_bind_driver(bus, "spi_flash_std", spec, &slave);
 	if (ret)
 		return ret;
 
diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c
index 8f0e3489a0e59287c60a2e3b34cc1fdc69eb91d7..4fe992bf2bf38dc9d53359dac9212efdf1246e0d 100644
--- a/drivers/serial/atmel_usart.c
+++ b/drivers/serial/atmel_usart.c
@@ -7,11 +7,16 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 #include <common.h>
+#include <dm.h>
+#include <errno.h>
 #include <watchdog.h>
 #include <serial.h>
 #include <linux/compiler.h>
 
 #include <asm/io.h>
+#ifdef CONFIG_DM_SERIAL
+#include <asm/arch/atmel_serial.h>
+#endif
 #include <asm/arch/clk.h>
 #include <asm/arch/hardware.h>
 
@@ -19,9 +24,9 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-static void atmel_serial_setbrg(void)
+static void atmel_serial_setbrg_internal(atmel_usart3_t *usart, int id,
+					 int baudrate)
 {
-	atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE;
 	unsigned long divisor;
 	unsigned long usart_hz;
 
@@ -30,15 +35,13 @@ static void atmel_serial_setbrg(void)
 	 * Baud Rate = --------------
 	 *                16 * CD
 	 */
-	usart_hz = get_usart_clk_rate(CONFIG_USART_ID);
-	divisor = (usart_hz / 16 + gd->baudrate / 2) / gd->baudrate;
+	usart_hz = get_usart_clk_rate(id);
+	divisor = (usart_hz / 16 + baudrate / 2) / baudrate;
 	writel(USART3_BF(CD, divisor), &usart->brgr);
 }
 
-static int atmel_serial_init(void)
+static void atmel_serial_init_internal(atmel_usart3_t *usart)
 {
-	atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE;
-
 	/*
 	 * Just in case: drain transmitter register
 	 * 1000us is enough for baudrate >= 9600
@@ -47,9 +50,10 @@ static int atmel_serial_init(void)
 		__udelay(1000);
 
 	writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr);
+}
 
-	serial_setbrg();
-
+static void atmel_serial_activate(atmel_usart3_t *usart)
+{
 	writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL)
 			   | USART3_BF(USCLKS, USART3_USCLKS_MCK)
 			   | USART3_BF(CHRL, USART3_CHRL_8)
@@ -59,6 +63,22 @@ static int atmel_serial_init(void)
 	writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr);
 	/* 100us is enough for the new settings to be settled */
 	__udelay(100);
+}
+
+#ifndef CONFIG_DM_SERIAL
+static void atmel_serial_setbrg(void)
+{
+	atmel_serial_setbrg_internal((atmel_usart3_t *)CONFIG_USART_BASE,
+				     CONFIG_USART_ID, gd->baudrate);
+}
+
+static int atmel_serial_init(void)
+{
+	atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE;
+
+	atmel_serial_init_internal(usart);
+	serial_setbrg();
+	atmel_serial_activate(usart);
 
 	return 0;
 }
@@ -109,3 +129,81 @@ __weak struct serial_device *default_serial_console(void)
 {
 	return &atmel_serial_drv;
 }
+#endif
+
+#ifdef CONFIG_DM_SERIAL
+
+struct atmel_serial_priv {
+	atmel_usart3_t *usart;
+};
+
+int atmel_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct atmel_serial_priv *priv = dev_get_priv(dev);
+
+	atmel_serial_setbrg_internal(priv->usart, 0 /* ignored */, baudrate);
+	atmel_serial_activate(priv->usart);
+
+	return 0;
+}
+
+static int atmel_serial_getc(struct udevice *dev)
+{
+	struct atmel_serial_priv *priv = dev_get_priv(dev);
+
+	if (!(readl(&priv->usart->csr) & USART3_BIT(RXRDY)))
+		return -EAGAIN;
+
+	return readl(&priv->usart->rhr);
+}
+
+static int atmel_serial_putc(struct udevice *dev, const char ch)
+{
+	struct atmel_serial_priv *priv = dev_get_priv(dev);
+
+	if (!(readl(&priv->usart->csr) & USART3_BIT(TXRDY)))
+		return -EAGAIN;
+
+	writel(ch, &priv->usart->thr);
+
+	return 0;
+}
+
+static int atmel_serial_pending(struct udevice *dev, bool input)
+{
+	struct atmel_serial_priv *priv = dev_get_priv(dev);
+	uint32_t csr = readl(&priv->usart->csr);
+
+	if (input)
+		return csr & USART3_BIT(RXRDY) ? 1 : 0;
+	else
+		return csr & USART3_BIT(TXEMPTY) ? 0 : 1;
+}
+
+static const struct dm_serial_ops atmel_serial_ops = {
+	.putc = atmel_serial_putc,
+	.pending = atmel_serial_pending,
+	.getc = atmel_serial_getc,
+	.setbrg = atmel_serial_setbrg,
+};
+
+static int atmel_serial_probe(struct udevice *dev)
+{
+	struct atmel_serial_platdata *plat = dev->platdata;
+	struct atmel_serial_priv *priv = dev_get_priv(dev);
+
+	priv->usart = (atmel_usart3_t *)plat->base_addr;
+	atmel_serial_init_internal(priv->usart);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(serial_atmel) = {
+	.name	= "serial_atmel",
+	.id	= UCLASS_SERIAL,
+	.probe = atmel_serial_probe,
+	.ops	= &atmel_serial_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+	.priv_auto_alloc_size	= sizeof(struct atmel_serial_priv),
+};
+#endif
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 0f019c8f505bf717fefe0b240d7524d09829f537..b09053f1402bdf55aa3c89abea593ab0bd82d2dd 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -6,6 +6,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <environment.h>
 #include <errno.h>
 #include <fdtdec.h>
 #include <os.h>
@@ -19,6 +20,11 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/*
+ * Table with supported baudrates (defined in config_xyz.h)
+ */
+static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE;
+
 #ifndef CONFIG_SYS_MALLOC_F_LEN
 #error "Serial is required before relocation - define CONFIG_SYS_MALLOC_F_LEN to make this work"
 #endif
@@ -160,10 +166,12 @@ void serial_stdio_init(void)
 {
 }
 
+#ifdef CONFIG_DM_STDIO
 static void serial_stub_putc(struct stdio_dev *sdev, const char ch)
 {
 	_serial_putc(sdev->priv, ch);
 }
+#endif
 
 void serial_stub_puts(struct stdio_dev *sdev, const char *str)
 {
@@ -180,11 +188,74 @@ int serial_stub_tstc(struct stdio_dev *sdev)
 	return _serial_tstc(sdev->priv);
 }
 
+/**
+ * on_baudrate() - Update the actual baudrate when the env var changes
+ *
+ * This will check for a valid baudrate and only apply it if valid.
+ */
+static int on_baudrate(const char *name, const char *value, enum env_op op,
+	int flags)
+{
+	int i;
+	int baudrate;
+
+	switch (op) {
+	case env_op_create:
+	case env_op_overwrite:
+		/*
+		 * Switch to new baudrate if new baudrate is supported
+		 */
+		baudrate = simple_strtoul(value, NULL, 10);
+
+		/* Not actually changing */
+		if (gd->baudrate == baudrate)
+			return 0;
+
+		for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) {
+			if (baudrate == baudrate_table[i])
+				break;
+		}
+		if (i == ARRAY_SIZE(baudrate_table)) {
+			if ((flags & H_FORCE) == 0)
+				printf("## Baudrate %d bps not supported\n",
+				       baudrate);
+			return 1;
+		}
+		if ((flags & H_INTERACTIVE) != 0) {
+			printf("## Switch baudrate to %d bps and press ENTER ...\n",
+			       baudrate);
+			udelay(50000);
+		}
+
+		gd->baudrate = baudrate;
+
+		serial_setbrg();
+
+		udelay(50000);
+
+		if ((flags & H_INTERACTIVE) != 0)
+			while (1) {
+				if (getc() == '\r')
+					break;
+			}
+
+		return 0;
+	case env_op_delete:
+		printf("## Baudrate may not be deleted\n");
+		return 1;
+	default:
+		return 0;
+	}
+}
+U_BOOT_ENV_CALLBACK(baudrate, on_baudrate);
+
 static int serial_post_probe(struct udevice *dev)
 {
-	struct stdio_dev sdev;
 	struct dm_serial_ops *ops = serial_get_ops(dev);
+#ifdef CONFIG_DM_STDIO
 	struct serial_dev_priv *upriv = dev->uclass_priv;
+	struct stdio_dev sdev;
+#endif
 	int ret;
 
 	/* Set the baud rate */
@@ -194,9 +265,9 @@ static int serial_post_probe(struct udevice *dev)
 			return ret;
 	}
 
+#ifdef CONFIG_DM_STDIO
 	if (!(gd->flags & GD_FLG_RELOC))
 		return 0;
-
 	memset(&sdev, '\0', sizeof(sdev));
 
 	strncpy(sdev.name, dev->name, sizeof(sdev.name));
@@ -207,7 +278,7 @@ static int serial_post_probe(struct udevice *dev)
 	sdev.getc = serial_stub_getc;
 	sdev.tstc = serial_stub_tstc;
 	stdio_register_dev(&sdev, &upriv->sdev);
-
+#endif
 	return 0;
 }
 
diff --git a/drivers/serial/serial_tegra.c b/drivers/serial/serial_tegra.c
index 7eb70e1de135eb18c9342fbabeb29ecb469406a0..b9227f05633c358a5c3918bec488a277d803601f 100644
--- a/drivers/serial/serial_tegra.c
+++ b/drivers/serial/serial_tegra.c
@@ -9,6 +9,7 @@
 #include <ns16550.h>
 #include <serial.h>
 
+#ifdef CONFIG_OF_CONTROL
 static const struct udevice_id tegra_serial_ids[] = {
 	{ .compatible = "nvidia,tegra20-uart" },
 	{ }
@@ -26,13 +27,28 @@ static int tegra_serial_ofdata_to_platdata(struct udevice *dev)
 
 	return 0;
 }
+#else
+struct ns16550_platdata tegra_serial = {
+	.base = CONFIG_SYS_NS16550_COM1,
+	.reg_shift = 2,
+	.clock = V_NS16550_CLK,
+};
+
+U_BOOT_DEVICE(ns16550_serial) = {
+	"serial_tegra20", &tegra_serial
+};
+#endif
+
 U_BOOT_DRIVER(serial_ns16550) = {
 	.name	= "serial_tegra20",
 	.id	= UCLASS_SERIAL,
+#ifdef CONFIG_OF_CONTROL
 	.of_match = tegra_serial_ids,
 	.ofdata_to_platdata = tegra_serial_ofdata_to_platdata,
 	.platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
+#endif
 	.priv_auto_alloc_size = sizeof(struct NS16550),
 	.probe = ns16550_serial_probe,
 	.ops	= &ns16550_serial_ops,
+	.flags	= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 637d420a3b2120be60cee9e751ff63403f6fd904..7a57bceb260f1c3e79fc9ed4d735f1b27c45160b 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -115,16 +115,7 @@ int spi_chip_select(struct udevice *dev)
 	return slave ? slave->cs : -ENOENT;
 }
 
-/**
- * spi_find_chip_select() - Find the slave attached to chip select
- *
- * @bus:	SPI bus to search
- * @cs:		Chip select to look for
- * @devp:	Returns the slave device if found
- * @return 0 if found, -ENODEV on error
- */
-static int spi_find_chip_select(struct udevice *bus, int cs,
-				struct udevice **devp)
+int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp)
 {
 	struct udevice *dev;
 
@@ -197,27 +188,6 @@ int spi_cs_info(struct udevice *bus, uint cs, struct spi_cs_info *info)
 	return -ENODEV;
 }
 
-int spi_bind_device(struct udevice *bus, int cs, const char *drv_name,
-		    const char *dev_name, struct udevice **devp)
-{
-	struct driver *drv;
-	int ret;
-
-	drv = lists_driver_lookup_name(drv_name);
-	if (!drv) {
-		printf("Cannot find driver '%s'\n", drv_name);
-		return -ENOENT;
-	}
-	ret = device_bind(bus, drv, dev_name, NULL, -1, devp);
-	if (ret) {
-		printf("Cannot create device named '%s' (err=%d)\n",
-		       dev_name, ret);
-		return ret;
-	}
-
-	return 0;
-}
-
 int spi_find_bus_and_cs(int busnum, int cs, struct udevice **busp,
 			struct udevice **devp)
 {
@@ -264,7 +234,7 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
 	if (ret == -ENODEV && drv_name) {
 		debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n",
 		      __func__, dev_name, busnum, cs, drv_name);
-		ret = spi_bind_device(bus, cs, drv_name, dev_name, &dev);
+		ret = device_bind_driver(bus, drv_name, dev_name, &dev);
 		if (ret)
 			return ret;
 		created = true;
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index f61acbc4fc12f5cd815965eedba5cc6f25140826..9c5a1e166f9acf428e709c74f07c6102664b8898 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -108,5 +108,6 @@ typedef struct global_data {
 #define GD_FLG_DISABLE_CONSOLE	0x00040	/* Disable console (in & out)	   */
 #define GD_FLG_ENV_READY	0x00080	/* Env. imported into hash table   */
 #define GD_FLG_SERIAL_READY	0x00100	/* Pre-reloc serial console ready  */
+#define GD_FLG_FULL_MALLOC_INIT	0x00200	/* Full malloc() is ready	   */
 
 #endif /* __ASM_GENERIC_GBL_DATA_H */
diff --git a/include/config_defaults.h b/include/config_defaults.h
index ad08c1d335d3ba29fd75dcab99f948a5c6a7e278..4d493150444f3f8827df51f9593d3ac4ed76aec0 100644
--- a/include/config_defaults.h
+++ b/include/config_defaults.h
@@ -20,4 +20,10 @@
 #define CONFIG_ZLIB 1
 #define CONFIG_PARTITIONS 1
 
+#ifndef CONFIG_SPL_BUILD
+#define CONFIG_DM_WARN
+#define CONFIG_DM_DEVICE_REMOVE
+#define CONFIG_DM_STDIO
+#endif
+
 #endif
diff --git a/include/configs/snapper9260.h b/include/configs/snapper9260.h
index 1ebee714ba3e5c568c86c99b937b7d5a2c645644..942af2e7f6244a59c2f3463b8d24dd539ea5baa7 100644
--- a/include/configs/snapper9260.h
+++ b/include/configs/snapper9260.h
@@ -15,11 +15,17 @@
 #include <asm/hardware.h>
 #include <linux/sizes.h>
 
-#define CONFIG_SYS_TEXT_BASE		0x20000000
+#define CONFIG_SYS_TEXT_BASE		0x21f00000
 
 /* ARM asynchronous clock */
 #define CONFIG_SYS_AT91_MAIN_CLOCK	18432000 /* External Crystal, in Hz */
 #define CONFIG_SYS_AT91_SLOW_CLOCK	32768
+#define CONFIG_SYS_GENERIC_BOARD
+#define CONFIG_DM
+#define CONFIG_CMD_DM
+#define CONFIG_DM_GPIO
+#define CONFIG_DM_SERIAL
+#define CONFIG_SYS_MALLOC_F_LEN		(1 << 10)
 
 /* CPU */
 #define CONFIG_ARCH_CPU_INIT
@@ -84,8 +90,10 @@
 
 /* UARTs/Serial console */
 #define CONFIG_ATMEL_USART
+#ifndef CONFIG_DM_SERIAL
 #define CONFIG_USART_BASE		ATMEL_BASE_DBGU
 #define CONFIG_USART_ID			ATMEL_ID_SYS
+#endif
 #define CONFIG_BAUDRATE			115200
 #define CONFIG_SYS_PROMPT		"Snapper> "
 
@@ -159,7 +167,7 @@
 #define CONFIG_CMD_DHCP
 #define CONFIG_CMD_FAT
 #define CONFIG_CMD_I2C
-#undef CONFIG_CMD_GPIO
+#define CONFIG_CMD_GPIO
 #define CONFIG_CMD_USB
 #define CONFIG_CMD_MII
 #define CONFIG_CMD_NAND
diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h
index 5d2b12a11d1ef1e1494fa76ae14f5eeae8401797..d690045eb046bb7d20cf9e10da1a1bab3cefe435 100644
--- a/include/configs/tegra-common.h
+++ b/include/configs/tegra-common.h
@@ -118,7 +118,9 @@
 #define CONFIG_SYS_MEMTEST_START	(NV_PA_SDRC_CS0 + 0x600000)
 #define CONFIG_SYS_MEMTEST_END		(CONFIG_SYS_MEMTEST_START + 0x100000)
 
+#ifndef CONFIG_SPL_BUILD
 #define CONFIG_USE_ARCH_MEMCPY
+#endif
 
 /*-----------------------------------------------------------------------
  * Physical Memory Map
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 44cb7ef93bfdeea682ea0602d33bf9615260ad9d..f0cc7947505176907677840bd47554b8a22973c6 100644
--- a/include/dm/device-internal.h
+++ b/include/dm/device-internal.h
@@ -87,7 +87,11 @@ int device_probe_child(struct udevice *dev, void *parent_priv);
  * @dev: Pointer to device to remove
  * @return 0 if OK, -ve on error (an error here is normally a very bad thing)
  */
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int device_remove(struct udevice *dev);
+#else
+static inline int device_remove(struct udevice *dev) { return 0; }
+#endif
 
 /**
  * device_unbind() - Unbind a device, destroying it
@@ -99,6 +103,12 @@ int device_remove(struct udevice *dev);
  */
 int device_unbind(struct udevice *dev);
 
+#ifdef CONFIG_DM_DEVICE_REMOVE
+void device_free(struct udevice *dev);
+#else
+static inline void device_free(struct udevice *dev) {}
+#endif
+
 /* Cast away any volatile pointer */
 #define DM_ROOT_NON_CONST		(((gd_t *)gd)->dm_root)
 #define DM_UCLASS_ROOT_NON_CONST	(((gd_t *)gd)->uclass_root)
diff --git a/include/dm/device.h b/include/dm/device.h
index 9ce95a834e75ee54d6d7edefb21ee0f848f97e0f..13598a15b68684ee81b023c30d8ec42e21941c2b 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -47,6 +47,7 @@ struct driver_info;
  * @name: Name of device, typically the FDT node name
  * @platdata: Configuration data for this device
  * @of_offset: Device tree node offset for this device (- for none)
+ * @of_id: Pointer to the udevice_id structure which created the device
  * @parent: Parent of this device, or NULL for the top level device
  * @priv: Private data for this device
  * @uclass: Pointer to uclass for this device
@@ -65,6 +66,7 @@ struct udevice {
 	const char *name;
 	void *platdata;
 	int of_offset;
+	const struct udevice_id *of_id;
 	struct udevice *parent;
 	void *priv;
 	struct uclass *uclass;
@@ -205,6 +207,23 @@ void *dev_get_parentdata(struct udevice *dev);
  */
 void *dev_get_priv(struct udevice *dev);
 
+/**
+ * struct dev_get_parent() - Get the parent of a device
+ *
+ * @child:	Child to check
+ * @return parent of child, or NULL if this is the root device
+ */
+struct udevice *dev_get_parent(struct udevice *child);
+
+/**
+ * dev_get_of_data() - get the device tree data used to bind a device
+ *
+ * When a device is bound using a device tree node, it matches a
+ * particular compatible string as in struct udevice_id. This function
+ * returns the associated data value for that compatible string
+ */
+ulong dev_get_of_data(struct udevice *dev);
+
 /**
  * device_get_child() - Get the child of a device by index
  *
diff --git a/include/dm/lists.h b/include/dm/lists.h
index 704e33e37fb9dadc739f71312e15f79c4f9bb651..1b50af9f23c19af91d8cc61bca853f05ec709982 100644
--- a/include/dm/lists.h
+++ b/include/dm/lists.h
@@ -60,4 +60,17 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only);
 int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 		   struct udevice **devp);
 
+/**
+ * device_bind_driver() - bind a device to a driver
+ *
+ * This binds a new device to a driver.
+ *
+ * @parent:	Parent device
+ * @drv_name:	Name of driver to attach to this parent
+ * @dev_name:	Name of the new device thus created
+ * @devp:	Returns the newly bound device
+ */
+int device_bind_driver(struct udevice *parent, const char *drv_name,
+		       const char *dev_name, struct udevice **devp);
+
 #endif
diff --git a/include/dm/util.h b/include/dm/util.h
index 6ac3a38ef008b686221a5e213e67f5e503233846..0cec17b52a413e8631bd0cc54c08d226e049515b 100644
--- a/include/dm/util.h
+++ b/include/dm/util.h
@@ -7,7 +7,13 @@
 #ifndef __DM_UTIL_H
 #define __DM_UTIL_H
 
+#ifdef CONFIG_DM_WARN
 void dm_warn(const char *fmt, ...);
+#else
+static inline void dm_warn(const char *fmt, ...)
+{
+}
+#endif
 
 #ifdef DEBUG
 void dm_dbg(const char *fmt, ...);
diff --git a/include/malloc.h b/include/malloc.h
index c33f3b494eb95b4390c5c2192295fa8f85937edb..5df634873f147f93fb444b9621abf07e1e6e21e1 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -872,33 +872,46 @@ extern Void_t*     sbrk();
 
 #else
 
-#ifdef USE_DL_PREFIX
-#define cALLOc		dlcalloc
-#define fREe		dlfree
-#define mALLOc		dlmalloc
-#define mEMALIGn	dlmemalign
-#define rEALLOc		dlrealloc
-#define vALLOc		dlvalloc
-#define pvALLOc		dlpvalloc
-#define mALLINFo	dlmallinfo
-#define mALLOPt		dlmallopt
-#else /* USE_DL_PREFIX */
-#define cALLOc		calloc
-#define fREe		free
-#define mALLOc		malloc
-#define mEMALIGn	memalign
-#define rEALLOc		realloc
-#define vALLOc		valloc
-#define pvALLOc		pvalloc
-#define mALLINFo	mallinfo
-#define mALLOPt		mallopt
-#endif /* USE_DL_PREFIX */
+#ifdef CONFIG_SYS_MALLOC_SIMPLE
+#define malloc malloc_simple
+#define realloc realloc_simple
+#define memalign memalign_simple
+static inline void free(void *ptr) {}
+void *calloc(size_t nmemb, size_t size);
+void *memalign_simple(size_t alignment, size_t bytes);
+void *realloc_simple(void *ptr, size_t size);
+#else
+
+# ifdef USE_DL_PREFIX
+# define cALLOc		dlcalloc
+# define fREe		dlfree
+# define mALLOc		dlmalloc
+# define mEMALIGn	dlmemalign
+# define rEALLOc		dlrealloc
+# define vALLOc		dlvalloc
+# define pvALLOc		dlpvalloc
+# define mALLINFo	dlmallinfo
+# define mALLOPt		dlmallopt
+# else /* USE_DL_PREFIX */
+# define cALLOc		calloc
+# define fREe		free
+# define mALLOc		malloc
+# define mEMALIGn	memalign
+# define rEALLOc		realloc
+# define vALLOc		valloc
+# define pvALLOc		pvalloc
+# define mALLINFo	mallinfo
+# define mALLOPt		mallopt
+# endif /* USE_DL_PREFIX */
 
 #endif
 
 /* Public routines */
 
-#if __STD_C
+/* Simple versions which can be used when space is tight */
+void *malloc_simple(size_t size);
+
+# if __STD_C
 
 Void_t* mALLOc(size_t);
 void    fREe(Void_t*);
@@ -913,7 +926,7 @@ size_t  malloc_usable_size(Void_t*);
 void    malloc_stats(void);
 int     mALLOPt(int, int);
 struct mallinfo mALLINFo(void);
-#else
+# else
 Void_t* mALLOc();
 void    fREe();
 Void_t* rEALLOc();
@@ -927,6 +940,7 @@ size_t  malloc_usable_size();
 void    malloc_stats();
 int     mALLOPt();
 struct mallinfo mALLINFo();
+# endif
 #endif
 
 /*
diff --git a/include/spi.h b/include/spi.h
index aa0a48ea62710dd019c77c75cc5350a9f61fb14d..5b7827113d9a693ccaa26fee8fe7d0f87105d5d1 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -534,18 +534,14 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
 int spi_chip_select(struct udevice *slave);
 
 /**
- * spi_bind_device() - bind a device to a bus's chip select
- *
- * This binds a new device to an given chip select (which must be unused).
+ * spi_find_chip_select() - Find the slave attached to chip select
  *
  * @bus:	SPI bus to search
- * @cs:		Chip select to attach to
- * @drv_name:	Name of driver to attach to this chip select
- * @dev_name:	Name of the new device thus created
- * @devp:	Returns the newly bound device
+ * @cs:		Chip select to look for
+ * @devp:	Returns the slave device if found
+ * @return 0 if found, -ENODEV on error
  */
-int spi_bind_device(struct udevice *bus, int cs, const char *drv_name,
-		    const char *dev_name, struct udevice **devp);
+int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp);
 
 /**
  * spi_ofdata_to_platdata() - decode standard SPI platform data
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index aafc4f931505227b71a01bbbcc8ef8850f8beac4..e8775df5d00b135972799040d03cc76932af759d 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -357,9 +357,9 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset,
 		slash = strrchr(prop, '/');
 		if (strcmp(slash + 1, find_name))
 			continue;
-		for (p = name; *p; p++) {
-			if (isdigit(*p)) {
-				*seqp = simple_strtoul(p, NULL, 10);
+		for (p = name + strlen(name) - 1; p > name; p--) {
+			if (!isdigit(*p)) {
+				*seqp = simple_strtoul(p + 1, NULL, 10);
 				debug("Found seq %d\n", *seqp);
 				return 0;
 			}
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index 342d82e71182b1038d899e0fc45da2142c57e82c..190544688016c7f856826896098986526f3b53b4 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -66,6 +66,7 @@ libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
 libs-$(CONFIG_SPL_FRAMEWORK) += common/spl/
 libs-$(CONFIG_SPL_LIBCOMMON_SUPPORT) += common/
 libs-$(CONFIG_SPL_LIBDISK_SUPPORT) += disk/
+libs-$(CONFIG_SPL_DM) += drivers/core/
 libs-$(CONFIG_SPL_I2C_SUPPORT) += drivers/i2c/
 libs-$(CONFIG_SPL_GPIO_SUPPORT) += drivers/gpio/
 libs-$(CONFIG_SPL_MMC_SUPPORT) += drivers/mmc/