[U-Boot] [PATCH] RFC: Add driver model support for PCI

Simon Glass sjg at chromium.org
Mon Nov 24 20:32:49 CET 2014


This is an early preview of some recent work to support PCI in driver model.
It was prompted by fiddling with bare x86 support and finding that PCI has
its own device model, but no actual storage as to what devices exist in
the system. It is not possible to specify configuration information for
devices other than in board code.

This patch is a collection of changes in core DM, sandbox and PCI code to
implement a PCI uclass and associated operations. Some basic tests are
provided but they are incomplete.

As is becoming common with DM conversions, the existing structure (here
struct pci_controller) becomes per-bus uclass data. This allows the concept
of a 'hose' (generally a PCI host controller and a bus) to continue to exist
in the interim, even if it should not be needed in the end. This makes it
much easier to convert over existing code.

There is one major core DM change tacked into this patch. The core DM
code is updated to move allocation of platform data into the bind()
stage instead of probe(). This is because with PCI we need to know the
bus address of a device (in PCI speak: device and function or devfn) before
we can probe it. Actually a similar problem arose with SPI and I2C and I
worked around it, but with evidence from PCI also it seems we should make
this change.

PCI buses are not scanned in the bind() method but only later when probe()
is called. This will be automatic if you access a bus, but it does mean that
if PCI is not used it will not be touched, in keeping with U-Boot's lazy-
init philosophy.

The existing 'pciauto' bus configuration code is still used, although it now
uses DM underneath. It works exclusively by reading and writing PCI config
and does not refer to DM data structures. In fact that file is not touched
in this patch which is an indication that a good level of compatibility is
achieved between DM and legacy PCI.

In order to support testing of PCI I/O and memory space, support has been
added to sandbox to allow mapping of these. This allows commands like 'md'
and 'iod' to display data from mapped PCI devices. Similarly, it is possible
to make changes to this space. This support relies on the existing
map_sysmem() and unmap_sysmem() calls which are now fairly widespread in
U-Boot.

Apart from the driver model tests (run with ./test/dm/test-dm.sh) you can
try out these commands which use the new 'swap_case' test device:

../u-boot -d b/sandbox/u-boot.dtb
....
=> iow.b 20000000 2
=> iod.b 20000000
0000: 02
=> mw.l 10000000 64436241
=> md.l 10000000 1
10000000: 44634261                               aBcD
=>

This shows an I/O access to 20000000, setting the value 2 which means to
swap the case. Then 'AbCd' is written to the memory space at 10000000 and
'aBcD' is read back.

The 'pci' command works to some extent.

Most existing PCI functions still work, but route through driver model.
The file drivers/pci/pci.c is replaced when driver model is enabled so not
everything is present. Also multiple bus support is untested and probably
broken. A new pci_common.c file holds functions common to driver model and
the old system, and pci_compat.c contains functions I would like to
eventually deprecate.

This series is not tested on any real hardware at this stage. Once the bare
x86 support is merged I will tidy this patch up and move it over, fix up
Kconfig, etc.

This patch is available at u-boot-dm.git branch pci-working.

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

 arch/sandbox/Kconfig                      |   6 +
 arch/sandbox/cpu/cpu.c                    |  37 ++-
 arch/sandbox/dts/sandbox.dts              |  26 ++-
 arch/sandbox/include/asm/io.h             |  16 +-
 arch/sandbox/include/asm/processor.h      |  12 +
 arch/sandbox/include/asm/test.h           |   7 +-
 arch/sandbox/include/asm/u-boot-sandbox.h |   7 +
 arch/sandbox/lib/Makefile                 |   2 +-
 arch/sandbox/lib/pci_io.c                 | 137 +++++++++++
 common/board_r.c                          |   2 +
 common/cmd_mem.c                          |   7 +-
 common/cmd_pci.c                          |  14 +-
 configs/sandbox_defconfig                 |   4 +
 doc/driver-model/pci-info.txt             |  52 +++++
 drivers/core/device.c                     |  92 ++++++--
 drivers/core/root.c                       |   3 +
 drivers/misc/Makefile                     |   1 +
 drivers/misc/swap_case.c                  | 284 +++++++++++++++++++++++
 drivers/pci/Kconfig                       |  22 ++
 drivers/pci/Makefile                      |  10 +-
 drivers/pci/pci-emul-uclass.c             |  66 ++++++
 drivers/pci/pci-uclass.c                  | 374 ++++++++++++++++++++++++++++++
 drivers/pci/pci.c                         |  72 +-----
 drivers/pci/pci_common.c                  |  72 ++++++
 drivers/pci/pci_compat.c                  |  67 ++++++
 drivers/pci/pci_sandbox.c                 | 171 ++++++++++++++
 include/configs/sandbox.h                 |   5 +-
 include/dm/device.h                       |  32 +++
 include/dm/uclass-id.h                    |   3 +
 include/dm/uclass.h                       |   4 +
 include/fdtdec.h                          |  11 +
 include/pci.h                             | 156 ++++++++++++-
 lib/fdtdec.c                              |   2 +-
 test/dm/Makefile                          |   1 +
 test/dm/test-fdt.c                        |   4 +-
 test/dm/test.dts                          |  17 ++
 36 files changed, 1687 insertions(+), 111 deletions(-)
 create mode 100644 arch/sandbox/include/asm/processor.h
 create mode 100644 arch/sandbox/lib/pci_io.c
 create mode 100644 doc/driver-model/pci-info.txt
 create mode 100644 drivers/misc/swap_case.c
 create mode 100644 drivers/pci/pci-emul-uclass.c
 create mode 100644 drivers/pci/pci-uclass.c
 create mode 100644 drivers/pci/pci_common.c
 create mode 100644 drivers/pci/pci_compat.c
 create mode 100644 drivers/pci/pci_sandbox.c

diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index 3057325..8d1ec1d 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -10,4 +10,10 @@ config SYS_BOARD
 config SYS_CONFIG_NAME
 	default "sandbox"
 
+config PCI
+	bool "PCI support"
+	help
+	  Enable support for PCI (Peripheral Interconnect Bus), a type of bus
+	  used on some devices to allow the CPU to communicate with
+
 endmenu
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c
index 1aa397c..1e67a31 100644
--- a/arch/sandbox/cpu/cpu.c
+++ b/arch/sandbox/cpu/cpu.c
@@ -2,7 +2,7 @@
  * Copyright (c) 2011 The Chromium OS Authors.
  * SPDX-License-Identifier:	GPL-2.0+
  */
-
+#define DEBUG
 #include <common.h>
 #include <dm/root.h>
 #include <os.h>
@@ -10,6 +10,13 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* Enable access to PCI memory with map_sysmem() */
+static bool enable_pci_map;
+
+/* Last device that was mapped into memory, and length of mapping */
+static struct udevice *map_dev;
+unsigned long map_len;
+
 void reset_cpu(ulong ignored)
 {
 	if (state_uninit())
@@ -59,9 +66,37 @@ int cleanup_before_linux(void)
 
 void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags)
 {
+#ifdef CONFIG_PCI
+	unsigned long plen = len;
+	void *ptr;
+
+	map_dev = NULL;
+	if (enable_pci_map && !pci_map_physmem(paddr, &len, &map_dev, &ptr)) {
+		if (plen != len) {
+			printf("%s: Warning: partial map at %x, wanted %lx, got %lx\n",
+			       __func__, paddr, len, plen);
+		}
+		map_len = len;
+		return ptr;
+	}
+#endif
+
 	return (void *)(gd->arch.ram_buf + paddr);
 }
 
+void unmap_physmem(const void *vaddr, unsigned long flags)
+{
+	if (map_dev) {
+		pci_unmap_physmem(vaddr, map_len, map_dev);
+		map_dev = NULL;
+	}
+}
+
+void sandbox_set_enable_pci_map(int enable)
+{
+	enable_pci_map = enable;
+}
+
 phys_addr_t map_to_sysmem(const void *ptr)
 {
 	return (u8 *)ptr - gd->arch.ram_buf;
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index 7614715..c8df40b 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -1,8 +1,14 @@
 /dts-v1/;
 
+#define MBUS_ID(target, attributes) (((target) << 24) | ((attributes) << 16))
+
 / {
 	#address-cells = <1>;
-	#size-cells = <0>;
+	#size-cells = <1>;
+
+	aliases {
+		pci0 = &pci;
+	};
 
 	chosen {
 		stdout-path = "/serial";
@@ -137,7 +143,7 @@
 	spi at 0 {
 		#address-cells = <1>;
 		#size-cells = <0>;
-		reg = <0>;
+		reg = <0 0>;
 		compatible = "sandbox,spi";
 		cs-gpios = <0>, <&gpio_a 0>;
 		flash at 0 {
@@ -157,4 +163,20 @@
 		};
 	};
 
+	pci: pci-controller {
+		compatible = "sandbox,pci";
+		device_type = "pci";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000
+				0x01000000 0 0x20000000 0x20000000 0 0x2000>;
+		pci at 1f,0 {
+			compatible = "pci-generic";
+			reg = <0xf800 0 0 0 0>;
+			emul at 1f,0 {
+				compatible = "sandbox,swap-case";
+			};
+		};
+	};
+
 };
diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h
index 895fcb8..5b87fde 100644
--- a/arch/sandbox/include/asm/io.h
+++ b/arch/sandbox/include/asm/io.h
@@ -22,10 +22,7 @@ void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags);
 /*
  * Take down a mapping set up by map_physmem().
  */
-static inline void unmap_physmem(void *vaddr, unsigned long flags)
-{
-
-}
+void unmap_physmem(const void *vaddr, unsigned long flags);
 
 /* For sandbox, we want addresses to point into our RAM buffer */
 static inline void *map_sysmem(phys_addr_t paddr, unsigned long len)
@@ -33,8 +30,10 @@ static inline void *map_sysmem(phys_addr_t paddr, unsigned long len)
 	return map_physmem(paddr, len, MAP_WRBACK);
 }
 
+/* Remove a previous mapping */
 static inline void unmap_sysmem(const void *vaddr)
 {
+	unmap_physmem(vaddr, MAP_WRBACK);
 }
 
 /* Map from a pointer to our RAM buffer */
@@ -48,6 +47,15 @@ phys_addr_t map_to_sysmem(const void *ptr);
 #define writew(v, addr)
 #define writel(v, addr)
 
+/* I/O access functions */
+int inl(unsigned int addr);
+int inw(unsigned int addr);
+int inb(unsigned int addr);
+
+void outl(unsigned int value, unsigned int addr);
+void outw(unsigned int value, unsigned int addr);
+void outb(unsigned int value, unsigned int addr);
+
 #include <iotrace.h>
 
 #endif
diff --git a/arch/sandbox/include/asm/processor.h b/arch/sandbox/include/asm/processor.h
new file mode 100644
index 0000000..3c1794e
--- /dev/null
+++ b/arch/sandbox/include/asm/processor.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _ASM_PROCESSOR_H
+#define _ASM_PROCESSOR_H
+
+/* This file is required for PCI */
+
+#endif
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index d7f7bb5..fa61b5e 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -10,6 +10,11 @@
 #define __ASM_TEST_H
 
 /* The sandbox driver always permits an I2C device with this address */
-#define SANDBOX_I2C_TEST_ADDR	0x59
+#define SANDBOX_I2C_TEST_ADDR		0x59
+
+#define SANDBOX_PCI_VENDOR_ID		0x1234
+#define SANDBOX_PCI_DEVICE_ID		0x5678
+#define SANDBOX_PCI_CLASS_CODE		PCI_CLASS_CODE_COMM
+#define SANDBOX_PCI_CLASS_SUB_CODE	PCI_CLASS_SUB_CODE_COMM_SERIAL
 
 #endif
diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h
index d2f1b65..1a29cf1 100644
--- a/arch/sandbox/include/asm/u-boot-sandbox.h
+++ b/arch/sandbox/include/asm/u-boot-sandbox.h
@@ -28,4 +28,11 @@ int cleanup_before_linux(void);
 /* drivers/video/sandbox_sdl.c */
 int sandbox_lcd_sdl_early_init(void);
 
+int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp,
+		    struct udevice **devp, void **ptrp);
+int pci_unmap_physmem(const void *addr, unsigned long len,
+		      struct udevice *dev);
+
+void sandbox_set_enable_pci_map(int enable);
+
 #endif	/* _U_BOOT_SANDBOX_H_ */
diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile
index 4c1a38d..dedc752 100644
--- a/arch/sandbox/lib/Makefile
+++ b/arch/sandbox/lib/Makefile
@@ -7,5 +7,5 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-
 obj-y	+= interrupts.o
+obj-y	+= pci_io.o
diff --git a/arch/sandbox/lib/pci_io.c b/arch/sandbox/lib/pci_io.c
new file mode 100644
index 0000000..601d969
--- /dev/null
+++ b/arch/sandbox/lib/pci_io.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/*
+ * IO space access commands.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <asm/io.h>
+
+int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp,
+		    struct udevice **devp, void **ptrp)
+{
+	struct udevice *dev;
+	int ret;
+
+	*ptrp = 0;
+	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
+	     dev;
+	     uclass_next_device(&dev)) {
+		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);
+
+		if (!ops || !ops->map_physmem)
+			continue;
+		ret = (ops->map_physmem)(dev, paddr, lenp, ptrp);
+		if (ret)
+			continue;
+		*devp = dev;
+		return 0;
+	}
+
+	debug("%s: failed: addr=%x\n", __func__, paddr);
+	return -ENOSYS;
+}
+
+int pci_unmap_physmem(const void *vaddr, unsigned long len,
+		      struct udevice *dev)
+{
+	struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);
+
+	if (!ops || !ops->unmap_physmem)
+		return -ENOSYS;
+	return (ops->unmap_physmem)(vaddr, len, dev);
+}
+
+static int pci_io_read(unsigned int addr, ulong *valuep, pci_size_t size)
+{
+	struct udevice *dev;
+	int ret;
+
+	*valuep = pci_get_ff(size);
+	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
+	     dev;
+	     uclass_next_device(&dev)) {
+		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);
+
+		if (ops && ops->read_io) {
+			ret = (ops->read_io)(dev, addr, valuep, size);
+			if (!ret)
+				return 0;
+		}
+	}
+
+	debug("%s: failed: addr=%x\n", __func__, addr);
+	return -ENOSYS;
+}
+
+static int pci_io_write(unsigned int addr, ulong value, pci_size_t size)
+{
+	struct udevice *dev;
+	int ret;
+
+	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
+	     dev;
+	     uclass_next_device(&dev)) {
+		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);
+
+		if (ops && ops->write_io) {
+			ret = (ops->write_io)(dev, addr, value, size);
+			if (!ret)
+				return 0;
+		}
+	}
+
+	debug("%s: failed: addr=%x, value=%lx\n", __func__, addr, value);
+	return -ENOSYS;
+}
+
+int inl(unsigned int addr)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_io_read(addr, &value, PCI_SIZE_32);
+
+	return ret ? 0 : value;
+}
+
+int inw(unsigned int addr)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_io_read(addr, &value, PCI_SIZE_16);
+
+	return ret ? 0 : value;
+}
+
+int inb(unsigned int addr)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_io_read(addr, &value, PCI_SIZE_8);
+
+	return ret ? 0 : value;
+}
+
+void outl(unsigned int value, unsigned int addr)
+{
+	pci_io_write(addr, value, PCI_SIZE_32);
+}
+
+void outw(unsigned int value, unsigned int addr)
+{
+	pci_io_write(addr, value, PCI_SIZE_16);
+}
+
+void outb(unsigned int value, unsigned int addr)
+{
+	pci_io_write(addr, value, PCI_SIZE_8);
+}
diff --git a/common/board_r.c b/common/board_r.c
index 8625a90..1448f42 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -223,7 +223,9 @@ static int initr_unlock_ram_in_cache(void)
 #ifdef CONFIG_PCI
 static int initr_pci(void)
 {
+#ifndef CONFIG_DM_PCI
 	pci_init();
+#endif
 
 	return 0;
 }
diff --git a/common/cmd_mem.c b/common/cmd_mem.c
index bcb3ee3..855aa57 100644
--- a/common/cmd_mem.c
+++ b/common/cmd_mem.c
@@ -165,7 +165,7 @@ static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 #endif
 	ulong	addr, count;
 	int	size;
-	void *buf;
+	void *buf, *start;
 	ulong bytes;
 
 	if ((argc < 3) || (argc > 4))
@@ -197,7 +197,8 @@ static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	}
 
 	bytes = size * count;
-	buf = map_sysmem(addr, bytes);
+	start = map_sysmem(addr, bytes);
+	buf = start;
 	while (count-- > 0) {
 		if (size == 4)
 			*((u32 *)buf) = (u32)writeval;
@@ -211,7 +212,7 @@ static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 			*((u8 *)buf) = (u8)writeval;
 		buf += size;
 	}
-	unmap_sysmem(buf);
+	unmap_sysmem(start);
 	return 0;
 }
 
diff --git a/common/cmd_pci.c b/common/cmd_pci.c
index a1ba42e..e847ada 100644
--- a/common/cmd_pci.c
+++ b/common/cmd_pci.c
@@ -47,6 +47,7 @@ void pciinfo(int BusNum, int ShortPCIListing)
 	unsigned char HeaderType;
 	unsigned short VendorID;
 	pci_dev_t dev;
+	int ret;
 
 	printf("Scanning PCI devices on bus %d\n", BusNum);
 
@@ -67,7 +68,10 @@ void pciinfo(int BusNum, int ShortPCIListing)
 
 			dev = PCI_BDF(BusNum, Device, Function);
 
-			pci_read_config_word(dev, PCI_VENDOR_ID, &VendorID);
+			ret = pci_read_config_word(dev, PCI_VENDOR_ID,
+						   &VendorID);
+			if (ret)
+				goto error;
 			if ((VendorID == 0xFFFF) || (VendorID == 0x0000))
 				continue;
 
@@ -84,8 +88,12 @@ void pciinfo(int BusNum, int ShortPCIListing)
 				       BusNum, Device, Function);
 				pci_header_show(dev);
 			}
-	    }
-    }
+		}
+	}
+
+	return;
+error:
+	printf("Cannot read bus configuration: %d\n", ret);
 }
 
 
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 47d8400..ca47480 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -1,3 +1,7 @@
 CONFIG_OF_CONTROL=y
 CONFIG_OF_HOSTFILE=y
 CONFIG_DEFAULT_DEVICE_TREE="sandbox"
+CONFIG_DM=y
+CONFIG_PCI=y
+CONFIG_DM_PCI=y
+CONFIG_PCI_SANDBOX=y
diff --git a/doc/driver-model/pci-info.txt b/doc/driver-model/pci-info.txt
new file mode 100644
index 0000000..ac82e7e
--- /dev/null
+++ b/doc/driver-model/pci-info.txt
@@ -0,0 +1,52 @@
+PCI with Driver Model
+=====================
+
+How busses are scanned
+----------------------
+
+Any config read will end up at pci_read_config(). This uses uclass_get_device by seq() to get the PCI bus for a particular bus number. Bus number 0 will need to be requested first, and the alias in the device tree file will point to the correct device:
+
+
+	aliases {
+		pci0 = &pci;
+	};
+
+	pci: pci-controller {
+		compatible = "sandbox,pci";
+		...
+	};
+
+The call to uclass_get_device by seq() will caus the PCI bus to be probed. This does a scan of the bus to locate available devices. These devices are bound to their appropriate driver if availbale. If there is no driver, then they are bound to a generic PCI driver which does nothing.
+
+After probing a bus the available devices will appear in the device tree under that bus.
+
+Note that this is all done on a lazy basis, as needed, so until something is touched on PCI it will not be probed.
+
+PCI devices can appear in the device tree. If they do this servers to specify the driver to use for the device. In this case they will be bound at start-up.
+
+
+Sandbox
+-------
+
+With sandbox we need a device emulator for each device on the bus since there is no real PCI bus. This works by looking in the device tree node for a driver. For example:
+
+
+	pci at 1f,0 {
+		compatible = "pci-generic";
+		reg = <0xf800 0 0 0 0>;
+		emul at 1f,0 {
+			compatible = "sandbox,swap-case";
+		};
+	};
+
+This means that there is a 'sandbox,swap-case' driver at that bus position.
+Note that the first cell in the 'reg' value is the bus/device/function. See
+PCI_BDF() for the encoding.
+
+When this bus is scanned we will end up with something like this:
+
+`- * pci-controller @ 05c660c8, 0
+ `-   pci at 1f,0 @ 05c661c8, 63488
+  `-   emul at 1f,0 @ 05c662c8
+
+When accesses go to the pci at 1f,0 device they are forwarded to its child, the emulator.
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 49faa29..5faf2d7 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -118,8 +118,27 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name,
 #else
 	dev->req_seq = -1;
 #endif
-	if (!dev->platdata && drv->platdata_auto_alloc_size)
+	if (!dev->platdata && drv->platdata_auto_alloc_size) {
 		dev->flags |= DM_FLAG_ALLOC_PDATA;
+		dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
+		if (!dev->platdata) {
+			ret = -ENOMEM;
+			goto fail_alloc1;
+		}
+	}
+	if (dev->parent && !dev->parent_platdata) {
+		int size = dev->parent->driver->
+				per_child_platdata_auto_alloc_size;
+
+		if (size) {
+			dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
+			dev->parent_platdata = calloc(1, size);
+			if (!dev->parent_platdata) {
+				ret = -ENOMEM;
+				goto fail_alloc2;
+			}
+		}
+	}
 
 	/* put dev into parent's successor list */
 	if (parent)
@@ -127,27 +146,50 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name,
 
 	ret = uclass_bind_device(dev);
 	if (ret)
-		goto fail_bind;
+		goto fail_uclass_bind;
 
 	/* if we fail to bind we remove device from successors and free it */
 	if (drv->bind) {
 		ret = drv->bind(dev);
-		if (ret) {
-			if (uclass_unbind_device(dev)) {
-				dm_warn("Failed to unbind dev '%s' on error path\n",
-					dev->name);
-			}
+		if (ret)
 			goto fail_bind;
-		}
 	}
+	if (dev->parent && dev->parent->driver->child_post_bind) {
+		ret = dev->parent->driver->child_post_bind(dev);
+		if (ret)
+			goto fail_child_post_bind;
+	}
+
 	if (parent)
 		dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
 	*devp = dev;
 
 	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);
+	}
+
 fail_bind:
+	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;
+	}
+fail_alloc2:
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		free(dev->platdata);
+		dev->platdata = NULL;
+	}
+fail_alloc1:
 	free(dev);
 	return ret;
 }
@@ -191,6 +233,14 @@ int device_unbind(struct udevice *dev)
 	if (ret)
 		return ret;
 
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		free(dev->platdata);
+		dev->platdata = NULL;
+	}
+	if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
+		free(dev->parent_platdata);
+		dev->parent_platdata = NULL;
+	}
 	ret = uclass_unbind_device(dev);
 	if (ret)
 		return ret;
@@ -214,10 +264,6 @@ static void device_free(struct udevice *dev)
 		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);
@@ -257,13 +303,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
 		}
 	}
 	/* Allocate private data if requested */
-	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
-		dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
-		if (!dev->platdata) {
-			ret = -ENOMEM;
-			goto fail;
-		}
-	}
 	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
 	if (size) {
 		dev->uclass_priv = calloc(1, size);
@@ -409,6 +448,16 @@ void *dev_get_platdata(struct udevice *dev)
 	return dev->platdata;
 }
 
+void *dev_get_parent_platdata(struct udevice *dev)
+{
+	if (!dev) {
+		dm_warn("%s: null device", __func__);
+		return NULL;
+	}
+
+	return dev->parent_platdata;
+}
+
 void *dev_get_priv(struct udevice *dev)
 {
 	if (!dev) {
@@ -548,3 +597,8 @@ int device_find_next_child(struct udevice **devp)
 
 	return 0;
 }
+
+enum uclass_id device_get_uclass_id(struct udevice *dev)
+{
+	return dev->uclass->uc_drv->id;
+}
diff --git a/drivers/core/root.c b/drivers/core/root.c
index a328a48..8dc615a 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -49,6 +49,9 @@ int dm_init(void)
 	ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
 	if (ret)
 		return ret;
+#ifdef CONFIG_OF_CONTROL
+	DM_ROOT_NON_CONST->of_offset = 0;
+#endif
 	ret = device_probe(DM_ROOT_NON_CONST);
 	if (ret)
 		return ret;
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2f2e48f..01ecf30 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
 obj-$(CONFIG_NS87308) += ns87308.o
 obj-$(CONFIG_PDSP188x) += pdsp188x.o
 obj-$(CONFIG_STATUS_LED) += status_led.o
+obj-$(CONFIG_SANDBOX) += swap_case.o
 obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
 obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c
new file mode 100644
index 0000000..50f3b1d
--- /dev/null
+++ b/drivers/misc/swap_case.c
@@ -0,0 +1,284 @@
+/*
+ * PCI emulation device which swaps the case of text
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+#include <asm/test.h>
+#include <linux/ctype.h>
+
+/**
+ * struct swap_case_platdata - platform data for this device
+ *
+ * @command:	Current PCI command value
+ * @bar:	Current base address values
+ */
+struct swap_case_platdata {
+	u16 command;
+	u32 bar[2];
+};
+
+#define offset_to_barnum(offset)	\
+		(((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32))
+
+enum {
+	MEM_TEXT_SIZE	= 0x100,
+};
+
+enum swap_case_op {
+	OP_TO_LOWER,
+	OP_TO_UPPER,
+	OP_SWAP,
+};
+
+static struct pci_bar {
+	int type;
+	u32 size;
+} barinfo[] = {
+	{ PCI_BASE_ADDRESS_SPACE_IO, 1 },
+	{ PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+struct swap_case_priv {
+	enum swap_case_op op;
+	char mem_text[MEM_TEXT_SIZE];
+};
+
+static int sandbox_swap_case_get_devfn(struct udevice *dev)
+{
+	struct pci_child_platdata *plat = dev_get_parent_platdata(dev);
+
+	return plat->devfn;
+}
+
+static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
+					 ulong *valuep, enum pci_size_t size)
+{
+	struct swap_case_platdata *plat = dev_get_platdata(emul);
+
+	switch (offset) {
+	case PCI_COMMAND:
+		*valuep = plat->command;
+		break;
+	case PCI_HEADER_TYPE:
+		*valuep = 0;
+		break;
+	case PCI_VENDOR_ID:
+		*valuep = SANDBOX_PCI_VENDOR_ID;
+		break;
+	case PCI_DEVICE_ID:
+		*valuep = SANDBOX_PCI_DEVICE_ID;
+		break;
+	case PCI_CLASS_DEVICE:
+		if (size == PCI_SIZE_8) {
+			*valuep = SANDBOX_PCI_CLASS_SUB_CODE;
+		} else {
+			*valuep = (SANDBOX_PCI_CLASS_CODE << 8) |
+					SANDBOX_PCI_CLASS_SUB_CODE;
+		}
+		break;
+	case PCI_CLASS_CODE:
+		*valuep = SANDBOX_PCI_CLASS_CODE;
+		break;
+	case PCI_BASE_ADDRESS_0:
+	case PCI_BASE_ADDRESS_1:
+	case PCI_BASE_ADDRESS_2:
+	case PCI_BASE_ADDRESS_3:
+	case PCI_BASE_ADDRESS_4:
+	case PCI_BASE_ADDRESS_5: {
+		int barnum;
+		u32 *bar, result;
+
+		barnum = offset_to_barnum(offset);
+		bar = &plat->bar[barnum];
+
+		result = *bar;
+		if (*bar == 0xffffffff) {
+			if (barinfo[barnum].type) {
+				result = (~(barinfo[barnum].size - 1) &
+					PCI_BASE_ADDRESS_IO_MASK) |
+					PCI_BASE_ADDRESS_SPACE_IO;
+			} else {
+				result = (~(barinfo[barnum].size - 1) &
+					PCI_BASE_ADDRESS_MEM_MASK) |
+					PCI_BASE_ADDRESS_MEM_TYPE_32;
+			}
+		}
+		debug("r bar %d=%x\n", barnum, result);
+		*valuep = result;
+		break;
+	}
+	}
+
+	return 0;
+}
+
+static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
+					  ulong value, enum pci_size_t size)
+{
+	struct swap_case_platdata *plat = dev_get_platdata(emul);
+
+	switch (offset) {
+	case PCI_COMMAND:
+		plat->command = value;
+		break;
+	case PCI_BASE_ADDRESS_0:
+	case PCI_BASE_ADDRESS_1: {
+		int barnum;
+		u32 *bar;
+
+		barnum = offset_to_barnum(offset);
+		bar = &plat->bar[barnum];
+
+		debug("w bar %d=%lx\n", barnum, value);
+		*bar = value;
+		break;
+	}
+	}
+
+	return 0;
+}
+
+static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr,
+				      int *barnump, unsigned int *offsetp)
+{
+	struct swap_case_platdata *plat = dev_get_platdata(emul);
+	int barnum;
+
+	for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
+		unsigned int size = barinfo[barnum].size;
+
+		if (addr >= plat->bar[barnum] &&
+		    addr < plat->bar[barnum] + size) {
+			*barnump = barnum;
+			*offsetp = addr - plat->bar[barnum];
+			return 0;
+		}
+	}
+	*barnump = -1;
+
+	return -ENOENT;
+}
+
+static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
+{
+	for (; len > 0; len--, str++) {
+		switch (op) {
+		case OP_TO_UPPER:
+			*str = toupper(*str);
+			break;
+		case OP_TO_LOWER:
+			*str = tolower(*str);
+			break;
+		case OP_SWAP:
+			if (isupper(*str))
+				*str = tolower(*str);
+			else
+				*str = toupper(*str);
+			break;
+		}
+	}
+}
+
+int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
+			      ulong *valuep, enum pci_size_t size)
+{
+	struct swap_case_priv *priv = dev_get_priv(dev);
+	unsigned int offset;
+	int barnum;
+	int ret;
+
+	ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
+	if (ret)
+		return ret;
+
+	if (barnum == 0 && offset == 0)
+		*valuep = (*valuep & ~0xff) | priv->op;
+
+	return 0;
+}
+
+int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
+			       ulong value, enum pci_size_t size)
+{
+	struct swap_case_priv *priv = dev_get_priv(dev);
+	unsigned int offset;
+	int barnum;
+	int ret;
+
+	ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
+	if (ret)
+		return ret;
+	if (barnum == 0 && offset == 0)
+		priv->op = value;
+
+	return 0;
+}
+
+static int sandbox_swap_case_map_physmem(struct udevice *dev,
+		phys_addr_t addr, unsigned long *lenp, void **ptrp)
+{
+	struct swap_case_priv *priv = dev_get_priv(dev);
+	unsigned int offset, avail;
+	int barnum;
+	int ret;
+
+	ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
+	if (ret)
+		return ret;
+	if (barnum == 1) {
+		*ptrp = priv->mem_text + offset;
+		avail = barinfo[1].size - offset;
+		if (avail > barinfo[1].size)
+			*lenp = 0;
+		else
+			*lenp = min(*lenp, avail);
+
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int sandbox_swap_case_unmap_physmem(const void *vaddr,
+				unsigned long len, struct udevice *dev)
+{
+	struct swap_case_priv *priv = dev_get_priv(dev);
+
+	sandbox_swap_case_do_op(priv->op, (void *)vaddr, len);
+
+	return 0;
+}
+
+struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
+	.get_devfn = sandbox_swap_case_get_devfn,
+	.read_config = sandbox_swap_case_read_config,
+	.write_config = sandbox_swap_case_write_config,
+	.read_io = sandbox_swap_case_read_io,
+	.write_io = sandbox_swap_case_write_io,
+	.map_physmem = sandbox_swap_case_map_physmem,
+	.unmap_physmem = sandbox_swap_case_unmap_physmem,
+};
+
+static const struct udevice_id sandbox_swap_case_ids[] = {
+	{ .compatible = "sandbox,swap-case" },
+	{ }
+};
+
+U_BOOT_DRIVER(sandbox_swap_case_emul) = {
+	.name		= "sandbox_swap_case_emul",
+	.id		= UCLASS_PCI_EMUL,
+	.of_match	= sandbox_swap_case_ids,
+	.ops		= &sandbox_swap_case_emul_ops,
+	.priv_auto_alloc_size = sizeof(struct swap_case_priv),
+	.platdata_auto_alloc_size = sizeof(struct swap_case_platdata),
+};
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index e69de29..167d405 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -0,0 +1,22 @@
+menu "PCI"
+
+config DM_PCI
+	bool "Enable driver mode for PCI"
+	depends on DM
+	help
+	  Use driver model for PCI. Driver model is the new method for
+	  orgnising devices in U-Boot. For PCI, driver model keeps track of
+	  available PCI devices, allows scanning of PCI buses and provides
+	  device configuration support.
+
+config PCI_SANDBOX
+	bool "Sandbox PCI support"
+	depends on SANDBOX && DM_PCI
+	help
+	  Support PCI on sandbox, as an emulated bus. This permits testing of
+	  PCI feature such as bus scanning, device configuration and device
+	  access. The available (emulated) devices are defined statically in
+	  the device tree but the normal PCI scan technique is used to find
+	  then.
+
+endmenu
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 55d6a9b..11bae12 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -5,13 +5,21 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+ifneq ($(CONFIG_DM_PCI),)
+obj-$(CONFIG_PCI) += pci-uclass.o pci_compat.o
+obj-$(CONFIG_SANDBOX) += pci-emul-uclass.o
+else
+obj-$(CONFIG_PCI) += pci.o pci_rom.o
+endif
+obj-$(CONFIG_PCI) += pci_common.o pci_auto.o
+
 obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
-obj-$(CONFIG_PCI) += pci.o pci_auto.o pci_rom.o
 obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o
 obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o
 obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
 obj-$(CONFIG_PCIE_IMX) += pcie_imx.o
 obj-$(CONFIG_FTPCI100) += pci_ftpci100.o
+obj-$(CONFIG_PCI_SANDBOX) += pci_sandbox.o
 obj-$(CONFIG_SH4_PCI) += pci_sh4.o
 obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
 obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o
diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c
new file mode 100644
index 0000000..2baeab6
--- /dev/null
+++ b/drivers/pci/pci-emul-uclass.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <pci.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sandbox_pci_priv {
+	int dev_count;
+};
+
+int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
+			 struct udevice **emulp)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = pci_bus_find_devfn(bus, find_devfn, &dev);
+	if (ret) {
+		debug("%s: Could not find emulator for dev %x\n", __func__,
+		      find_devfn);
+		return ret;
+	}
+
+	ret = device_find_first_child(dev, emulp);
+	if (ret)
+		return ret;
+
+	return *emulp ? 0 : -ENODEV;
+}
+
+static int sandbox_pci_emul_post_probe(struct udevice *dev)
+{
+	struct sandbox_pci_priv *priv = dev->uclass->priv;
+
+	priv->dev_count++;
+	sandbox_set_enable_pci_map(true);
+
+	return 0;
+}
+
+static int sandbox_pci_emul_pre_remove(struct udevice *dev)
+{
+	struct sandbox_pci_priv *priv = dev->uclass->priv;
+
+	priv->dev_count--;
+	sandbox_set_enable_pci_map(priv->dev_count > 0);
+
+	return 0;
+}
+
+UCLASS_DRIVER(pci_emul) = {
+	.id		= UCLASS_PCI_EMUL,
+	.name		= "pci_emul",
+	.post_probe	= sandbox_pci_emul_post_probe,
+	.pre_remove	= sandbox_pci_emul_pre_remove,
+	.priv_auto_alloc_size	= sizeof(struct sandbox_pci_priv),
+};
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
new file mode 100644
index 0000000..a5bd8df
--- /dev/null
+++ b/drivers/pci/pci-uclass.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pci_get_ff(enum pci_size_t size)
+{
+	switch (size) {
+	case PCI_SIZE_8:
+		return 0xff;
+	case PCI_SIZE_16:
+		return 0xffff;
+	default:
+		return 0xffffffff;
+	}
+}
+
+int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
+		       struct udevice **devp)
+{
+	struct udevice *dev;
+
+	for (device_find_first_child(bus, &dev);
+	     dev;
+	     device_find_next_child(&dev)) {
+		struct pci_child_platdata *pplat;
+
+		pplat = dev_get_parent_platdata(dev);
+		if (pplat && pplat->devfn == find_devfn) {
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+int pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp)
+{
+	struct udevice *bus;
+	int ret;
+
+	ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+	if (ret)
+		return ret;
+	return pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), devp);
+}
+
+static int pci_device_matches_ids(struct udevice *dev,
+				  struct pci_device_id *ids)
+{
+	struct pci_child_platdata *pplat;
+	int i;
+
+	pplat = dev_get_parent_platdata(dev);
+	if (!pplat)
+		return -EINVAL;
+	for (i = 0; ids[i].vendor != 0; i++) {
+		if (pplat->vendor == ids[i].vendor &&
+		    pplat->device == ids[i].device)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids,
+			 int *indexp, struct udevice **devp)
+{
+	struct udevice *dev;
+
+	/* Scan all devices on this bus */
+	for (device_find_first_child(bus, &dev);
+	     dev;
+	     device_find_next_child(&dev)) {
+		if (pci_device_matches_ids(dev, ids) >= 0) {
+			if ((*indexp)-- <= 0) {
+				*devp = dev;
+				return 0;
+			}
+		}
+	}
+
+	return -ENODEV;
+}
+
+int pci_find_device_id(struct pci_device_id *ids, int index,
+		       struct udevice **devp)
+{
+	struct udevice *bus;
+
+	/* Scan all known buses */
+	for (uclass_first_device(UCLASS_PCI, &bus);
+	     bus;
+	     uclass_next_device(&bus)) {
+		if (!pci_bus_find_devices(bus, ids, &index, devp))
+			return 0;
+	}
+	*devp = NULL;
+
+	return -ENODEV;
+}
+
+int pci_bus_write_config(struct udevice *bus, pci_dev_t devfn, int offset,
+			 unsigned long value, enum pci_size_t size)
+{
+	struct dm_pci_ops *ops;
+
+	ops = pci_get_ops(bus);
+	if (!ops->write_config)
+		return -ENOSYS;
+	return ops->write_config(bus, devfn, offset, value, size);
+}
+
+int pci_write_config(pci_dev_t bdf, int offset, unsigned long value,
+		     enum pci_size_t size)
+{
+	struct udevice *bus;
+	int ret;
+
+	ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+	if (ret)
+		return ret;
+
+	return pci_bus_write_config(bus, PCI_MASK_BUS(bdf), offset, value,
+				    size);
+}
+
+int pci_write_config32(pci_dev_t devfn, int offset, u32 value)
+{
+	return pci_write_config(devfn, offset, value, PCI_SIZE_8);
+}
+
+int pci_write_config16(pci_dev_t devfn, int offset, u16 value)
+{
+	return pci_write_config(devfn, offset, value, PCI_SIZE_16);
+}
+
+int pci_write_config8(pci_dev_t devfn, int offset, u8 value)
+{
+	return pci_write_config(devfn, offset, value, PCI_SIZE_8);
+}
+
+int pci_bus_read_config(struct udevice *bus, pci_dev_t devfn, int offset,
+			unsigned long *valuep, enum pci_size_t size)
+{
+	struct dm_pci_ops *ops;
+
+	ops = pci_get_ops(bus);
+	if (!ops->read_config)
+		return -ENOSYS;
+	return ops->read_config(bus, devfn, offset, valuep, size);
+}
+
+int pci_read_config(pci_dev_t bdf, int offset, unsigned long *valuep,
+		    enum pci_size_t size)
+{
+	struct udevice *bus;
+	int ret;
+
+	ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
+	if (ret)
+		return ret;
+
+	return pci_bus_read_config(bus, PCI_MASK_BUS(bdf), offset, valuep,
+				   size);
+}
+
+int pci_read_config32(pci_dev_t bdf, int offset, u32 *valuep)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_read_config(bdf, offset, &value, PCI_SIZE_32);
+	if (ret)
+		return ret;
+	*valuep = value;
+
+	return 0;
+}
+
+int pci_read_config16(pci_dev_t bdf, int offset, u16 *valuep)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_read_config(bdf, offset, &value, PCI_SIZE_16);
+	if (ret)
+		return ret;
+	*valuep = value;
+
+	return 0;
+}
+
+int pci_read_config8(pci_dev_t bdf, int offset, u8 *valuep)
+{
+	unsigned long value;
+	int ret;
+
+	ret = pci_read_config(bdf, offset, &value, PCI_SIZE_8);
+	if (ret)
+		return ret;
+	*valuep = value;
+
+	return 0;
+}
+
+int pci_auto_config_devices(struct udevice *bus)
+{
+	struct pci_controller *hose = bus->uclass_priv;
+	struct pci_platdata *bus_plat;
+	unsigned int sub_bus;
+	struct udevice *dev;
+	int ret;
+
+	bus_plat = dev_get_platdata(bus);
+	sub_bus = bus_plat->busnum;
+	debug("%s: start\n", __func__);
+	pciauto_config_init(hose);
+	for (ret = device_find_first_child(bus, &dev);
+	     !ret && dev;
+	     ret = device_find_next_child(&dev)) {
+		struct pci_child_platdata *pplat;
+
+		pplat = dev_get_parent_platdata(dev);
+		int max_bus;
+		pci_dev_t bdf;
+
+		bdf = PCI_ADD_BUS(bus_plat->busnum, pplat->devfn);
+		debug("%s: device %s\n", __func__, dev->name);
+		max_bus = pciauto_config_device(hose, bdf);
+		sub_bus = max(sub_bus, max_bus);
+	}
+	debug("%s: done\n", __func__);
+
+	return sub_bus;
+}
+
+int pci_bind_bus_devices(struct udevice *bus)
+{
+	struct pci_platdata *bus_plat;
+	ulong vendor, device;
+	ulong header_type;
+	pci_dev_t devfn, end;
+	bool found_multi;
+	int ret;
+
+	bus_plat = dev_get_platdata(bus);
+	found_multi = false;
+	end = PCI_DEVFN(PCI_MAX_PCI_DEVICES - 1, PCI_MAX_PCI_FUNCTIONS - 1);
+	for (devfn = PCI_DEVFN(0, 0); devfn < end; devfn += PCI_DEVFN(0, 1)) {
+		struct pci_child_platdata *pplat;
+		struct udevice *dev;
+		ulong val;
+
+		if (PCI_FUNC(devfn) && !found_multi)
+			continue;
+		/* Check only the first access, we don't expect problems */
+		ret = pci_bus_read_config(bus, devfn, PCI_HEADER_TYPE,
+					  &header_type, PCI_SIZE_8);
+		if (ret)
+			goto error;
+		pci_bus_read_config(bus, devfn, PCI_VENDOR_ID, &vendor,
+				    PCI_SIZE_16);
+		if (vendor == 0xffff || vendor == 0x0000)
+			continue;
+
+		if (!PCI_FUNC(devfn))
+			found_multi = header_type & 0x80;
+
+		debug("%s: bus %s: found device %d, function %d\n", __func__,
+		      bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
+		pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
+				    PCI_SIZE_16);
+
+		/* Find this device in the device tree */
+		ret = pci_bus_find_devfn(bus, devfn, &dev);
+		/*
+		if (ret == -ENODEV) {
+			u32 vendev = PCI_VENDEV(vendor, device);
+
+			ret = pci_bus_find_vendev(bus, vendev, &dev);
+		}
+*/
+		/* If nothing in the device tree, bind a generic device */
+		if (ret == -ENODEV) {
+			char name[30], *str;
+
+			sprintf(name, "pci_%x:%x.%x", bus_plat->busnum,
+				PCI_DEV(devfn), PCI_FUNC(devfn));
+			str = strdup(name);
+			if (!str)
+				return -ENOMEM;
+			ret = device_bind_driver(bus, "pci_generic_drv", str,
+						 &dev);
+		}
+		if (ret)
+			return ret;
+
+		/* Update the platform data */
+		pplat = dev_get_parent_platdata(dev);
+		pplat->devfn = devfn;
+		pplat->vendor = vendor;
+		pplat->device = device;
+		pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &val,
+				    PCI_SIZE_16);
+		pplat->class = val;
+	}
+
+	return 0;
+error:
+	printf("Cannot read bus configuration: %d\n", ret);
+
+	return ret;
+}
+
+static int pci_uclass_post_bind(struct udevice *bus)
+{
+	/*
+	 * Scan the device tree for devices. This does not probe the PCI bus,
+	 * as this is not permitted while binding. It just finds devices
+	 * mentioned in the device tree.
+	 */
+	return dm_scan_fdt_node(bus, gd->fdt_blob, bus->of_offset, false);
+}
+
+static int pci_uclass_post_probe(struct udevice *bus)
+{
+	int ret;
+
+	debug("%s: probing\n", __func__);
+	ret = pci_bind_bus_devices(bus);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_PCI_PNP
+	ret = pci_auto_config_devices(bus);
+#endif
+
+	return ret;
+}
+
+UCLASS_DRIVER(pci) = {
+	.id		= UCLASS_PCI,
+	.name		= "pci",
+	.post_bind	= pci_uclass_post_bind,
+	.post_probe	= pci_uclass_post_probe,
+	.per_device_auto_alloc_size = sizeof(struct pci_controller),
+};
+
+UCLASS_DRIVER(pci_generic) = {
+	.id		= UCLASS_PCI_GENERIC,
+	.name		= "pci_generic",
+};
+
+static const struct udevice_id pci_generic_ids[] = {
+	{ .compatible = "pci-generic" },
+	{ }
+};
+
+U_BOOT_DRIVER(pci_generic_drv) = {
+	.name		= "pci_generic_drv",
+	.id		= UCLASS_PCI_GENERIC,
+	.of_match	= pci_generic_ids,
+};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 332df61..9ccee7b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -15,6 +15,7 @@
 #include <common.h>
 
 #include <command.h>
+#include <inttypes.h>
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <pci.h>
@@ -398,8 +399,8 @@ int pci_hose_config_device(struct pci_controller *hose,
 	unsigned char pin;
 	int bar, found_mem64;
 
-	debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io,
-		(u64)mem, command);
+	debug("PCI Config: I/O=0x%lx, Memory=0x%" PRIx64 ", Command=0x%lx\n",
+	      io, (u64)mem, command);
 
 	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0);
 
@@ -525,71 +526,6 @@ void pci_cfgfunc_do_nothing(struct pci_controller *hose,
  */
 extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
 
-#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI_SCAN_SHOW)
-const char * pci_class_str(u8 class)
-{
-	switch (class) {
-	case PCI_CLASS_NOT_DEFINED:
-		return "Build before PCI Rev2.0";
-		break;
-	case PCI_BASE_CLASS_STORAGE:
-		return "Mass storage controller";
-		break;
-	case PCI_BASE_CLASS_NETWORK:
-		return "Network controller";
-		break;
-	case PCI_BASE_CLASS_DISPLAY:
-		return "Display controller";
-		break;
-	case PCI_BASE_CLASS_MULTIMEDIA:
-		return "Multimedia device";
-		break;
-	case PCI_BASE_CLASS_MEMORY:
-		return "Memory controller";
-		break;
-	case PCI_BASE_CLASS_BRIDGE:
-		return "Bridge device";
-		break;
-	case PCI_BASE_CLASS_COMMUNICATION:
-		return "Simple comm. controller";
-		break;
-	case PCI_BASE_CLASS_SYSTEM:
-		return "Base system peripheral";
-		break;
-	case PCI_BASE_CLASS_INPUT:
-		return "Input device";
-		break;
-	case PCI_BASE_CLASS_DOCKING:
-		return "Docking station";
-		break;
-	case PCI_BASE_CLASS_PROCESSOR:
-		return "Processor";
-		break;
-	case PCI_BASE_CLASS_SERIAL:
-		return "Serial bus controller";
-		break;
-	case PCI_BASE_CLASS_INTELLIGENT:
-		return "Intelligent controller";
-		break;
-	case PCI_BASE_CLASS_SATELLITE:
-		return "Satellite controller";
-		break;
-	case PCI_BASE_CLASS_CRYPT:
-		return "Cryptographic device";
-		break;
-	case PCI_BASE_CLASS_SIGNAL_PROCESSING:
-		return "DSP";
-		break;
-	case PCI_CLASS_OTHERS:
-		return "Does not fit any class";
-		break;
-	default:
-	return  "???";
-		break;
-	};
-}
-#endif /* CONFIG_CMD_PCI || CONFIG_PCI_SCAN_SHOW */
-
 __weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
 {
 	/*
@@ -733,6 +669,7 @@ int pci_hose_scan(struct pci_controller *hose)
 	return pci_hose_scan_bus(hose, hose->current_busno);
 }
 
+#ifndef CONFIG_DM_PCI
 void pci_init(void)
 {
 	hose_head = NULL;
@@ -740,6 +677,7 @@ void pci_init(void)
 	/* now call board specific pci_init()... */
 	pci_init_board();
 }
+#endif
 
 /* Returns the address of the requested capability structure within the
  * device's PCI configuration space or 0 in case the device does not
diff --git a/drivers/pci/pci_common.c b/drivers/pci/pci_common.c
new file mode 100644
index 0000000..4d01373
--- /dev/null
+++ b/drivers/pci/pci_common.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+
+#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI_SCAN_SHOW)
+const char *pci_class_str(u8 class)
+{
+	switch (class) {
+	case PCI_CLASS_NOT_DEFINED:
+		return "Build before PCI Rev2.0";
+		break;
+	case PCI_BASE_CLASS_STORAGE:
+		return "Mass storage controller";
+		break;
+	case PCI_BASE_CLASS_NETWORK:
+		return "Network controller";
+		break;
+	case PCI_BASE_CLASS_DISPLAY:
+		return "Display controller";
+		break;
+	case PCI_BASE_CLASS_MULTIMEDIA:
+		return "Multimedia device";
+		break;
+	case PCI_BASE_CLASS_MEMORY:
+		return "Memory controller";
+		break;
+	case PCI_BASE_CLASS_BRIDGE:
+		return "Bridge device";
+		break;
+	case PCI_BASE_CLASS_COMMUNICATION:
+		return "Simple comm. controller";
+		break;
+	case PCI_BASE_CLASS_SYSTEM:
+		return "Base system peripheral";
+		break;
+	case PCI_BASE_CLASS_INPUT:
+		return "Input device";
+		break;
+	case PCI_BASE_CLASS_DOCKING:
+		return "Docking station";
+		break;
+	case PCI_BASE_CLASS_PROCESSOR:
+		return "Processor";
+		break;
+	case PCI_BASE_CLASS_SERIAL:
+		return "Serial bus controller";
+		break;
+	case PCI_BASE_CLASS_INTELLIGENT:
+		return "Intelligent controller";
+		break;
+	case PCI_BASE_CLASS_SATELLITE:
+		return "Satellite controller";
+		break;
+	case PCI_BASE_CLASS_CRYPT:
+		return "Cryptographic device";
+		break;
+	case PCI_BASE_CLASS_SIGNAL_PROCESSING:
+		return "DSP";
+		break;
+	case PCI_CLASS_OTHERS:
+		return "Does not fit any class";
+		break;
+	default:
+	return  "???";
+		break;
+	};
+}
+#endif /* CONFIG_CMD_PCI || CONFIG_PCI_SCAN_SHOW */
diff --git a/drivers/pci/pci_compat.c b/drivers/pci/pci_compat.c
new file mode 100644
index 0000000..ed8a67d
--- /dev/null
+++ b/drivers/pci/pci_compat.c
@@ -0,0 +1,67 @@
+/*
+ * Compatibility functions for pre-driver-model code
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+
+#define PCI_HOSE_OP(rw, name, size, type)				\
+int pci_hose_##rw##_config_##name(struct pci_controller *hose,		\
+				  pci_dev_t dev,			\
+				  int offset, type value)		\
+{									\
+	return pci_##rw##_config##size(dev, offset, value);		\
+}
+
+PCI_HOSE_OP(read, byte, 8, u8 *)
+PCI_HOSE_OP(read, word, 16, u16 *)
+PCI_HOSE_OP(read, dword, 32, u32 *)
+PCI_HOSE_OP(write, byte, 8, u8)
+PCI_HOSE_OP(write, word, 16, u16)
+PCI_HOSE_OP(write, dword, 32, u32)
+
+int pci_hose_scan_bus(struct pci_controller *hose, int busnum)
+{
+	struct pci_platdata *bus_plat;
+	struct udevice *bus;
+	int sub_bus;
+	int ret;
+
+	ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
+	if (ret)
+		return ret;
+
+	ret = pci_bind_bus_devices(bus);
+	if (ret)
+		return ret;
+	bus_plat = dev_get_platdata(bus);
+	sub_bus = bus_plat->busnum;
+
+#ifdef CONFIG_PCI_PNP
+	sub_bus = pci_auto_config_devices(bus);
+#else
+	/* TODO(sjg at chromium.org): Configure from device tree */
+#endif
+
+	return sub_bus;
+}
+
+pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
+{
+	struct pci_child_platdata *pplat;
+	struct pci_platdata *bus_plat;
+	struct udevice *bus, *dev;
+
+	if (pci_find_device_id(ids, index, &dev))
+		return -1;
+	bus = dev->parent;
+	bus_plat = dev_get_platdata(bus);
+	pplat = dev_get_parent_platdata(dev);
+
+	return PCI_ADD_BUS(bus_plat->busnum, pplat->devfn);
+}
diff --git a/drivers/pci/pci_sandbox.c b/drivers/pci/pci_sandbox.c
new file mode 100644
index 0000000..435f2a4
--- /dev/null
+++ b/drivers/pci/pci_sandbox.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <inttypes.h>
+#include <pci.h>
+#include <dm/root.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int sandbox_pci_write_config(struct udevice *bus, pci_dev_t devfn,
+				    uint offset, ulong value,
+				    enum pci_size_t size)
+{
+	struct dm_pci_emul_ops *ops;
+	struct udevice *emul;
+	int ret;
+
+	ret = sandbox_pci_get_emul(bus, devfn, &emul);
+	if (ret)
+		return ret == -ENODEV ? 0 : ret;
+	ops = pci_get_emul_ops(emul);
+	if (!ops || !ops->write_config)
+		return -ENOSYS;
+
+	return ops->write_config(emul, offset, value, size);
+}
+
+static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
+				   uint offset, ulong *valuep,
+				   enum pci_size_t size)
+{
+	struct dm_pci_emul_ops *ops;
+	struct udevice *emul;
+	int ret;
+
+	/* Prepare the default response */
+	*valuep = pci_get_ff(size);
+	ret = sandbox_pci_get_emul(bus, devfn, &emul);
+	if (ret)
+		return ret == -ENODEV ? 0 : ret;
+	ops = pci_get_emul_ops(emul);
+	if (!ops || !ops->read_config)
+		return -ENOSYS;
+
+	return ops->read_config(emul, offset, valuep, size);
+}
+
+static int sandbox_pci_child_pre_probe(struct udevice *dev)
+{
+	return 0;
+}
+
+static int sandbox_pci_child_post_bind(struct udevice *dev)
+{
+	struct pci_child_platdata *pplat;
+
+	/*
+	 * We could read vendor, device, class if available. But we must have
+	 * the device/function
+	 */
+	pplat = dev_get_parent_platdata(dev);
+	if (fdtdec_pci_get_bdf(gd->fdt_blob, dev->of_offset, &pplat->devfn)) {
+		debug("Cannot decode reg property from '%s'\n", dev->name);
+		return -EINVAL;
+	}
+
+	/* Attach an emulator if we can */
+	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+static int decode_regions(struct pci_controller *hose, const void *blob,
+			  int parent_node, int node)
+{
+	int pci_addr_cells, addr_cells, size_cells;
+	int cells_per_record;
+	const u32 *prop;
+	int len;
+	int i;
+
+	prop = fdt_getprop(blob, node, "ranges", &len);
+	if (!prop)
+		return -EINVAL;
+	pci_addr_cells = fdt_address_cells(blob, node);
+	addr_cells = fdt_address_cells(blob, parent_node);
+	size_cells = fdt_size_cells(blob, node);
+
+	/* PCI addresses are always 3 cells */
+	len /= sizeof(u32);
+	cells_per_record = pci_addr_cells + addr_cells + size_cells;
+	hose->region_count = 0;
+	debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
+	      cells_per_record);
+	for (i = 0; i < MAX_PCI_REGIONS; i++, len -= cells_per_record) {
+		u64 pci_addr, addr, size;
+		int space_code;
+		int type;
+
+		if (len < cells_per_record)
+			break;
+		space_code = (fdt32_to_cpu(prop[0]) >> 24) & 3;
+		pci_addr = fdtdec_get_number(prop + 1, 2);
+		prop += pci_addr_cells;
+		addr = fdtdec_get_number(prop, addr_cells);
+		prop += addr_cells;
+		size = fdtdec_get_number(prop, size_cells);
+		prop += size_cells;
+		if (space_code & 2)
+			type = PCI_REGION_MEM;
+		else if (space_code & 1)
+			type = PCI_REGION_IO;
+		else
+			continue;
+		debug("%s: region %d, pci_addr=%" PRIx64 ", addr=%" PRIx64
+		      ", size=%" PRIx64 ", type=%d\n", __func__,
+		      hose->region_count, pci_addr, addr, size, type);
+		pci_set_region(hose->regions + hose->region_count++, pci_addr,
+			       addr, size, type);
+	}
+
+	return 0;
+}
+
+static int sandbox_pci_probe(struct udevice *bus)
+{
+	struct pci_controller *hose;
+	int ret;
+
+	debug("%s, bus=%s, parent=%s\n", __func__, bus->name,
+	      bus->parent->name);
+	hose = bus->uclass_priv;
+	ret = decode_regions(hose, gd->fdt_blob, bus->parent->of_offset,
+			     bus->of_offset);
+	if (ret) {
+		debug("%s: Cannot decode regions\n", __func__);
+		return ret;
+	}
+	hose->bus = bus;
+	hose->first_busno = 0;
+	hose->last_busno = 0;
+
+	return 0;
+}
+
+static const struct dm_pci_ops sandbox_pci_ops = {
+	.read_config = sandbox_pci_read_config,
+	.write_config = sandbox_pci_write_config,
+};
+
+static const struct udevice_id sandbox_pci_ids[] = {
+	{ .compatible = "sandbox,pci" },
+	{ }
+};
+
+U_BOOT_DRIVER(pci_sandbox) = {
+	.name	= "pci_sandbox",
+	.id	= UCLASS_PCI,
+	.of_match = sandbox_pci_ids,
+	.ops	= &sandbox_pci_ops,
+	.probe	= sandbox_pci_probe,
+	.child_post_bind = sandbox_pci_child_post_bind,
+	.child_pre_probe = sandbox_pci_child_pre_probe,
+	.platdata_auto_alloc_size = sizeof(struct pci_platdata),
+	.per_child_platdata_auto_alloc_size =
+			sizeof(struct pci_child_platdata),
+};
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index ee4b244..d8e6535 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -23,7 +23,6 @@
 
 #define CONFIG_BOOTSTAGE
 #define CONFIG_BOOTSTAGE_REPORT
-#define CONFIG_DM
 #define CONFIG_CMD_DEMO
 #define CONFIG_CMD_DM
 #define CONFIG_DM_DEMO
@@ -47,6 +46,10 @@
 #define CONFIG_CMD_FDT
 #define CONFIG_ANDROID_BOOT_IMAGE
 
+#define CONFIG_CMD_PCI
+#define CONFIG_PCI_PNP
+#define CONFIG_CMD_IO
+
 #define CONFIG_FS_FAT
 #define CONFIG_FS_EXT4
 #define CONFIG_EXT4_WRITE
diff --git a/include/dm/device.h b/include/dm/device.h
index 9ce95a8..90779e6 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -26,6 +26,9 @@ struct driver_info;
 /* DM should init this device prior to relocation */
 #define DM_FLAG_PRE_RELOC	(1 << 2)
 
+/* DM is responsible for allocating and freeing parent_platdata */
+#define DM_FLAG_ALLOC_PARENT_PDATA	(1 << 3)
+
 /**
  * struct udevice - An instance of a driver
  *
@@ -46,6 +49,7 @@ struct driver_info;
  * @driver: The driver used by this device
  * @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
  * @of_offset: Device tree node offset for this device (- for none)
  * @parent: Parent of this device, or NULL for the top level device
  * @priv: Private data for this device
@@ -61,9 +65,13 @@ struct driver_info;
  * when the device is probed and will be unique within the device's uclass.
  */
 struct udevice {
+#ifdef CONFIG_DM_PCI
+	struct udevice *bus;
+#endif
 	struct driver *driver;
 	const char *name;
 	void *platdata;
+	void *parent_platdata;
 	int of_offset;
 	struct udevice *parent;
 	void *priv;
@@ -125,6 +133,7 @@ struct udevice_id {
  * @remove: Called to remove a device, i.e. de-activate it
  * @unbind: Called to unbind a device from its driver
  * @ofdata_to_platdata: Called before probe to decode device tree data
+ * @child_post_bind: Called after a new child has been bound
  * @child_pre_probe: Called before a child device is probed. The device has
  * memory allocated but it has not yet been probed.
  * @child_post_remove: Called after a child device is removed. The device
@@ -144,6 +153,9 @@ struct udevice_id {
  * device_probe_child() pass it in. So far the use case for allocating it
  * is SPI, but I found that unsatisfactory. Since it is here I will leave it
  * until things are clearer.
+ * @per_child_platdata_auto_alloc_size: A bus likes to store information about
+ * its children. If non-zero this is the size of this data, to be allocated
+ * in the child's parent_platdata pointer.
  * @ops: Driver-specific operations. This is typically a list of function
  * pointers defined by the driver, to implement driver functions required by
  * the uclass.
@@ -158,11 +170,13 @@ struct driver {
 	int (*remove)(struct udevice *dev);
 	int (*unbind)(struct udevice *dev);
 	int (*ofdata_to_platdata)(struct udevice *dev);
+	int (*child_post_bind)(struct udevice *dev);
 	int (*child_pre_probe)(struct udevice *dev);
 	int (*child_post_remove)(struct udevice *dev);
 	int priv_auto_alloc_size;
 	int platdata_auto_alloc_size;
 	int per_child_auto_alloc_size;
+	int per_child_platdata_auto_alloc_size;
 	const void *ops;	/* driver-specific operations */
 	uint32_t flags;
 };
@@ -182,6 +196,16 @@ struct driver {
 void *dev_get_platdata(struct udevice *dev);
 
 /**
+ * dev_get_parent_latdata() - Get the parent platform data for a device
+ *
+ * This checks that dev is not NULL, but no other checks for now
+ *
+ * @dev		Device to check
+ * @return parent's platform data, or NULL if none
+ */
+void *dev_get_parent_platdata(struct udevice *dev);
+
+/**
  * dev_get_parentdata() - Get the parent data for a device
  *
  * The parent data is data stored in the device but owned by the parent.
@@ -206,6 +230,14 @@ void *dev_get_parentdata(struct udevice *dev);
 void *dev_get_priv(struct udevice *dev);
 
 /**
+ * device_get_uclass_id() - return the uclass ID of a device
+ *
+ * @dev:	Device to check
+ * @return uclass ID for the device
+ */
+enum uclass_id device_get_uclass_id(struct udevice *dev);
+
+/**
  * device_get_child() - Get the child of a device by index
  *
  * Returns the numbered child, 0 being the first. This does not use
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index a8944c9..364921e 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -19,6 +19,7 @@ enum uclass_id {
 	UCLASS_TEST_FDT,
 	UCLASS_TEST_BUS,
 	UCLASS_SPI_EMUL,	/* sandbox SPI device emulator */
+	UCLASS_PCI_EMUL,	/* sandbox PCI device emulator */
 	UCLASS_SIMPLE_BUS,
 
 	/* U-Boot uclasses start here */
@@ -28,6 +29,8 @@ enum uclass_id {
 	UCLASS_SPI_GENERIC,	/* Generic SPI flash target */
 	UCLASS_SPI_FLASH,	/* SPI flash */
 	UCLASS_CROS_EC,	/* Chrome OS EC */
+	UCLASS_PCI,		/* PCI bus */
+	UCLASS_PCI_GENERIC,	/* Generic PCI bus device */
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index f6ec6d7..2577ae6 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -141,6 +141,8 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node,
 /**
  * uclass_first_device() - Get the first device in a uclass
  *
+ * The device returned is probed if necessary, and ready for use
+ *
  * @id: Uclass ID to look up
  * @devp: Returns pointer to the first device in that uclass, or NULL if none
  * @return 0 if OK (found or not found), -1 on error
@@ -150,6 +152,8 @@ int uclass_first_device(enum uclass_id id, struct udevice **devp);
 /**
  * uclass_next_device() - Get the next device in a uclass
  *
+ * The device returned is probed if necessary, and ready for use
+ *
  * @devp: On entry, pointer to device to lookup. On exit, returns pointer
  * to the next device in the same uclass, or NULL if none
  * @return 0 if OK (found or not found), -1 on error
diff --git a/include/fdtdec.h b/include/fdtdec.h
index ea92894..01bf82c 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -266,6 +266,17 @@ s32 fdtdec_get_int(const void *blob, int node, const char *prop_name,
 		s32 default_val);
 
 /**
+ * Get a variable-sized number from a property
+ *
+ * This reads a number from one or more cells.
+ *
+ * @param ptr	Pointer to property
+ * @param cells	Number of cells containing the number
+ * @return the value in the cells
+ */
+u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells);
+
+/**
  * Look up a 64-bit integer property in a node and return it. The property
  * must have at least 8 bytes of data (2 cells). The first two cells are
  * concatenated to form a 8 bytes value, where the first cell is top half and
diff --git a/include/pci.h b/include/pci.h
index ccda2c5..4412dd3 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -460,8 +460,11 @@ typedef int pci_dev_t;
 #define PCI_BUS(d)	(((d) >> 16) & 0xff)
 #define PCI_DEV(d)	(((d) >> 11) & 0x1f)
 #define PCI_FUNC(d)	(((d) >> 8) & 0x7)
-#define PCI_BDF(b,d,f)	((b) << 16 | (d) << 11 | (f) << 8)
-
+#define PCI_DEVFN(d, f)	((d) << 11 | (f) << 8)
+#define PCI_MASK_BUS(bdf)	((bdf) & 0xffff)
+#define PCI_ADD_BUS(bus, devfn)		(((bus) << 16) | (devfn))
+#define PCI_BDF(b, d, f)	((b) << 16 | PCI_DEVFN(d, f))
+#define PCI_VENDEV(v, d)	(((v) << 16) | (d))
 #define PCI_ANY_ID (~0)
 
 struct pci_device_id {
@@ -495,6 +498,9 @@ extern void pci_cfgfunc_config_device(struct pci_controller* hose, pci_dev_t dev
  * Structure of a PCI controller (host bridge)
  */
 struct pci_controller {
+#ifdef CONFIG_DM_PCI
+	struct udevice *bus;
+#endif
 	struct pci_controller *next;
 
 	int first_busno;
@@ -511,7 +517,7 @@ struct pci_controller {
 	struct pci_config_table *config_table;
 
 	void (*fixup_irq)(struct pci_controller *, pci_dev_t);
-
+#ifndef CONFIG_DM_PCI
 	/* Low-level architecture-dependent routines */
 	int (*read_byte)(struct pci_controller*, pci_dev_t, int where, u8 *);
 	int (*read_word)(struct pci_controller*, pci_dev_t, int where, u16 *);
@@ -519,6 +525,7 @@ struct pci_controller {
 	int (*write_byte)(struct pci_controller*, pci_dev_t, int where, u8);
 	int (*write_word)(struct pci_controller*, pci_dev_t, int where, u16);
 	int (*write_dword)(struct pci_controller*, pci_dev_t, int where, u32);
+#endif
 
 	/* Used by auto config */
 	struct pci_region *pci_mem, *pci_io, *pci_prefetch;
@@ -530,6 +537,7 @@ struct pci_controller {
 	void *priv_data;
 };
 
+#ifndef CONFIG_DM_PCI
 static inline void pci_set_ops(struct pci_controller *hose,
 				   int (*read_byte)(struct pci_controller*,
 						    pci_dev_t, int where, u8 *),
@@ -550,6 +558,7 @@ static inline void pci_set_ops(struct pci_controller *hose,
 	hose->write_word  = write_word;
 	hose->write_dword = write_dword;
 }
+#endif
 
 #ifdef CONFIG_PCI_INDIRECT_BRIDGE
 extern void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data);
@@ -602,12 +611,14 @@ extern int pci_hose_write_config_word(struct pci_controller *hose,
 extern int pci_hose_write_config_dword(struct pci_controller *hose,
 				       pci_dev_t dev, int where, u32 val);
 
+#ifndef CONFIG_DM_PCI
 extern int pci_read_config_byte(pci_dev_t dev, int where, u8 *val);
 extern int pci_read_config_word(pci_dev_t dev, int where, u16 *val);
 extern int pci_read_config_dword(pci_dev_t dev, int where, u32 *val);
 extern int pci_write_config_byte(pci_dev_t dev, int where, u8 val);
 extern int pci_write_config_word(pci_dev_t dev, int where, u16 val);
 extern int pci_write_config_dword(pci_dev_t dev, int where, u32 val);
+#endif
 
 extern int pci_hose_read_config_byte_via_dword(struct pci_controller *hose,
 					       pci_dev_t dev, int where, u8 *val);
@@ -643,8 +654,6 @@ extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
 
 extern pci_dev_t pci_find_device (unsigned int vendor, unsigned int device, int index);
 extern pci_dev_t pci_find_devices (struct pci_device_id *ids, int index);
-extern pci_dev_t pci_find_class(int wanted_class, int wanted_sub_code,
-				int wanted_prog_if, int index);
 
 extern int pci_hose_config_device(struct pci_controller *hose,
 				  pci_dev_t dev,
@@ -700,5 +709,142 @@ void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum,
  * */
 u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum);
 
+#ifdef CONFIG_DM_PCI
+struct pci_child_platdata {
+	int devfn;          /* encoded device & function index */
+	unsigned short vendor;
+	unsigned short device;
+	unsigned int class;          /* 3 bytes: (base,sub,prog-if) */
+};
+
+struct pci_platdata {
+	unsigned int busnum;
+	unsigned int devfn;          /* encoded device & function index */
+};
+
+enum pci_size_t {
+	PCI_SIZE_8,
+	PCI_SIZE_16,
+	PCI_SIZE_32,
+};
+
+struct udevice;
+
+struct dm_pci_ops {
+	int (*write_config)(struct udevice *bus, pci_dev_t devfn, uint offset,
+			    ulong value, enum pci_size_t size);
+	int (*read_config)(struct udevice *bus, pci_dev_t devfn, uint offset,
+			   ulong *valuep, enum pci_size_t size);
+};
+
+#define pci_get_ops(dev)	((struct dm_pci_ops *)(dev)->driver->ops)
+
+int pci_bind_bus_devices(struct udevice *bus);
+
+int pci_auto_config_devices(struct udevice *bus);
+
+int pci_bus_read_config(struct udevice *bus, pci_dev_t devfn, int offset,
+			unsigned long *valuep, enum pci_size_t size);
+
+int pci_bus_write_config(struct udevice *bus, pci_dev_t devfn, int offset,
+			 unsigned long value, enum pci_size_t size);
+
+int pci_write_config32(pci_dev_t pcidev, int offset, u32 value);
+
+/* Compatibility with old naming */
+static inline int pci_write_config_dword(pci_dev_t pcidev, int offset,
+					 u32 value)
+{
+	return pci_write_config32(pcidev, offset, value);
+}
+
+int pci_write_config16(pci_dev_t pcidev, int offset, u16 value);
+
+/* Compatibility with old naming */
+static inline int pci_write_config_word(pci_dev_t pcidev, int offset,
+					u16 value)
+{
+	return pci_write_config16(pcidev, offset, value);
+}
+
+int pci_write_config8(pci_dev_t pcidev, int offset, u8 value);
+
+/* Compatibility with old naming */
+static inline int pci_write_config_byte(pci_dev_t pcidev, int offset,
+					u8 value)
+{
+	return pci_write_config8(pcidev, offset, value);
+}
+
+int pci_read_config32(pci_dev_t pcidev, int offset, u32 *valuep);
+
+/* Compatibility with old naming */
+static inline int pci_read_config_dword(pci_dev_t pcidev, int offset,
+					u32 *valuep)
+{
+	return pci_read_config32(pcidev, offset, valuep);
+}
+
+int pci_read_config16(pci_dev_t pcidev, int offset, u16 *valuep);
+
+/* Compatibility with old naming */
+static inline int pci_read_config_word(pci_dev_t pcidev, int offset,
+				       u16 *valuep)
+{
+	return pci_read_config16(pcidev, offset, valuep);
+}
+
+int pci_read_config8(pci_dev_t pcidev, int offset, u8 *valuep);
+
+/* Compatibility with old naming */
+static inline int pci_read_config_byte(pci_dev_t pcidev, int offset,
+				       u8 *valuep)
+{
+	return pci_read_config8(pcidev, offset, valuep);
+}
+
+struct dm_pci_emul_ops {
+	/**
+	 * get_devfn(): Check which device and function this emulators
+	 *
+	 * @return the device and function this emulatoes, or -ve on error
+	 */
+	int (*get_devfn)(struct udevice *dev);
+
+	int (*read_config)(struct udevice *emul, uint offset, ulong *valuep,
+			   enum pci_size_t size);
+
+	int (*write_config)(struct udevice *emul, uint offset, ulong value,
+			    enum pci_size_t size);
+	int (*write_io)(struct udevice *dev, unsigned int addr,
+			ulong value, enum pci_size_t size);
+	int (*read_io)(struct udevice *dev, unsigned int addr, ulong *valuep,
+		       enum pci_size_t size);
+	int (*map_physmem)(struct udevice *dev, phys_addr_t addr,
+			   unsigned long *lenp, void **ptrp);
+	int (*unmap_physmem)(const void *vaddr, unsigned long len,
+			     struct udevice *dev);
+};
+
+int pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp);
+
+int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
+		       struct udevice **devp);
+
+#define pci_get_emul_ops(dev)	((struct dm_pci_emul_ops *)(dev)->driver->ops)
+
+int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
+			 struct udevice **emulp);
+
+int pci_get_ff(enum pci_size_t size);
+
+int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids,
+			 int *indexp, struct udevice **devp);
+
+int pci_find_device_id(struct pci_device_id *ids, int index,
+		       struct udevice **devp);
+
+#endif
+
 #endif /* __ASSEMBLY__ */
 #endif /* _PCI_H */
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 2345df4..b622ba2 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -735,7 +735,7 @@ int fdtdec_read_fmap_entry(const void *blob, int node, const char *name,
 	return 0;
 }
 
-static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells)
+u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells)
 {
 	u64 number = 0;
 
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 612aa95..8281779 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -21,4 +21,5 @@ obj-$(CONFIG_DM_GPIO) += gpio.o
 obj-$(CONFIG_DM_SPI) += spi.o
 obj-$(CONFIG_DM_SPI_FLASH) += sf.o
 obj-$(CONFIG_DM_I2C) += i2c.o
+obj-$(CONFIG_DM_PCI) += pci.o
 endif
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index cd2c389..3341a08 100644
--- a/test/dm/test-fdt.c
+++ b/test/dm/test-fdt.c
@@ -143,12 +143,12 @@ static int dm_test_fdt(struct dm_test_state *dms)
 	/* These are num_devices compatible root-level device tree nodes */
 	ut_asserteq(num_devices, list_count_items(&uc->dev_head));
 
-	/* Each should have no platdata / priv */
+	/* Each should have platdata but no priv */
 	for (i = 0; i < num_devices; i++) {
 		ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev);
 		ut_assert(!ret);
 		ut_assert(!dev_get_priv(dev));
-		ut_assert(!dev->platdata);
+		ut_assert(dev->platdata);
 	}
 
 	ut_assertok(dm_check_devices(dms, num_devices));
diff --git a/test/dm/test.dts b/test/dm/test.dts
index 69991a3..af2fb2c 100644
--- a/test/dm/test.dts
+++ b/test/dm/test.dts
@@ -8,6 +8,7 @@
 
 	aliases {
 		console = &uart0;
+		pci0 = &pci;
 		testfdt6 = "/e-test";
 	};
 
@@ -110,6 +111,22 @@
 		};
 	};
 
+	pci: pci-controller {
+		compatible = "sandbox,pci";
+		device_type = "pci";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000
+				0x01000000 0 0x20000000 0x20000000 0 0x2000>;
+		pci at 1f,0 {
+			compatible = "pci-generic";
+			reg = <0xf800 0 0 0 0>;
+			emul at 1f,0 {
+				compatible = "sandbox,swap-case";
+			};
+		};
+	};
+
 	spi at 0 {
 		#address-cells = <1>;
 		#size-cells = <0>;
-- 
2.1.0.rc2.206.gedb03e5



More information about the U-Boot mailing list