diff --git a/Documentation/devicetree/bindings/misc/gdsys,iocon_fpga.txt b/Documentation/devicetree/bindings/misc/gdsys,iocon_fpga.txt
new file mode 100644
index 0000000000000000000000000000000000000000..acd466fdc6d0fdd6eaf2c2ddc21c930f444023be
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/gdsys,iocon_fpga.txt
@@ -0,0 +1,19 @@
+gdsys IHS FPGA for CON devices
+
+The gdsys IHS FPGA is the main FPGA on gdsys CON devices. This driver provides
+support for enabling and starting the FPGA, as well as verifying working bus
+communication.
+
+Required properties:
+- compatible: must be "gdsys,iocon_fpga"
+- reset-gpios: List of GPIOs controlling the FPGA's reset
+- done-gpios: List of GPIOs notifying whether the FPGA's reconfiguration is
+              done
+
+Example:
+
+FPGA0 {
+	compatible = "gdsys,iocon_fpga";
+	reset-gpios = <&PPCPCA 26 0>;
+	done-gpios = <&GPIO_VB0 19 0>;
+};
diff --git a/Documentation/devicetree/bindings/misc/gdsys,iocpu_fpga.txt b/Documentation/devicetree/bindings/misc/gdsys,iocpu_fpga.txt
new file mode 100644
index 0000000000000000000000000000000000000000..819db22bf7df79a8e8613a4ac2e5fc4f5323fe55
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/gdsys,iocpu_fpga.txt
@@ -0,0 +1,19 @@
+gdsys IHS FPGA for CPU devices
+
+The gdsys IHS FPGA is the main FPGA on gdsys CPU devices. This driver provides
+support for enabling and starting the FPGA, as well as verifying working bus
+communication.
+
+Required properties:
+- compatible: must be "gdsys,iocpu_fpga"
+- reset-gpios: List of GPIOs controlling the FPGA's reset
+- done-gpios: List of GPIOs notifying whether the FPGA's reconfiguration is
+              done
+
+Example:
+
+FPGA0 {
+	compatible = "gdsys,iocpu_fpga";
+	reset-gpios = <&PPCPCA 26 0>;
+	done-gpios = <&GPIO_VB0 19 0>;
+};
diff --git a/Documentation/devicetree/bindings/misc/gdsys,soc.txt b/Documentation/devicetree/bindings/misc/gdsys,soc.txt
new file mode 100644
index 0000000000000000000000000000000000000000..278e935b166bca8abb466378cba63a6c6e1d644c
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/gdsys,soc.txt
@@ -0,0 +1,16 @@
+gdsys soc bus driver
+
+This driver provides a simple interface for the busses associated with gdsys
+IHS FPGAs. The bus itself contains devices whose register maps are contained
+within the FPGA's register space.
+
+Required properties:
+- fpga: A phandle to the controlling IHS FPGA
+
+Example:
+
+FPGA0BUS: fpga0bus {
+	compatible = "gdsys,soc";
+	ranges = <0x0 0xe0600000 0x00004000>;
+	fpga = <&FPGA0>;
+};
diff --git a/arch/Kconfig b/arch/Kconfig
index 1f2f407d64dd2a7cca3425fcd293e0c74f885371..9fdd2f7e66aea3f21593e4ddcc7985b7c7aa70b3 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -110,6 +110,11 @@ config SANDBOX
 	imply LIBAVB
 	imply CMD_AVB
 	imply UDP_FUNCTION_FASTBOOT
+	imply VIRTIO_MMIO
+	imply VIRTIO_PCI
+	imply VIRTIO_SANDBOX
+	imply VIRTIO_BLK
+	imply VIRTIO_NET
 
 config SH
 	bool "SuperH architecture"
@@ -120,6 +125,7 @@ config X86
 	select CREATE_ARCH_SYMLINK
 	select DM
 	select DM_PCI
+	select HAVE_ARCH_IOMAP
 	select HAVE_PRIVATE_LIBGCC
 	select OF_CONTROL
 	select PCI
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2899a60793c6748924e60d00869f63b9d6cc5a6a..f0e7fde13772288bf12718b8e0acaf581eb9bd95 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1496,6 +1496,7 @@ source "board/broadcom/bcmns2/Kconfig"
 source "board/cavium/thunderx/Kconfig"
 source "board/cirrus/edb93xx/Kconfig"
 source "board/eets/pdu001/Kconfig"
+source "board/emulation/qemu-arm/Kconfig"
 source "board/freescale/ls2080a/Kconfig"
 source "board/freescale/ls2080aqds/Kconfig"
 source "board/freescale/ls2080ardb/Kconfig"
diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c
index 0e152efc045ffa6bb090e678e003d1a53226e090..d087a313892603fbc3a7868807a2fd86982aba70 100644
--- a/arch/arm/mach-stm32mp/bsec.c
+++ b/arch/arm/mach-stm32mp/bsec.c
@@ -417,7 +417,6 @@ U_BOOT_DRIVER(stm32mp_bsec) = {
 	.ofdata_to_platdata = stm32mp_bsec_ofdata_to_platdata,
 	.platdata_auto_alloc_size = sizeof(struct stm32mp_bsec_platdata),
 	.ops = &stm32mp_bsec_ops,
-	.flags  = DM_FLAG_PRE_RELOC,
 };
 
 /* bsec IP is not present in device tee, manage IP address by platdata */
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 957442effd23ea1f920990b718028685ac02b2fe..7c40e415c74f02f7fe61edab1666db44de834241 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -547,6 +547,28 @@ __BUILD_CLRSETBITS(bwlq, sfx, end, type)
 #define __to_cpu(v)		(v)
 #define cpu_to__(v)		(v)
 
+#define out_arch(type, endian, a, v)	__raw_write##type(cpu_to_##endian(v),a)
+#define in_arch(type, endian, a)	endian##_to_cpu(__raw_read##type(a))
+
+#define out_le64(a, v)	out_arch(q, le64, a, v)
+#define out_le32(a, v)	out_arch(l, le32, a, v)
+#define out_le16(a, v)	out_arch(w, le16, a, v)
+
+#define in_le64(a)	in_arch(q, le64, a)
+#define in_le32(a)	in_arch(l, le32, a)
+#define in_le16(a)	in_arch(w, le16, a)
+
+#define out_be64(a, v)	out_arch(q, be64, a, v)
+#define out_be32(a, v)	out_arch(l, be32, a, v)
+#define out_be16(a, v)	out_arch(w, be16, a, v)
+
+#define in_be64(a)	in_arch(q, be64, a)
+#define in_be32(a)	in_arch(l, be32, a)
+#define in_be16(a)	in_arch(w, be16, a)
+
+#define out_8(a, v)	__raw_writeb(v, a)
+#define in_8(a)		__raw_readb(a)
+
 BUILD_CLRSETBITS(b, 8, _, u8)
 BUILD_CLRSETBITS(w, le16, le16, u16)
 BUILD_CLRSETBITS(w, be16, be16, u16)
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index a7a9fb921b6b31d64ee7bff17a871e4f6c2d2c46..2b5ccce9338ef16ec32694fd50f4a5a9f690d3e7 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -9,9 +9,11 @@
 #include <common.h>
 #include <command.h>
 #include <image.h>
-#include <u-boot/zlib.h>
 #include <asm/byteorder.h>
 #include <asm/csr.h>
+#include <dm/device.h>
+#include <dm/root.h>
+#include <u-boot/zlib.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -57,6 +59,13 @@ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
 	/* we assume that the kernel is in place */
 	printf("\nStarting kernel ...\n\n");
 
+	/*
+	 * Call remove function of all devices with a removal flag set.
+	 * This may be useful for last-stage operations, like cancelling
+	 * of DMA operation or releasing device internal buffers.
+	 */
+	dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
 	cleanup_before_linux();
 
 	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index 07e46471fe5b67c374a343c7577e84527f9a182d..325ded51d8a34fbf1958c4fe5d237691af83b9e5 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -174,7 +174,12 @@ void *os_malloc(size_t length)
 	struct os_mem_hdr *hdr;
 	int page_size = getpagesize();
 
-	hdr = mmap(NULL, length + page_size,
+	/*
+	 * Use an address that is hopefully available to us so that pointers
+	 * to this memory are fairly obvious. If we end up with a different
+	 * address, that's fine too.
+	 */
+	hdr = mmap((void *)0x10000000, length + page_size,
 		   PROT_READ | PROT_WRITE | PROT_EXEC,
 		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 	if (hdr == MAP_FAILED)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 57e0dd766317ac87afd10b2f6355811b097cee64..024aa7c51220bb96f72b7a8858149c3dec728bce 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -186,6 +186,10 @@
 		compatible = "denx,u-boot-fdt-test";
 	};
 
+	h-test {
+		compatible = "denx,u-boot-fdt-test1";
+	};
+
 	clocks {
 		clk_fixed: clk-fixed {
 			compatible = "fixed-clock";
@@ -346,14 +350,17 @@
 
 	cpu-test1 {
 		compatible = "sandbox,cpu_sandbox";
+		u-boot,dm-pre-reloc;
 	};
 
 	cpu-test2 {
 		compatible = "sandbox,cpu_sandbox";
+		u-boot,dm-pre-reloc;
 	};
 
 	cpu-test3 {
 		compatible = "sandbox,cpu_sandbox";
+		u-boot,dm-pre-reloc;
 	};
 
 	misc-test {
@@ -525,7 +532,7 @@
 
 	syscon@0 {
 		compatible = "sandbox,syscon0";
-		reg = <0x10 4>;
+		reg = <0x10 16>;
 	};
 
 	syscon@1 {
@@ -712,6 +719,14 @@
 	sandbox_tee {
 		compatible = "sandbox,tee";
 	};
+
+	sandbox_virtio1 {
+		compatible = "sandbox,virtio1";
+	};
+
+	sandbox_virtio2 {
+		compatible = "sandbox,virtio2";
+	};
 };
 
 #include "sandbox_pmic.dtsi"
diff --git a/arch/x86/cpu/baytrail/cpu.c b/arch/x86/cpu/baytrail/cpu.c
index 56e98131d7574d887af17ca888f3bbe15e38a51d..2eb917283bc00b932de1850541170a9c2d86bc7b 100644
--- a/arch/x86/cpu/baytrail/cpu.c
+++ b/arch/x86/cpu/baytrail/cpu.c
@@ -203,4 +203,5 @@ U_BOOT_DRIVER(cpu_x86_baytrail_drv) = {
 	.bind		= cpu_x86_bind,
 	.probe		= cpu_x86_baytrail_probe,
 	.ops		= &cpu_x86_baytrail_ops,
+	.flags		= DM_FLAG_PRE_RELOC,
 };
diff --git a/arch/x86/cpu/broadwell/cpu.c b/arch/x86/cpu/broadwell/cpu.c
index 02b3169cf5d3b8b20f64152339ed542a8aabf68b..232fa40eb53e54d0548cc3ff60c00d5fd2cd59a4 100644
--- a/arch/x86/cpu/broadwell/cpu.c
+++ b/arch/x86/cpu/broadwell/cpu.c
@@ -764,4 +764,5 @@ U_BOOT_DRIVER(cpu_x86_broadwell_drv) = {
 	.probe		= cpu_x86_broadwell_probe,
 	.ops		= &cpu_x86_broadwell_ops,
 	.priv_auto_alloc_size	= sizeof(struct cpu_broadwell_priv),
+	.flags		= DM_FLAG_PRE_RELOC,
 };
diff --git a/arch/x86/cpu/cpu_x86.c b/arch/x86/cpu/cpu_x86.c
index 2b6cc9f22d5c82e13869d7ee6fe0b9da38bb0ceb..1aaf851bb45a22c5c8c00f31b33d5d2473ebd078 100644
--- a/arch/x86/cpu/cpu_x86.c
+++ b/arch/x86/cpu/cpu_x86.c
@@ -94,4 +94,5 @@ U_BOOT_DRIVER(cpu_x86_drv) = {
 	.of_match	= cpu_x86_ids,
 	.bind		= cpu_x86_bind,
 	.ops		= &cpu_x86_ops,
+	.flags		= DM_FLAG_PRE_RELOC,
 };
diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c
index 33e5c6263d475e8a6a12019d90e07698aa6a499a..6edc3e233c29f4cceaadfa7a6f80c7ee8a575f31 100644
--- a/arch/x86/cpu/ivybridge/model_206ax.c
+++ b/arch/x86/cpu/ivybridge/model_206ax.c
@@ -478,4 +478,5 @@ U_BOOT_DRIVER(cpu_x86_model_206ax_drv) = {
 	.bind		= cpu_x86_bind,
 	.probe		= cpu_x86_model_206ax_probe,
 	.ops		= &cpu_x86_model_206ax_ops,
+	.flags		= DM_FLAG_PRE_RELOC,
 };
diff --git a/arch/x86/cpu/tangier/sysreset.c b/arch/x86/cpu/tangier/sysreset.c
index e762ee1b81ce7784abfcb08f44fccc23c39ee19b..b03bc28f9353aa4cb5e372bac165545273b3323e 100644
--- a/arch/x86/cpu/tangier/sysreset.c
+++ b/arch/x86/cpu/tangier/sysreset.c
@@ -44,5 +44,4 @@ U_BOOT_DRIVER(tangier_sysreset) = {
 	.id = UCLASS_SYSRESET,
 	.of_match = tangier_sysreset_ids,
 	.ops = &tangier_sysreset_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index c05c6bf8a25fd50582907012808c8cede0fc33bb..81def0afd30063af862f3daeb81eed312ae7814e 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -241,6 +241,72 @@ static inline void sync(void)
 #define __iormb()	dmb()
 #define __iowmb()	dmb()
 
+/*
+ * Read/write from/to an (offsettable) iomem cookie. It might be a PIO
+ * access or a MMIO access, these functions don't care. The info is
+ * encoded in the hardware mapping set up by the mapping functions
+ * (or the cookie itself, depending on implementation and hw).
+ *
+ * The generic routines don't assume any hardware mappings, and just
+ * encode the PIO/MMIO as part of the cookie. They coldly assume that
+ * the MMIO IO mappings are not in the low address range.
+ *
+ * Architectures for which this is not true can't use this generic
+ * implementation and should do their own copy.
+ */
+
+/*
+ * We assume that all the low physical PIO addresses (0-0xffff) always
+ * PIO. That means we can do some sanity checks on the low bits, and
+ * don't need to just take things for granted.
+ */
+#define PIO_RESERVED	0x10000UL
+
+/*
+ * Ugly macros are a way of life.
+ */
+#define IO_COND(addr, is_pio, is_mmio) do {			\
+	unsigned long port = (unsigned long __force)addr;	\
+	if (port >= PIO_RESERVED) {				\
+		is_mmio;					\
+	} else {						\
+		is_pio;						\
+	}							\
+} while (0)
+
+static inline u8 ioread8(const volatile void __iomem *addr)
+{
+	IO_COND(addr, return inb(port), return readb(addr));
+	return 0xff;
+}
+
+static inline u16 ioread16(const volatile void __iomem *addr)
+{
+	IO_COND(addr, return inw(port), return readw(addr));
+	return 0xffff;
+}
+
+static inline u32 ioread32(const volatile void __iomem *addr)
+{
+	IO_COND(addr, return inl(port), return readl(addr));
+	return 0xffffffff;
+}
+
+static inline void iowrite8(u8 value, volatile void __iomem *addr)
+{
+	IO_COND(addr, outb(value, port), writeb(value, addr));
+}
+
+static inline void iowrite16(u16 value, volatile void __iomem *addr)
+{
+	IO_COND(addr, outw(value, port), writew(value, addr));
+}
+
+static inline void iowrite32(u32 value, volatile void __iomem *addr)
+{
+	IO_COND(addr, outl(value, port), writel(value, addr));
+}
+
 #include <asm-generic/io.h>
 
 #endif /* _ASM_IO_H */
diff --git a/board/emulation/qemu-arm/Kconfig b/board/emulation/qemu-arm/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..02ae4d9884d632225be97a7f2f050cef3e0d7ac3
--- /dev/null
+++ b/board/emulation/qemu-arm/Kconfig
@@ -0,0 +1,13 @@
+if TARGET_QEMU_ARM_32BIT || TARGET_QEMU_ARM_64BIT
+
+config SYS_TEXT_BASE
+	default 0x00000000
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+	imply VIRTIO_MMIO
+	imply VIRTIO_PCI
+	imply VIRTIO_NET
+	imply VIRTIO_BLK
+
+endif
diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c
index 812c90636d7cc59a5581558f8f8791b1dd9a0525..e1f4709c4cfb981e90d8ed2f999cb328f5dded42 100644
--- a/board/emulation/qemu-arm/qemu-arm.c
+++ b/board/emulation/qemu-arm/qemu-arm.c
@@ -2,8 +2,12 @@
 /*
  * Copyright (c) 2017 Tuomas Tynkkynen
  */
+
 #include <common.h>
+#include <dm.h>
 #include <fdtdec.h>
+#include <virtio_types.h>
+#include <virtio.h>
 
 #ifdef CONFIG_ARM64
 #include <asm/armv8/mmu.h>
@@ -58,6 +62,12 @@ struct mm_region *mem_map = qemu_arm64_mem_map;
 
 int board_init(void)
 {
+	/*
+	 * Make sure virtio bus is enumerated so that peripherals
+	 * on the virtio bus can be discovered by their drivers
+	 */
+	virtio_init();
+
 	return 0;
 }
 
diff --git a/board/emulation/qemu-riscv/Kconfig b/board/emulation/qemu-riscv/Kconfig
index af23363fcf719c214b35aa67cf672d61dd64467f..37a80db6a94d069d5c7942fc0e3df95d07f8d8cb 100644
--- a/board/emulation/qemu-riscv/Kconfig
+++ b/board/emulation/qemu-riscv/Kconfig
@@ -18,5 +18,16 @@ config SYS_TEXT_BASE
 config BOARD_SPECIFIC_OPTIONS # dummy
 	def_bool y
 	imply SYS_NS16550
+	imply VIRTIO_MMIO
+	imply VIRTIO_NET
+	imply VIRTIO_BLK
+	imply CMD_PING
+	imply CMD_FS_GENERIC
+	imply DOS_PARTITION
+	imply EFI_PARTITION
+	imply ISO_PARTITION
+	imply CMD_EXT2
+	imply CMD_EXT4
+	imply CMD_FAT
 
 endif
diff --git a/board/emulation/qemu-riscv/qemu-riscv.c b/board/emulation/qemu-riscv/qemu-riscv.c
index 041e716c9bf1bdb78cfd7c181a7c96f3e7e10863..2730a288fb123554ad6e1c8c06b35266332aa464 100644
--- a/board/emulation/qemu-riscv/qemu-riscv.c
+++ b/board/emulation/qemu-riscv/qemu-riscv.c
@@ -4,12 +4,21 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <fdtdec.h>
+#include <virtio_types.h>
+#include <virtio.h>
 
 #define MROM_FDT_ADDR	0x1020
 
 int board_init(void)
 {
+	/*
+	 * Make sure virtio bus is enumerated so that peripherals
+	 * on the virtio bus can be discovered by their drivers
+	 */
+	virtio_init();
+
 	return 0;
 }
 
diff --git a/board/emulation/qemu-x86/Kconfig b/board/emulation/qemu-x86/Kconfig
index 41a27dd933b0d8b98cb30c4b946fd0b54a10f9a9..6d19299d8b6cb3bdc8dccad67de6c1b00417ac21 100644
--- a/board/emulation/qemu-x86/Kconfig
+++ b/board/emulation/qemu-x86/Kconfig
@@ -21,5 +21,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
 	select X86_RESET_VECTOR
 	select QEMU
 	select BOARD_ROMSIZE_KB_1024
+	imply VIRTIO_PCI
+	imply VIRTIO_NET
+	imply VIRTIO_BLK
 
 endif
diff --git a/cmd/Kconfig b/cmd/Kconfig
index ad14c9ce712489a03ecc4e8ea3cc7343bab64deb..d609f9d1c96078de90a195253751a63c0b1853b0 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1065,6 +1065,13 @@ config CMD_USB_MASS_STORAGE
 	help
 	  USB mass storage support
 
+config CMD_VIRTIO
+	bool "virtio"
+	depends on VIRTIO
+	default y if VIRTIO
+	help
+	  VirtIO block device support
+
 config CMD_AXI
 	bool "axi"
 	depends on AXI
diff --git a/cmd/Makefile b/cmd/Makefile
index ac4830a692f22c60bf63460416f890b07e42a806..12a1330b0629fd9e151510340101e2b5f363bf72 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -135,6 +135,7 @@ obj-$(CONFIG_CMD_UBI) += ubi.o
 obj-$(CONFIG_CMD_UBIFS) += ubifs.o
 obj-$(CONFIG_CMD_UNIVERSE) += universe.o
 obj-$(CONFIG_CMD_UNZIP) += unzip.o
+obj-$(CONFIG_CMD_VIRTIO) += virtio.o
 obj-$(CONFIG_CMD_LZMADEC) += lzmadec.o
 
 obj-$(CONFIG_CMD_USB) += usb.o disk.o
diff --git a/cmd/sata.c b/cmd/sata.c
index 4f0c6e0137162e1990b115436be2e016a667edbd..6d62ba8f74aaceb9702d9db361e68966b89b304a 100644
--- a/cmd/sata.c
+++ b/cmd/sata.c
@@ -51,7 +51,6 @@ int sata_probe(int devnum)
 {
 #ifdef CONFIG_AHCI
 	struct udevice *dev;
-	struct udevice *blk;
 	int rc;
 
 	rc = uclass_get_device(UCLASS_AHCI, devnum, &dev);
@@ -67,14 +66,6 @@ int sata_probe(int devnum)
 		return CMD_RET_FAILURE;
 	}
 
-	rc = blk_get_from_parent(dev, &blk);
-	if (!rc) {
-		struct blk_desc *desc = dev_get_uclass_platdata(blk);
-
-		if (desc->lba > 0 && desc->blksz > 0)
-			part_init(desc);
-	}
-
 	return 0;
 #else
 	return sata_initialize() < 0 ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
diff --git a/cmd/virtio.c b/cmd/virtio.c
new file mode 100644
index 0000000000000000000000000000000000000000..b7082bc63adcda0c77c4955f558edbf3157b539e
--- /dev/null
+++ b/cmd/virtio.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+
+static int virtio_curr_dev;
+
+static int do_virtio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	if (argc == 2 && !strcmp(argv[1], "scan")) {
+		/* make sure all virtio devices are enumerated */
+		virtio_init();
+
+		return CMD_RET_SUCCESS;
+	}
+
+	return blk_common_cmd(argc, argv, IF_TYPE_VIRTIO, &virtio_curr_dev);
+}
+
+U_BOOT_CMD(
+	virtio, 8, 1, do_virtio,
+	"virtio block devices sub-system",
+	"scan - initialize virtio bus\n"
+	"virtio info - show all available virtio block devices\n"
+	"virtio device [dev] - show or set current virtio block device\n"
+	"virtio part [dev] - print partition table of one or all virtio block devices\n"
+	"virtio read addr blk# cnt - read `cnt' blocks starting at block\n"
+	"     `blk#' to memory address `addr'\n"
+	"virtio write addr blk# cnt - write `cnt' blocks starting at block\n"
+	"     `blk#' from memory address `addr'"
+);
diff --git a/common/board_f.c b/common/board_f.c
index afafec5e4d02657926f164f7a06abaf76914e851..96503ff8d3c36a12339de06b0f872f678118baec 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -11,6 +11,7 @@
 
 #include <common.h>
 #include <console.h>
+#include <cpu.h>
 #include <dm.h>
 #include <environment.h>
 #include <fdtdec.h>
@@ -165,6 +166,33 @@ static int print_resetinfo(void)
 }
 #endif
 
+#if defined(CONFIG_DISPLAY_CPUINFO) && CONFIG_IS_ENABLED(CPU)
+static int print_cpuinfo(void)
+{
+	struct udevice *dev;
+	char desc[512];
+	int ret;
+
+	ret = uclass_first_device_err(UCLASS_CPU, &dev);
+	if (ret) {
+		debug("%s: Could not get CPU device (err = %d)\n",
+		      __func__, ret);
+		return ret;
+	}
+
+	ret = cpu_get_desc(dev, desc, sizeof(desc));
+	if (ret) {
+		debug("%s: Could not get CPU description (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	printf("CPU:   %s\n", desc);
+
+	return 0;
+}
+#endif
+
 static int announce_dram_init(void)
 {
 	puts("DRAM:  ");
diff --git a/common/usb_storage.c b/common/usb_storage.c
index d92ebb6eb19043fba3851885ea8dfc94652c50c8..560d60538b645b62f41e1df70f7218af3866ec50 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -226,9 +226,7 @@ static int usb_stor_probe_device(struct usb_device *udev)
 		blkdev->lun = lun;
 
 		ret = usb_stor_get_info(udev, data, blkdev);
-		if (ret == 1)
-			ret = blk_prepare_device(dev);
-		if (!ret) {
+		if (ret == 1) {
 			usb_max_devs++;
 			debug("%s: Found device %p\n", __func__, udev);
 		} else {
diff --git a/configs/imx8qxp_mek_defconfig b/configs/imx8qxp_mek_defconfig
index 518f7259f2b9a276771cedd84085627b4064e257..58b4ca08611bd0c68656e46f0b13d4a3b4db6651 100644
--- a/configs/imx8qxp_mek_defconfig
+++ b/configs/imx8qxp_mek_defconfig
@@ -6,7 +6,6 @@ CONFIG_TARGET_IMX8QXP_MEK=y
 CONFIG_NR_DRAM_BANKS=3
 CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/imx8qxp_mek/imximage.cfg"
 CONFIG_BOOTDELAY=3
-# CONFIG_DISPLAY_CPUINFO is not set
 CONFIG_CMD_CPU=y
 # CONFIG_CMD_IMPORTENV is not set
 CONFIG_CMD_CLK=y
diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig
index e9e28192721bab237c24683c73084912c3344621..f4502c9e9f2710fffea1523e29a880c3b3964055 100644
--- a/configs/qemu_arm64_defconfig
+++ b/configs/qemu_arm64_defconfig
@@ -1,7 +1,6 @@
 CONFIG_ARM=y
 CONFIG_ARM_SMCCC=y
 CONFIG_ARCH_QEMU=y
-CONFIG_SYS_TEXT_BASE=0x00000000
 CONFIG_TARGET_QEMU_ARM_64BIT=y
 CONFIG_AHCI=y
 CONFIG_DISTRO_DEFAULTS=y
diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig
index 04c9afdb02e54c53ccbde381d51221670c558493..acebdc5a4e04463d15d6d86697cec67c2505b82b 100644
--- a/configs/qemu_arm_defconfig
+++ b/configs/qemu_arm_defconfig
@@ -1,7 +1,6 @@
 CONFIG_ARM=y
 CONFIG_ARM_SMCCC=y
 CONFIG_ARCH_QEMU=y
-CONFIG_SYS_TEXT_BASE=0x00000000
 CONFIG_TARGET_QEMU_ARM_32BIT=y
 CONFIG_AHCI=y
 CONFIG_DISTRO_DEFAULTS=y
diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig
index 93b22401855dee16569c4ac1be51ee53f4d1b619..e71e2d3860cb42dcbbf9632192178f47b96278c6 100644
--- a/configs/sandbox_noblk_defconfig
+++ b/configs/sandbox_noblk_defconfig
@@ -171,6 +171,7 @@ CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y
 CONFIG_VIDEO_SANDBOX_SDL=y
 CONFIG_OSD=y
 CONFIG_SANDBOX_OSD=y
+# CONFIG_VIRTIO_BLK is not set
 CONFIG_FS_CBFS=y
 CONFIG_FS_CRAMFS=y
 CONFIG_CMD_DHRYSTONE=y
diff --git a/disk/part.c b/disk/part.c
index 9e457a6e72ef0c5af095a7f4e1a1b8b2ba0adedf..f30f9e9187d7e679d5aa5faa225bb199388cac80 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -150,6 +150,9 @@ void dev_print (struct blk_desc *dev_desc)
 			dev_desc->revision,
 			dev_desc->product);
 		break;
+	case IF_TYPE_VIRTIO:
+		printf("%s VirtIO Block Device\n", dev_desc->vendor);
+		break;
 	case IF_TYPE_DOC:
 		puts("device type DOC\n");
 		return;
@@ -281,6 +284,9 @@ static void print_part_header(const char *type, struct blk_desc *dev_desc)
 	case IF_TYPE_NVME:
 		puts ("NVMe");
 		break;
+	case IF_TYPE_VIRTIO:
+		puts("VirtIO");
+		break;
 	default:
 		puts ("UNKNOWN");
 		break;
diff --git a/doc/README.virtio b/doc/README.virtio
new file mode 100644
index 0000000000000000000000000000000000000000..d3652f2e2f8cbdf7f59f37f53d9ab9bce632c73f
--- /dev/null
+++ b/doc/README.virtio
@@ -0,0 +1,253 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+
+VirtIO Support
+==============
+
+This document describes the information about U-Boot support for VirtIO [1]
+devices, including supported boards, build instructions, driver details etc.
+
+What's VirtIO?
+--------------
+VirtIO is a virtualization standard for network and disk device drivers where
+just the guest's device driver "knows" it is running in a virtual environment,
+and cooperates with the hypervisor. This enables guests to get high performance
+network and disk operations, and gives most of the performance benefits of
+paravirtualization. In the U-Boot case, the guest is U-Boot itself, while the
+virtual environment are normally QEMU [2] targets like ARM, RISC-V and x86.
+
+Status
+------
+VirtIO can use various different buses, aka transports as described in the
+spec. While VirtIO devices are commonly implemented as PCI devices on x86,
+embedded devices models like ARM/RISC-V, which does not normally come with
+PCI support might use simple memory mapped device (MMIO) instead of the PCI
+device. The memory mapped virtio device behaviour is based on the PCI device
+specification. Therefore most operations including device initialization,
+queues configuration and buffer transfers are nearly identical. Both MMIO
+and PCI transport options are supported in U-Boot.
+
+The VirtIO spec defines a lots of VirtIO device types, however at present only
+network and block device, the most two commonly used devices, are supported.
+
+The following QEMU targets are supported.
+
+  - qemu_arm_defconfig
+  - qemu_arm64_defconfig
+  - qemu-riscv32_defconfig
+  - qemu-riscv64_defconfig
+  - qemu-x86_defconfig
+  - qemu-x86_64_defconfig
+
+Note ARM and RISC-V targets are configured with VirtIO MMIO transport driver,
+and on x86 it's the PCI transport driver.
+
+Build Instructions
+------------------
+Building U-Boot for pre-configured QEMU targets is no different from others.
+For example, we can do the following with the CROSS_COMPILE environment
+variable being properly set to a working toolchain for ARM:
+
+  $ make qemu_arm_defconfig
+  $ make
+
+You can even create a QEMU ARM target with VirtIO devices showing up on both
+MMIO and PCI buses. In this case, you can enable the PCI transport driver
+from 'make menuconfig':
+
+Device Drivers  --->
+	...
+	VirtIO Drivers  --->
+		...
+		[*] PCI driver for virtio devices
+
+Other drivers are at the same location and can be tuned to suit the needs.
+
+Requirements
+------------
+It is required that QEMU v2.5.0+ should be used to test U-Boot VirtIO support
+on QEMU ARM and x86, and v2.12.0+ on QEMU RISC-V.
+
+Testing
+-------
+The following QEMU command line is used to get U-Boot up and running with
+VirtIO net and block devices on ARM.
+
+  $ qemu-system-arm -nographic -machine virt -bios u-boot.bin \
+    -netdev tap,ifname=tap0,id=net0 \
+    -device virtio-net-device,netdev=net0 \
+    -drive if=none,file=test.img,format=raw,id=hd0 \
+    -device virtio-blk-device,drive=hd0
+
+On x86, command is slightly different to create PCI VirtIO devices.
+
+  $ qemu-system-i386 -nographic -bios u-boot.rom \
+    -netdev tap,ifname=tap0,id=net0 \
+    -device virtio-net-pci,netdev=net0 \
+    -drive if=none,file=test.img,format=raw,id=hd0 \
+    -device virtio-blk-pci,drive=hd0
+
+Additional net and block devices can be created by appending more '-device'
+parameters. It is also possible to specify both MMIO and PCI VirtIO devices.
+For example, the following commnad creates 3 VirtIO devices, with 1 on MMIO
+and 2 on PCI bus.
+
+  $ qemu-system-arm -nographic -machine virt -bios u-boot.bin \
+    -netdev tap,ifname=tap0,id=net0 \
+    -device virtio-net-pci,netdev=net0 \
+    -drive if=none,file=test0.img,format=raw,id=hd0 \
+    -device virtio-blk-device,drive=hd0 \
+    -drive if=none,file=test1.img,format=raw,id=hd1 \
+    -device virtio-blk-pci,drive=hd1
+
+By default QEMU creates VirtIO legacy devices by default. To create non-legacy
+(aka modern) devices, pass additional device property/value pairs like below:
+
+  $ qemu-system-i386 -nographic -bios u-boot.rom \
+    -netdev tap,ifname=tap0,id=net0 \
+    -device virtio-net-pci,netdev=net0,disable-legacy=true,disable-modern=false \
+    -drive if=none,file=test.img,format=raw,id=hd0 \
+    -device virtio-blk-pci,drive=hd0,disable-legacy=true,disable-modern=false
+
+A 'virtio' command is provided in U-Boot shell.
+
+  => virtio
+  virtio - virtio block devices sub-system
+
+  Usage:
+  virtio scan - initialize virtio bus
+  virtio info - show all available virtio block devices
+  virtio device [dev] - show or set current virtio block device
+  virtio part [dev] - print partition table of one or all virtio block devices
+  virtio read addr blk# cnt - read `cnt' blocks starting at block
+       `blk#' to memory address `addr'
+  virtio write addr blk# cnt - write `cnt' blocks starting at block
+       `blk#' from memory address `addr'
+
+To probe all the VirtIO devices, type:
+
+  => virtio scan
+
+Then we can show the connected block device details by:
+
+  => virtio info
+  Device 0: QEMU VirtIO Block Device
+              Type: Hard Disk
+              Capacity: 4096.0 MB = 4.0 GB (8388608 x 512)
+
+And list the directories and files on the disk by:
+
+  => ls virtio 0 /
+  <DIR>       4096 .
+  <DIR>       4096 ..
+  <DIR>      16384 lost+found
+  <DIR>       4096 dev
+  <DIR>       4096 proc
+  <DIR>       4096 sys
+  <DIR>       4096 var
+  <DIR>       4096 etc
+  <DIR>       4096 usr
+  <SYM>          7 bin
+  <SYM>          8 sbin
+  <SYM>          7 lib
+  <SYM>          9 lib64
+  <DIR>       4096 run
+  <DIR>       4096 boot
+  <DIR>       4096 home
+  <DIR>       4096 media
+  <DIR>       4096 mnt
+  <DIR>       4096 opt
+  <DIR>       4096 root
+  <DIR>       4096 srv
+  <DIR>       4096 tmp
+                 0 .autorelabel
+
+Driver Internals
+----------------
+There are 3 level of drivers in the VirtIO driver family.
+
+	+---------------------------------------+
+	|	 virtio device drivers		|
+	|    +-------------+ +------------+	|
+	|    | virtio-net  | | virtio-blk |	|
+	|    +-------------+ +------------+	|
+	+---------------------------------------+
+	+---------------------------------------+
+	|	virtio transport drivers	|
+	|    +-------------+ +------------+	|
+	|    | virtio-mmio | | virtio-pci |	|
+	|    +-------------+ +------------+	|
+	+---------------------------------------+
+		+----------------------+
+		| virtio uclass driver |
+		+----------------------+
+
+The root one is the virtio uclass driver (virtio-uclass.c), which does lots of
+common stuff for the transport drivers (virtio_mmio.c, virtio_pci.c). The real
+virtio device is discovered in the transport driver's probe() method, and its
+device ID is saved in the virtio uclass's private data of the transport device.
+Then in the virtio uclass's post_probe() method, the real virtio device driver
+(virtio_net.c, virtio_blk.c) is bound if there is a match on the device ID.
+
+The child_post_bind(), child_pre_probe() and child_post_probe() methods of the
+virtio uclass driver help bring the virtio device driver online. They do things
+like acknowledging device, feature negotiation, etc, which are really common
+for all virtio devices.
+
+The transport drivers provide a set of ops (struct dm_virtio_ops) for the real
+virtio device driver to call. These ops APIs's parameter is designed to remind
+the caller to pass the correct 'struct udevice' id of the virtio device, eg:
+
+int virtio_get_status(struct udevice *vdev, u8 *status)
+
+So the parameter 'vdev' indicates the device should be the real virtio device.
+But we also have an API like:
+
+struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
+					 unsigned int vring_align,
+					 struct udevice *udev)
+
+Here the parameter 'udev' indicates the device should be the transport device.
+Similar naming is applied in other functions that are even not APIs, eg:
+
+static int virtio_uclass_post_probe(struct udevice *udev)
+static int virtio_uclass_child_pre_probe(struct udevice *vdev)
+
+So it's easy to tell which device these functions are operating on.
+
+Development Flow
+----------------
+At present only VirtIO network card (device ID 1) and block device (device
+ID 2) are supported. If you want to develop new driver for new devices,
+please follow the guideline below.
+
+1. add new device ID in virtio.h
+#define VIRTIO_ID_XXX		X
+
+2. update VIRTIO_ID_MAX_NUM to be the largest device ID plus 1
+
+3. add new driver name string in virtio.h
+#define VIRTIO_XXX_DRV_NAME	"virtio-xxx"
+
+4. create a new driver with name set to the name string above
+U_BOOT_DRIVER(virtio_xxx) = {
+	.name = VIRTIO_XXX_DRV_NAME,
+	...
+	.remove = virtio_reset,
+	.flags = DM_FLAG_ACTIVE_DMA,
+}
+
+Note the driver needs to provide the remove method and normally this can be
+hooked to virtio_reset(). The driver flags should contain DM_FLAG_ACTIVE_DMA
+for the remove method to be called before jumping to OS.
+
+5. provide bind() method in the driver, where virtio_driver_features_init()
+   should be called for driver to negotiate feature support with the device.
+
+6. do funny stuff with the driver
+
+References
+----------
+[1] http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf
+[2] https://www.qemu.org
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
index 36541630a2730207a0551967d36be7db87abec0a..07b120d51218dd7d2b494fc7cbdf2c49f7efdcca 100644
--- a/doc/driver-model/README.txt
+++ b/doc/driver-model/README.txt
@@ -830,10 +830,18 @@ Pre-Relocation Support
 ----------------------
 
 For pre-relocation we simply call the driver model init function. Only
-drivers marked with DM_FLAG_PRE_RELOC or the device tree
-'u-boot,dm-pre-reloc' flag are initialised prior to relocation. This helps
-to reduce the driver model overhead. This flag applies to SPL and TPL as
-well, if device tree is enabled there.
+drivers marked with DM_FLAG_PRE_RELOC or the device tree 'u-boot,dm-pre-reloc'
+property are initialised prior to relocation. This helps to reduce the driver
+model overhead. This flag applies to SPL and TPL as well, if device tree is
+enabled (CONFIG_OF_CONTROL) there.
+
+Note when device tree is enabled, the device tree 'u-boot,dm-pre-reloc'
+property can provide better control granularity on which device is bound
+before relocation. While with DM_FLAG_PRE_RELOC flag of the driver all
+devices with the same driver are bound, which requires allocation a large
+amount of memory. When device tree is not used, DM_FLAG_PRE_RELOC is the
+only way for statically declared devices via U_BOOT_DEVICE() to be bound
+prior to relocation.
 
 It is possible to limit this to specific relocation steps, by using
 the more specialized 'u-boot,dm-spl' and 'u-boot,dm-tpl' flags
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 927a2b87f6a7dda6b8e55388e3ee4e22c3fa9c14..4ac823d962e855e149ab6ab1fce5fdc2557479b5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -112,6 +112,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/video/Kconfig"
 
+source "drivers/virtio/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/w1-eeprom/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index fb38b67541340d0897518915ba0b416adf82e9f8..4453c62ad33e078bb6821f7337d96b3ead8ebd25 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_$(SPL_TPL_)SERIAL_SUPPORT) += serial/
 obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/
 obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/
 obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/
+obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/
 obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/
 obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
 
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index facf52711ccfd735171ba7c0235aa275ad089af1..65a766e586d610d78fd6bad56757fb32dab0823d 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -23,6 +23,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
 	[IF_TYPE_HOST]		= "host",
 	[IF_TYPE_NVME]		= "nvme",
 	[IF_TYPE_EFI]		= "efi",
+	[IF_TYPE_VIRTIO]	= "virtio",
 };
 
 static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
@@ -37,6 +38,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
 	[IF_TYPE_HOST]		= UCLASS_ROOT,
 	[IF_TYPE_NVME]		= UCLASS_NVME,
 	[IF_TYPE_EFI]		= UCLASS_EFI,
+	[IF_TYPE_VIRTIO]	= UCLASS_VIRTIO,
 };
 
 static enum if_type if_typename_to_iftype(const char *if_typename)
@@ -471,15 +473,6 @@ unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
 	return ops->erase(dev, start, blkcnt);
 }
 
-int blk_prepare_device(struct udevice *dev)
-{
-	struct blk_desc *desc = dev_get_uclass_platdata(dev);
-
-	part_init(desc);
-
-	return 0;
-}
-
 int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
 {
 	struct udevice *dev;
@@ -526,7 +519,7 @@ int blk_find_max_devnum(enum if_type if_type)
 	return max_devnum;
 }
 
-static int blk_next_free_devnum(enum if_type if_type)
+int blk_next_free_devnum(enum if_type if_type)
 {
 	int ret;
 
@@ -644,8 +637,20 @@ int blk_unbind_all(int if_type)
 	return 0;
 }
 
+static int blk_post_probe(struct udevice *dev)
+{
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
+	struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+	part_init(desc);
+#endif
+
+	return 0;
+}
+
 UCLASS_DRIVER(blk) = {
 	.id		= UCLASS_BLK,
 	.name		= "blk",
+	.post_probe	= blk_post_probe,
 	.per_device_platdata_auto_alloc_size = sizeof(struct blk_desc),
 };
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index 38adb6a2417892238a8e78ae9dd476c024fd68b3..4b8a4eac176cfa122a9287644e2f8d481821c51a 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1169,8 +1169,6 @@ static int ide_blk_probe(struct udevice *udev)
 		BLK_REV_SIZE);
 	desc->revision[BLK_REV_SIZE] = '\0';
 
-	part_init(desc);
-
 	return 0;
 }
 
diff --git a/drivers/block/sandbox.c b/drivers/block/sandbox.c
index 0392437309e71f5dc0077bf90c5a8e7f8507a307..d3b1aaaba36d77921ad306f39e9606821ed8b92e 100644
--- a/drivers/block/sandbox.c
+++ b/drivers/block/sandbox.c
@@ -33,7 +33,7 @@ static unsigned long host_block_read(struct udevice *dev,
 				     unsigned long start, lbaint_t blkcnt,
 				     void *buffer)
 {
-	struct host_block_dev *host_dev = dev_get_priv(dev);
+	struct host_block_dev *host_dev = dev_get_platdata(dev);
 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
 
 #else
@@ -64,7 +64,7 @@ static unsigned long host_block_write(struct udevice *dev,
 				      unsigned long start, lbaint_t blkcnt,
 				      const void *buffer)
 {
-	struct host_block_dev *host_dev = dev_get_priv(dev);
+	struct host_block_dev *host_dev = dev_get_platdata(dev);
 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
 #else
 static unsigned long host_block_write(struct blk_desc *block_dev,
@@ -131,17 +131,18 @@ int host_dev_bind(int devnum, char *filename)
 				os_lseek(fd, 0, OS_SEEK_END) / 512, &dev);
 	if (ret)
 		goto err_file;
+
+	host_dev = dev_get_platdata(dev);
+	host_dev->fd = fd;
+	host_dev->filename = fname;
+
 	ret = device_probe(dev);
 	if (ret) {
 		device_unbind(dev);
 		goto err_file;
 	}
 
-	host_dev = dev_get_priv(dev);
-	host_dev->fd = fd;
-	host_dev->filename = fname;
-
-	return blk_prepare_device(dev);
+	return 0;
 err_file:
 	os_close(fd);
 err:
@@ -226,7 +227,7 @@ U_BOOT_DRIVER(sandbox_host_blk) = {
 	.name		= "sandbox_host_blk",
 	.id		= UCLASS_BLK,
 	.ops		= &sandbox_host_blk_ops,
-	.priv_auto_alloc_size	= sizeof(struct host_block_dev),
+	.platdata_auto_alloc_size = sizeof(struct host_block_dev),
 };
 #else
 U_BOOT_LEGACY_BLK(sandbox_host) = {
diff --git a/drivers/clk/altera/clk-arria10.c b/drivers/clk/altera/clk-arria10.c
index 78102c760d2fcba3ac872dd29cea7e5554ad4e49..612a1718dcb5c9667a598ff2332b2b5f479e7585 100644
--- a/drivers/clk/altera/clk-arria10.c
+++ b/drivers/clk/altera/clk-arria10.c
@@ -352,7 +352,6 @@ static const struct udevice_id socfpga_a10_clk_match[] = {
 U_BOOT_DRIVER(socfpga_a10_clk) = {
 	.name		= "clk-a10",
 	.id		= UCLASS_CLK,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.of_match	= socfpga_a10_clk_match,
 	.ops		= &socfpga_a10_clk_ops,
 	.bind		= socfpga_a10_clk_bind,
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c
index fdf95a12da512fbf75233a3d1a244b0ed4d9172a..b3ac0d5a92aad1d63f402789711890cb7231f161 100644
--- a/drivers/clk/clk_pic32.c
+++ b/drivers/clk/clk_pic32.c
@@ -418,7 +418,6 @@ U_BOOT_DRIVER(pic32_clk) = {
 	.name		= "pic32_clk",
 	.id		= UCLASS_CLK,
 	.of_match	= pic32_clk_ids,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.ops		= &pic32_pic32_clk_ops,
 	.probe		= pic32_clk_probe,
 	.priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
diff --git a/drivers/clk/clk_zynq.c b/drivers/clk/clk_zynq.c
index d647e0a01e77e67ac63e4115eac65ddd5f882b66..482f0937cb5a94947e919dedcdca68a29638cac2 100644
--- a/drivers/clk/clk_zynq.c
+++ b/drivers/clk/clk_zynq.c
@@ -480,7 +480,6 @@ U_BOOT_DRIVER(zynq_clk) = {
 	.name		= "zynq_clk",
 	.id		= UCLASS_CLK,
 	.of_match	= zynq_clk_ids,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.ops		= &zynq_clk_ops,
 	.priv_auto_alloc_size = sizeof(struct zynq_clk_priv),
 	.probe		= zynq_clk_probe,
diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c
index 763567b17c339fed217129694f0646a4dbf8160c..aa86c7ca4457e498492520779c8e481a0d0adb64 100644
--- a/drivers/clk/exynos/clk-exynos7420.c
+++ b/drivers/clk/exynos/clk-exynos7420.c
@@ -201,7 +201,6 @@ U_BOOT_DRIVER(exynos7420_clk_topc) = {
 	.probe = exynos7420_clk_topc_probe,
 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
 	.ops = &exynos7420_clk_topc_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 static const struct udevice_id exynos7420_clk_top0_compat[] = {
@@ -216,7 +215,6 @@ U_BOOT_DRIVER(exynos7420_clk_top0) = {
 	.probe = exynos7420_clk_top0_probe,
 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
 	.ops = &exynos7420_clk_top0_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 static const struct udevice_id exynos7420_clk_peric1_compat[] = {
@@ -229,5 +227,4 @@ U_BOOT_DRIVER(exynos7420_clk_peric1) = {
 	.id = UCLASS_CLK,
 	.of_match = exynos7420_clk_peric1_compat,
 	.ops = &exynos7420_clk_peric1_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/clk/owl/clk_s900.c b/drivers/clk/owl/clk_s900.c
index 2b39bb99afe1039485357389055bdf7f9864e629..a7c15d2812e2f8f2bfa08f421de071f2204b2349 100644
--- a/drivers/clk/owl/clk_s900.c
+++ b/drivers/clk/owl/clk_s900.c
@@ -134,5 +134,4 @@ U_BOOT_DRIVER(clk_owl) = {
 	.ops		= &owl_clk_ops,
 	.priv_auto_alloc_size = sizeof(struct owl_clk_priv),
 	.probe		= owl_clk_probe,
-	.flags		= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 5176aa3f866f49a422a0dba715237144ad5039cd..47a697f3e5c3fa0329816db779bcf67129742431 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -834,5 +834,5 @@ int dev_enable_by_path(const char *path)
 	if (ret)
 		return ret;
 
-	return lists_bind_fdt(parent, node, NULL);
+	return lists_bind_fdt(parent, node, NULL, false);
 }
diff --git a/drivers/core/dump.c b/drivers/core/dump.c
index d7cdb1475d7cb0daadd549f4a667468354b55595..90680844043e64a6f89f7d90a5169b53cd027bb5 100644
--- a/drivers/core/dump.c
+++ b/drivers/core/dump.c
@@ -89,7 +89,7 @@ void dm_dump_uclass(void)
 		printf("uclass %d: %s\n", id, uc->uc_drv->name);
 		if (list_empty(&uc->dev_head))
 			continue;
-		list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+		uclass_foreach_dev(dev, uc) {
 			dm_display_line(dev, i);
 			i++;
 		}
diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index a1677269d8d657279c219cf046905bd389a52bca..a1f828463ecd615ba1e9175fc68cabd35543615a 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -122,7 +122,8 @@ static int driver_check_compatible(const struct udevice_id *of_match,
 	return -ENOENT;
 }
 
-int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp)
+int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
+		   bool pre_reloc_only)
 {
 	struct driver *driver = ll_entry_start(struct driver, driver);
 	const int n_ents = ll_entry_count(struct driver, driver);
@@ -171,6 +172,12 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp)
 		if (entry == driver + n_ents)
 			continue;
 
+		if (pre_reloc_only) {
+			if (!dm_ofnode_pre_reloc(node) &&
+			    !(entry->flags & DM_FLAG_PRE_RELOC))
+				return 0;
+		}
+
 		pr_debug("   - found match at '%s'\n", entry->name);
 		ret = device_bind_with_driver_data(parent, entry, name,
 						   id->data, node, &dev);
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index b7b7ad3a625da9cbff62bed3644067eabc72ac1f..d9b5280b2d4f163fea8b7cd34e17e7d47db36586 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -831,8 +831,10 @@ int ofnode_write_prop(ofnode node, const char *propname, int len,
 		return -ENOMEM;
 
 	new->name = strdup(propname);
-	if (!new->name)
+	if (!new->name) {
+		free(new);
 		return -ENOMEM;
+	}
 
 	new->value = (void *)value;
 	new->length = len;
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c
index 8e5c3bcf61bbc4927b2ccf577df927bafba1efb6..5ef0f71c8b7d2d07f2884ea9f84215c683c66225 100644
--- a/drivers/core/regmap.c
+++ b/drivers/core/regmap.c
@@ -17,6 +17,12 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/**
+ * regmap_alloc() - Allocate a regmap with a given number of ranges.
+ *
+ * @count: Number of ranges to be allocated for the regmap.
+ * Return: A pointer to the newly allocated regmap, or NULL on error.
+ */
 static struct regmap *regmap_alloc(int count)
 {
 	struct regmap *map;
@@ -50,6 +56,58 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
 	return 0;
 }
 #else
+/**
+ * init_range() - Initialize a single range of a regmap
+ * @node:     Device node that will use the map in question
+ * @range:    Pointer to a regmap_range structure that will be initialized
+ * @addr_len: The length of the addr parts of the reg property
+ * @size_len: The length of the size parts of the reg property
+ * @index:    The index of the range to initialize
+ *
+ * This function will read the necessary 'reg' information from the device tree
+ * (the 'addr' part, and the 'length' part), and initialize the range in
+ * quesion.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int init_range(ofnode node, struct regmap_range *range, int addr_len,
+		      int size_len, int index)
+{
+	fdt_size_t sz;
+	struct resource r;
+
+	if (of_live_active()) {
+		int ret;
+
+		ret = of_address_to_resource(ofnode_to_np(node),
+					     index, &r);
+		if (ret) {
+			debug("%s: Could not read resource of range %d (ret = %d)\n",
+			      ofnode_get_name(node), index, ret);
+			return ret;
+		}
+
+		range->start = r.start;
+		range->size = r.end - r.start + 1;
+	} else {
+		int offset = ofnode_to_offset(node);
+
+		range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
+							  "reg", index,
+							  addr_len, size_len,
+							  &sz, true);
+		if (range->start == FDT_ADDR_T_NONE) {
+			debug("%s: Could not read start of range %d\n",
+			      ofnode_get_name(node), index);
+			return -EINVAL;
+		}
+
+		range->size = sz;
+	}
+
+	return 0;
+}
+
 int regmap_init_mem(ofnode node, struct regmap **mapp)
 {
 	struct regmap_range *range;
@@ -58,19 +116,41 @@ int regmap_init_mem(ofnode node, struct regmap **mapp)
 	int addr_len, size_len, both_len;
 	int len;
 	int index;
-	struct resource r;
 
 	addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
+	if (addr_len < 0) {
+		debug("%s: Error while reading the addr length (ret = %d)\n",
+		      ofnode_get_name(node), addr_len);
+		return addr_len;
+	}
+
 	size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
+	if (size_len < 0) {
+		debug("%s: Error while reading the size length: (ret = %d)\n",
+		      ofnode_get_name(node), size_len);
+		return size_len;
+	}
+
 	both_len = addr_len + size_len;
+	if (!both_len) {
+		debug("%s: Both addr and size length are zero\n",
+		      ofnode_get_name(node));
+		return -EINVAL;
+	}
 
 	len = ofnode_read_size(node, "reg");
-	if (len < 0)
+	if (len < 0) {
+		debug("%s: Error while reading reg size (ret = %d)\n",
+		      ofnode_get_name(node), len);
 		return len;
+	}
 	len /= sizeof(fdt32_t);
 	count = len / both_len;
-	if (!count)
+	if (!count) {
+		debug("%s: Not enough data in reg property\n",
+		      ofnode_get_name(node));
 		return -EINVAL;
+	}
 
 	map = regmap_alloc(count);
 	if (!map)
@@ -78,19 +158,21 @@ int regmap_init_mem(ofnode node, struct regmap **mapp)
 
 	for (range = map->ranges, index = 0; count > 0;
 	     count--, range++, index++) {
-		fdt_size_t sz;
-		if (of_live_active()) {
-			of_address_to_resource(ofnode_to_np(node), index, &r);
-			range->start = r.start;
-			range->size = r.end - r.start + 1;
-		} else {
-			range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob,
-					ofnode_to_offset(node), "reg", index,
-					addr_len, size_len, &sz, true);
-			range->size = sz;
-		}
+		int ret = init_range(node, range, addr_len, size_len, index);
+
+		if (ret)
+			return ret;
 	}
 
+	if (ofnode_read_bool(node, "little-endian"))
+		map->endianness = REGMAP_LITTLE_ENDIAN;
+	else if (ofnode_read_bool(node, "big-endian"))
+		map->endianness = REGMAP_BIG_ENDIAN;
+	else if (ofnode_read_bool(node, "native-endian"))
+		map->endianness = REGMAP_NATIVE_ENDIAN;
+	else /* Default: native endianness */
+		map->endianness = REGMAP_NATIVE_ENDIAN;
+
 	*mapp = map;
 
 	return 0;
@@ -115,24 +197,218 @@ int regmap_uninit(struct regmap *map)
 	return 0;
 }
 
-int regmap_read(struct regmap *map, uint offset, uint *valp)
+static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
 {
-	u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE);
+	return readb(addr);
+}
 
-	*valp = le32_to_cpu(readl(ptr));
+static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_LITTLE_ENDIAN:
+		return in_le16(addr);
+	case REGMAP_BIG_ENDIAN:
+		return in_be16(addr);
+	case REGMAP_NATIVE_ENDIAN:
+		return readw(addr);
+	}
+
+	return readw(addr);
+}
+
+static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_LITTLE_ENDIAN:
+		return in_le32(addr);
+	case REGMAP_BIG_ENDIAN:
+		return in_be32(addr);
+	case REGMAP_NATIVE_ENDIAN:
+		return readl(addr);
+	}
+
+	return readl(addr);
+}
+
+#if defined(in_le64) && defined(in_be64) && defined(readq)
+static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_LITTLE_ENDIAN:
+		return in_le64(addr);
+	case REGMAP_BIG_ENDIAN:
+		return in_be64(addr);
+	case REGMAP_NATIVE_ENDIAN:
+		return readq(addr);
+	}
+
+	return readq(addr);
+}
+#endif
+
+int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
+			  void *valp, size_t val_len)
+{
+	struct regmap_range *range;
+	void *ptr;
+
+	if (range_num >= map->range_count) {
+		debug("%s: range index %d larger than range count\n",
+		      __func__, range_num);
+		return -ERANGE;
+	}
+	range = &map->ranges[range_num];
+
+	ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
+
+	if (offset + val_len > range->size) {
+		debug("%s: offset/size combination invalid\n", __func__);
+		return -ERANGE;
+	}
+
+	switch (val_len) {
+	case REGMAP_SIZE_8:
+		*((u8 *)valp) = __read_8(ptr, map->endianness);
+		break;
+	case REGMAP_SIZE_16:
+		*((u16 *)valp) = __read_16(ptr, map->endianness);
+		break;
+	case REGMAP_SIZE_32:
+		*((u32 *)valp) = __read_32(ptr, map->endianness);
+		break;
+#if defined(in_le64) && defined(in_be64) && defined(readq)
+	case REGMAP_SIZE_64:
+		*((u64 *)valp) = __read_64(ptr, map->endianness);
+		break;
+#endif
+	default:
+		debug("%s: regmap size %zu unknown\n", __func__, val_len);
+		return -EINVAL;
+	}
 
 	return 0;
 }
 
-int regmap_write(struct regmap *map, uint offset, uint val)
+int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
 {
-	u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE);
+	return regmap_raw_read_range(map, 0, offset, valp, val_len);
+}
 
-	writel(cpu_to_le32(val), ptr);
+int regmap_read(struct regmap *map, uint offset, uint *valp)
+{
+	return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
+}
+
+static inline void __write_8(u8 *addr, const u8 *val,
+			     enum regmap_endianness_t endianness)
+{
+	writeb(*val, addr);
+}
+
+static inline void __write_16(u16 *addr, const u16 *val,
+			      enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_NATIVE_ENDIAN:
+		writew(*val, addr);
+		break;
+	case REGMAP_LITTLE_ENDIAN:
+		out_le16(addr, *val);
+		break;
+	case REGMAP_BIG_ENDIAN:
+		out_be16(addr, *val);
+		break;
+	}
+}
+
+static inline void __write_32(u32 *addr, const u32 *val,
+			      enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_NATIVE_ENDIAN:
+		writel(*val, addr);
+		break;
+	case REGMAP_LITTLE_ENDIAN:
+		out_le32(addr, *val);
+		break;
+	case REGMAP_BIG_ENDIAN:
+		out_be32(addr, *val);
+		break;
+	}
+}
+
+#if defined(out_le64) && defined(out_be64) && defined(writeq)
+static inline void __write_64(u64 *addr, const u64 *val,
+			      enum regmap_endianness_t endianness)
+{
+	switch (endianness) {
+	case REGMAP_NATIVE_ENDIAN:
+		writeq(*val, addr);
+		break;
+	case REGMAP_LITTLE_ENDIAN:
+		out_le64(addr, *val);
+		break;
+	case REGMAP_BIG_ENDIAN:
+		out_be64(addr, *val);
+		break;
+	}
+}
+#endif
+
+int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
+			   const void *val, size_t val_len)
+{
+	struct regmap_range *range;
+	void *ptr;
+
+	if (range_num >= map->range_count) {
+		debug("%s: range index %d larger than range count\n",
+		      __func__, range_num);
+		return -ERANGE;
+	}
+	range = &map->ranges[range_num];
+
+	ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
+
+	if (offset + val_len > range->size) {
+		debug("%s: offset/size combination invalid\n", __func__);
+		return -ERANGE;
+	}
+
+	switch (val_len) {
+	case REGMAP_SIZE_8:
+		__write_8(ptr, val, map->endianness);
+		break;
+	case REGMAP_SIZE_16:
+		__write_16(ptr, val, map->endianness);
+		break;
+	case REGMAP_SIZE_32:
+		__write_32(ptr, val, map->endianness);
+		break;
+#if defined(out_le64) && defined(out_be64) && defined(writeq)
+	case REGMAP_SIZE_64:
+		__write_64(ptr, val, map->endianness);
+		break;
+#endif
+	default:
+		debug("%s: regmap size %zu unknown\n", __func__, val_len);
+		return -EINVAL;
+	}
 
 	return 0;
 }
 
+int regmap_raw_write(struct regmap *map, uint offset, const void *val,
+		     size_t val_len)
+{
+	return regmap_raw_write_range(map, 0, offset, val, val_len);
+}
+
+int regmap_write(struct regmap *map, uint offset, uint val)
+{
+	return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
+}
+
 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
 {
 	uint reg;
diff --git a/drivers/core/root.c b/drivers/core/root.c
index b54bf5bcdc5937352f81a950238d7cd85defaaa1..4ce55f9cc890d1726f98f3048cd6aec24d03d60d 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -222,14 +222,22 @@ static int dm_scan_fdt_live(struct udevice *parent,
 	int ret = 0, err;
 
 	for (np = node_parent->child; np; np = np->sibling) {
-		if (pre_reloc_only &&
-		    !of_find_property(np, "u-boot,dm-pre-reloc", NULL))
+		/* "chosen" node isn't a device itself but may contain some: */
+		if (!strcmp(np->name, "chosen")) {
+			pr_debug("parsing subnodes of \"chosen\"\n");
+
+			err = dm_scan_fdt_live(parent, np, pre_reloc_only);
+			if (err && !ret)
+				ret = err;
 			continue;
+		}
+
 		if (!of_device_is_available(np)) {
 			pr_debug("   - ignoring disabled device\n");
 			continue;
 		}
-		err = lists_bind_fdt(parent, np_to_ofnode(np), NULL);
+		err = lists_bind_fdt(parent, np_to_ofnode(np), NULL,
+				     pre_reloc_only);
 		if (err && !ret) {
 			ret = err;
 			debug("%s: ret=%d\n", np->name, ret);
@@ -282,14 +290,12 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob,
 			continue;
 		}
 
-		if (pre_reloc_only &&
-		    !dm_fdt_pre_reloc(blob, offset))
-			continue;
 		if (!fdtdec_get_is_enabled(blob, offset)) {
 			pr_debug("   - ignoring disabled device\n");
 			continue;
 		}
-		err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL);
+		err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL,
+				     pre_reloc_only);
 		if (err && !ret) {
 			ret = err;
 			debug("%s: ret=%d\n", node_name, ret);
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 3113d6a56ba334866f4f355fe65c9b25fafae1fa..6cfcde8918c4dc4dfba1e722e82ccb1d9ec6b16b 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -180,7 +180,7 @@ int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp)
 	if (list_empty(&uc->dev_head))
 		return -ENODEV;
 
-	list_for_each_entry(iter, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(iter, uc) {
 		if (iter == dev) {
 			if (ucp)
 				*ucp = uc;
@@ -205,7 +205,7 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
 	if (list_empty(&uc->dev_head))
 		return -ENODEV;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		if (!index--) {
 			*devp = dev;
 			return 0;
@@ -259,7 +259,7 @@ int uclass_find_device_by_name(enum uclass_id id, const char *name,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		if (!strncmp(dev->name, name, strlen(name))) {
 			*devp = dev;
 			return 0;
@@ -284,7 +284,7 @@ int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		debug("   - %d %d '%s'\n", dev->req_seq, dev->seq, dev->name);
 		if ((find_req_seq ? dev->req_seq : dev->seq) ==
 				seq_or_req_seq) {
@@ -312,7 +312,7 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		if (dev_of_offset(dev) == node) {
 			*devp = dev;
 			return 0;
@@ -337,7 +337,7 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		log(LOGC_DM, LOGL_DEBUG_CONTENT, "      - checking %s\n",
 		    dev->name);
 		if (ofnode_equal(dev_ofnode(dev), node)) {
@@ -372,7 +372,7 @@ static int uclass_find_device_by_phandle(enum uclass_id id,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		uint phandle;
 
 		phandle = dev_read_phandle(dev);
@@ -399,7 +399,7 @@ int uclass_get_device_by_driver(enum uclass_id id,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		if (dev->driver == find_drv)
 			return uclass_get_device_tail(dev, 0, devp);
 	}
@@ -499,7 +499,7 @@ int uclass_get_device_by_phandle_id(enum uclass_id id, uint phandle_id,
 	if (ret)
 		return ret;
 
-	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+	uclass_foreach_dev(dev, uc) {
 		uint phandle;
 
 		phandle = dev_read_phandle(dev);
@@ -687,8 +687,19 @@ int uclass_pre_probe_device(struct udevice *dev)
 
 int uclass_post_probe_device(struct udevice *dev)
 {
-	struct uclass_driver *uc_drv = dev->uclass->uc_drv;
+	struct uclass_driver *uc_drv;
+	int ret;
+
+	if (dev->parent) {
+		uc_drv = dev->parent->uclass->uc_drv;
+		if (uc_drv->child_post_probe) {
+			ret = uc_drv->child_post_probe(dev);
+			if (ret)
+				return ret;
+		}
+	}
 
+	uc_drv = dev->uclass->uc_drv;
 	if (uc_drv->post_probe)
 		return uc_drv->post_probe(dev);
 
diff --git a/drivers/core/util.c b/drivers/core/util.c
index 451d4766d08c7a9db25da13f54f9fa6af52ede1d..27a68487034e182331ece2ed2244ebf685bd5a79 100644
--- a/drivers/core/util.c
+++ b/drivers/core/util.c
@@ -4,6 +4,7 @@
  */
 
 #include <common.h>
+#include <dm/ofnode.h>
 #include <dm/util.h>
 #include <linux/libfdt.h>
 #include <vsprintf.h>
@@ -53,3 +54,27 @@ bool dm_fdt_pre_reloc(const void *blob, int offset)
 
 	return false;
 }
+
+bool dm_ofnode_pre_reloc(ofnode node)
+{
+	if (ofnode_read_bool(node, "u-boot,dm-pre-reloc"))
+		return true;
+
+#ifdef CONFIG_TPL_BUILD
+	if (ofnode_read_bool(node, "u-boot,dm-tpl"))
+		return true;
+#elif defined(CONFIG_SPL_BUILD)
+	if (ofnode_read_bool(node, "u-boot,dm-spl"))
+		return true;
+#else
+	/*
+	 * In regular builds individual spl and tpl handling both
+	 * count as handled pre-relocation for later second init.
+	 */
+	if (ofnode_read_bool(node, "u-boot,dm-spl") ||
+	    ofnode_read_bool(node, "u-boot,dm-tpl"))
+		return true;
+#endif
+
+	return false;
+}
diff --git a/drivers/cpu/mpc83xx_cpu.c b/drivers/cpu/mpc83xx_cpu.c
index 31717afaec4f899328a63ac9a49847e9653f612c..7bc86bf9b208a07fd3301848ba84c253d0fed576 100644
--- a/drivers/cpu/mpc83xx_cpu.c
+++ b/drivers/cpu/mpc83xx_cpu.c
@@ -262,7 +262,7 @@ static int mpc83xx_cpu_get_desc(struct udevice *dev, char *buf, int size)
 	determine_cpu_data(dev);
 
 	snprintf(buf, size,
-		 "CPU:   %s, MPC%s%s%s, Rev: %d.%d at %s MHz, CSB: %s MHz\n",
+		 "%s, MPC%s%s%s, Rev: %d.%d at %s MHz, CSB: %s MHz",
 		 e300_names[priv->e300_type],
 		 cpu_type_names[priv->type],
 		 priv->is_e_processor ? "E" : "",
diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c
index 555eba26622e81a7ea1201522fea4460607db266..0031415d03eff9ad00f29d0f0519d67611cca2a8 100644
--- a/drivers/gpio/omap_gpio.c
+++ b/drivers/gpio/omap_gpio.c
@@ -372,7 +372,9 @@ U_BOOT_DRIVER(gpio_omap) = {
 	.ops	= &gpio_omap_ops,
 	.probe	= omap_gpio_probe,
 	.priv_auto_alloc_size = sizeof(struct gpio_bank),
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
 
 #endif /* CONFIG_DM_GPIO */
diff --git a/drivers/gpio/stm32f7_gpio.c b/drivers/gpio/stm32f7_gpio.c
index 4c0786fff88da382e3be6db392f53dc062b2d84f..b903dc46b33e967e88d44d25d878e5df9d260ea8 100644
--- a/drivers/gpio/stm32f7_gpio.c
+++ b/drivers/gpio/stm32f7_gpio.c
@@ -123,6 +123,6 @@ U_BOOT_DRIVER(gpio_stm32) = {
 	.of_match = stm32_gpio_ids,
 	.probe	= gpio_stm32_probe,
 	.ops	= &gpio_stm32_ops,
-	.flags	= DM_FLAG_PRE_RELOC | DM_UC_FLAG_SEQ_ALIAS,
+	.flags	= DM_UC_FLAG_SEQ_ALIAS,
 	.priv_auto_alloc_size	= sizeof(struct stm32_gpio_priv),
 };
diff --git a/drivers/gpio/tegra186_gpio.c b/drivers/gpio/tegra186_gpio.c
index 1f0e8d51dadacaf56d752e762a466956e4deb7e7..6626b5415a389d97176032e29f92e7bb1dafbd7d 100644
--- a/drivers/gpio/tegra186_gpio.c
+++ b/drivers/gpio/tegra186_gpio.c
@@ -281,5 +281,4 @@ U_BOOT_DRIVER(tegra186_gpio) = {
 	.bind = tegra186_gpio_bind,
 	.probe = tegra186_gpio_probe,
 	.ops = &tegra186_gpio_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
index 302efddc2729aa00b3598d35342e1a8adb77994c..a730f5c4fec0903fd3aa9f7d2be82a06b3f25538 100644
--- a/drivers/gpio/tegra_gpio.c
+++ b/drivers/gpio/tegra_gpio.c
@@ -378,5 +378,4 @@ U_BOOT_DRIVER(gpio_tegra) = {
 	.probe = gpio_tegra_probe,
 	.priv_auto_alloc_size = sizeof(struct tegra_port_info),
 	.ops	= &gpio_tegra_ops,
-	.flags	= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index 54bf35e552eed47df0ef8a1fc1a4bf2a42c92a68..51f923752c319d7807383e0ffbacef133fc6700b 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -925,7 +925,9 @@ U_BOOT_DRIVER(i2c_omap) = {
 	.probe	= omap_i2c_probe,
 	.priv_auto_alloc_size = sizeof(struct omap_i2c),
 	.ops	= &omap_i2c_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags  = DM_FLAG_PRE_RELOC,
+#endif
 };
 
 #endif /* CONFIG_DM_I2C */
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c5697011f22d449a7d89c00175a8d9a15c241c78..48febc47d263b9be2dd1439901e6fe03ba581062 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -312,4 +312,21 @@ config FS_LOADER
 	  The consumer driver would then use this loader to program whatever,
 	  ie. the FPGA device.
 
+config GDSYS_SOC
+	bool "Enable gdsys SOC driver"
+	depends on MISC
+	help
+	  Support for gdsys IHS SOC, a simple bus associated with each gdsys
+	  IHS (Integrated Hardware Systems) FPGA, which holds all devices whose
+	  register maps are contained within the FPGA's register map.
+
+config IHS_FPGA
+	bool "Enable IHS FPGA driver"
+	depends on MISC
+	help
+	  Support IHS (Integrated Hardware Systems) FPGA, the main FPGAs on
+	  gdsys devices, which supply the majority of the functionality offered
+	  by the devices. This driver supports both CON and CPU variants of the
+	  devices, depending on the device tree entry.
+
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 759d2c791be69040c55ee4bae1166c22a48a43ae..302d4415927431ab593eea03b4c2366fcc22ff67 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -4,11 +4,6 @@
 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 
 obj-$(CONFIG_MISC) += misc-uclass.o
-obj-$(CONFIG_ALI152X) += ali512x.o
-obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
-obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
-obj-$(CONFIG_DS4510)  += ds4510.o
-obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_CROS_EC) += cros_ec.o
 obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
@@ -16,46 +11,54 @@ obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
 obj-$(CONFIG_CROS_EC_SANDBOX) += cros_ec_sandbox.o
 obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
 endif
-obj-$(CONFIG_FSL_IIM) += fsl_iim.o
-obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o
-obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o
-obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
-obj-$(CONFIG_IMX8) += imx8/
-obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o
-obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
-obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
-obj-$(CONFIG_NS87308) += ns87308.o
-obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
+
 ifdef CONFIG_DM_I2C
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o
 endif
 endif
-obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
-obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
-obj-$(CONFIG_LED_STATUS) += status_led.o
-obj-$(CONFIG_SANDBOX) += swap_case.o
 ifdef CONFIG_SPL_OF_PLATDATA
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SANDBOX) += spltest_sandbox.o
 endif
 endif
-obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
-obj-$(CONFIG_TEGRA_CAR) += tegra_car.o
-obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o
-obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
+obj-$(CONFIG_ALI152X) += ali512x.o
+obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
+obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
+obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
+obj-$(CONFIG_DS4510)  += ds4510.o
+obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
 obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
+obj-$(CONFIG_FSL_IIM) += fsl_iim.o
+obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
 obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
+obj-$(CONFIG_FS_LOADER) += fs_loader.o
+obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
+obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
+obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o
+obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o
+obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o
+obj-$(CONFIG_IMX8) += imx8/
+obj-$(CONFIG_LED_STATUS) += status_led.o
+obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o
+obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o
+obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o
+obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
+obj-$(CONFIG_NS87308) += ns87308.o
+obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
 obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
-obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
-obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o
+obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
 obj-$(CONFIG_QFW) += qfw.o
 obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
-obj-$(CONFIG_STM32_RCC) += stm32_rcc.o
+obj-$(CONFIG_SANDBOX) += swap_case.o
+obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
+obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
+obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
 obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o
+obj-$(CONFIG_STM32_RCC) += stm32_rcc.o
 obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o
-obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
-obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
+obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o
+obj-$(CONFIG_TEGRA_CAR) += tegra_car.o
+obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
 obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress_config.o
-obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o
-obj-$(CONFIG_FS_LOADER) += fs_loader.o
+obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o
diff --git a/drivers/misc/gdsys_soc.c b/drivers/misc/gdsys_soc.c
new file mode 100644
index 0000000000000000000000000000000000000000..94a21e08af713368b900c376de9c13431d51a4fa
--- /dev/null
+++ b/drivers/misc/gdsys_soc.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/lists.h>
+
+#include "gdsys_soc.h"
+
+/**
+ * struct gdsys_soc_priv - Private data for gdsys soc bus
+ * @fpga: The gdsys IHS FPGA this bus is associated with
+ */
+struct gdsys_soc_priv {
+	struct udevice *fpga;
+};
+
+static const struct udevice_id gdsys_soc_ids[] = {
+	{ .compatible = "gdsys,soc" },
+	{ /* sentinel */ }
+};
+
+int gdsys_soc_get_fpga(struct udevice *child, struct udevice **fpga)
+{
+	struct gdsys_soc_priv *bus_priv;
+
+	if (!child->parent) {
+		debug("%s: Invalid parent\n", child->name);
+		return -EINVAL;
+	}
+
+	if (!device_is_compatible(child->parent, "gdsys,soc")) {
+		debug("%s: Not child of a gdsys soc\n", child->name);
+		return -EINVAL;
+	}
+
+	bus_priv = dev_get_priv(child->parent);
+
+	*fpga = bus_priv->fpga;
+
+	return 0;
+}
+
+static int gdsys_soc_probe(struct udevice *dev)
+{
+	struct gdsys_soc_priv *priv = dev_get_priv(dev);
+	struct udevice *fpga;
+	int res = uclass_get_device_by_phandle(UCLASS_MISC, dev, "fpga",
+					       &fpga);
+	if (res == -ENOENT) {
+		debug("%s: Could not find 'fpga' phandle\n", dev->name);
+		return -EINVAL;
+	}
+
+	if (res == -ENODEV) {
+		debug("%s: Could not get FPGA device\n", dev->name);
+		return -EINVAL;
+	}
+
+	priv->fpga = fpga;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(gdsys_soc_bus) = {
+	.name           = "gdsys_soc_bus",
+	.id             = UCLASS_SIMPLE_BUS,
+	.of_match       = gdsys_soc_ids,
+	.probe          = gdsys_soc_probe,
+	.priv_auto_alloc_size = sizeof(struct gdsys_soc_priv),
+};
diff --git a/drivers/misc/gdsys_soc.h b/drivers/misc/gdsys_soc.h
new file mode 100644
index 0000000000000000000000000000000000000000..088d3b65234e6c9c9ffcf105f2b369c85982764a
--- /dev/null
+++ b/drivers/misc/gdsys_soc.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#ifndef _GDSYS_SOC_H_
+#define _GDSYS_SOC_H_
+
+/**
+ * gdsys_soc_get_fpga() - Retrieve pointer to parent bus' FPGA device
+ * @child:	The child device on the FPGA bus needing access to the FPGA.
+ * @fpga:	Pointer to the retrieved FPGA device.
+ *
+ * To access their register maps, devices on gdsys soc buses usually have
+ * facilitate the accessor function of the IHS FPGA their parent bus is
+ * attached to. To access the FPGA device from within the bus' children, this
+ * function returns a pointer to it.
+ *
+ * Return: 0 on success, -ve on failure
+ */
+int gdsys_soc_get_fpga(struct udevice *child, struct udevice **fpga);
+#endif /* _GDSYS_SOC_H_ */
diff --git a/drivers/misc/ihs_fpga.c b/drivers/misc/ihs_fpga.c
new file mode 100644
index 0000000000000000000000000000000000000000..f9e4b27a270da3b5c29abf79b101129dfe03cf84
--- /dev/null
+++ b/drivers/misc/ihs_fpga.c
@@ -0,0 +1,867 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * based on the ioep-fpga driver, which is
+ *
+ * (C) Copyright 2014
+ * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <asm/gpio.h>
+
+#include "ihs_fpga.h"
+
+/**
+ * struct ihs_fpga_priv - Private data structure for IHS FPGA driver
+ * @map:        Register map for the FPGA's own register space
+ * @reset_gpio: GPIO to start FPGA reconfiguration
+ * @done_gpio:  GPOI to read the 'ready' status of the FPGA
+ */
+struct ihs_fpga_priv {
+	struct regmap *map;
+	struct gpio_desc reset_gpio;
+	struct gpio_desc done_gpio;
+};
+
+/* Test pattern for reflection test */
+const u16 REFLECTION_TESTPATTERN = 0xdead;
+/* Delay (in ms) for each round in the reflection test */
+const uint REFLECTION_TEST_DELAY = 100;
+/* Maximum number of rounds in the reflection test */
+const uint REFLECTION_TEST_ROUNDS = 5;
+/* Delay (in ms) for each round waiting for the FPGA's done GPIO */
+const uint FPGA_DONE_WAIT_DELAY = 100;
+/* Maximum number of rounds for waiting for the FPGA's done GPIO */
+const uint FPGA_DONE_WAIT_ROUND = 5;
+
+/**
+ * enum pcb_video_type - Video type of the PCB
+ * @PCB_DVI_SL:     Video type is DVI single-link
+ * @PCB_DP_165MPIX: Video type is DisplayPort (165Mpix)
+ * @PCB_DP_300MPIX: Video type is DisplayPort (300Mpix)
+ * @PCB_HDMI:       Video type is HDMI
+ * @PCB_DP_1_2:     Video type is DisplayPort 1.2
+ * @PCB_HDMI_2_0:   Video type is HDMI 2.0
+ */
+enum pcb_video_type {
+	PCB_DVI_SL,
+	PCB_DP_165MPIX,
+	PCB_DP_300MPIX,
+	PCB_HDMI,
+	PCB_DP_1_2,
+	PCB_HDMI_2_0,
+};
+
+/**
+ * enum pcb_transmission_type - Transmission type of the PCB
+ * @PCB_CAT_1G:    Transmission type is 1G Ethernet
+ * @PCB_FIBER_3G:  Transmission type is 3G Fiber
+ * @PCB_CAT_10G:   Transmission type is 10G Ethernet
+ * @PCB_FIBER_10G: Transmission type is 10G Fiber
+ */
+enum pcb_transmission_type {
+	PCB_CAT_1G,
+	PCB_FIBER_3G,
+	PCB_CAT_10G,
+	PCB_FIBER_10G,
+};
+
+/**
+ * enum carrier_speed - Speed of the FPGA's carrier
+ * @CARRIER_SPEED_1G:   The carrier speed is 1G
+ * @CARRIER_SPEED_2_5G: The carrier speed is 2.5G
+ * @CARRIER_SPEED_3G:   The carrier speed is 3G
+ * @CARRIER_SPEED_10G:  The carrier speed is 10G
+ */
+enum carrier_speed {
+	CARRIER_SPEED_1G,
+	CARRIER_SPEED_3G,
+	CARRIER_SPEED_2_5G = CARRIER_SPEED_3G,
+	CARRIER_SPEED_10G,
+};
+
+/**
+ * enum ram_config - FPGA's RAM configuration
+ * @RAM_DDR2_32BIT_295MBPS:  DDR2 32 bit at 295Mb/s
+ * @RAM_DDR3_32BIT_590MBPS:  DDR3 32 bit at 590Mb/s
+ * @RAM_DDR3_48BIT_590MBPS:  DDR3 48 bit at 590Mb/s
+ * @RAM_DDR3_64BIT_1800MBPS: DDR3 64 bit at 1800Mb/s
+ * @RAM_DDR3_48BIT_1800MBPS: DDR3 48 bit at 1800Mb/s
+ */
+enum ram_config {
+	RAM_DDR2_32BIT_295MBPS,
+	RAM_DDR3_32BIT_590MBPS,
+	RAM_DDR3_48BIT_590MBPS,
+	RAM_DDR3_64BIT_1800MBPS,
+	RAM_DDR3_48BIT_1800MBPS,
+};
+
+/**
+ * enum sysclock - Speed of the FPGA's system clock
+ * @SYSCLK_147456: System clock is 147.456 MHz
+ */
+enum sysclock {
+	SYSCLK_147456,
+};
+
+/**
+ * struct fpga_versions - Data read from the versions register
+ * @video_channel:	   Is the FPGA for a video channel (true) or main
+ *			   channel (false) device?
+ * @con_side:		   Is the FPGA for a CON (true) or a CPU (false) device?
+ * @pcb_video_type:	   Defines for whch video type the FPGA is configured
+ * @pcb_transmission_type: Defines for which transmission type the FPGA is
+ *			   configured
+ * @hw_version:		   Hardware version of the FPGA
+ */
+struct fpga_versions {
+	bool video_channel;
+	bool con_side;
+	enum pcb_video_type pcb_video_type;
+	enum pcb_transmission_type pcb_transmission_type;
+	unsigned int hw_version;
+};
+
+/**
+ * struct fpga_features - Data read from the features register
+ * @video_channels:	Number of video channels supported
+ * @carriers:		Number of carrier channels supported
+ * @carrier_speed:	Speed of carriers
+ * @ram_config:		RAM configuration of FPGA
+ * @sysclock:		System clock speed of FPGA
+ * @pcm_tx:		Support for PCM transmission
+ * @pcm_rx:		Support for PCM reception
+ * @spdif_tx:		Support for SPDIF audio transmission
+ * @spdif_rx:		Support for SPDIF audio reception
+ * @usb2:		Support for transparent USB2.0
+ * @rs232:		Support for bidirectional RS232
+ * @compression_type1:	Support for compression type 1
+ * @compression_type2:	Support for compression type 2
+ * @compression_type3:	Support for compression type 3
+ * @interlace:		Support for interlace image formats
+ * @osd:		Support for a OSD
+ * @compression_pipes:	Number of compression pipes supported
+ */
+struct fpga_features {
+	u8 video_channels;
+	u8 carriers;
+	enum carrier_speed carrier_speed;
+	enum ram_config ram_config;
+	enum sysclock sysclock;
+	bool pcm_tx;
+	bool pcm_rx;
+	bool spdif_tx;
+	bool spdif_rx;
+	bool usb2;
+	bool rs232;
+	bool compression_type1;
+	bool compression_type2;
+	bool compression_type3;
+	bool interlace;
+	bool osd;
+	bool compression_pipes;
+};
+
+#ifdef CONFIG_SYS_FPGA_FLAVOR_GAZERBEAM
+
+/**
+ * get_versions() - Fill structure with info from version register.
+ * @dev:      FPGA device to be queried for information
+ * @versions: Pointer to the structure to fill with information from the
+ *	      versions register
+ * Return: 0
+ */
+static int get_versions(struct udevice *dev, struct fpga_versions *versions)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	enum {
+		VERSIONS_FPGA_VIDEO_CHANNEL = BIT(12),
+		VERSIONS_FPGA_CON_SIDE = BIT(13),
+		VERSIONS_FPGA_SC = BIT(14),
+		VERSIONS_PCB_CON = BIT(9),
+		VERSIONS_PCB_SC = BIT(8),
+		VERSIONS_PCB_VIDEO_MASK = 0x3 << 6,
+		VERSIONS_PCB_VIDEO_DP_1_2 = 0x0 << 6,
+		VERSIONS_PCB_VIDEO_HDMI_2_0 = 0x1 << 6,
+		VERSIONS_PCB_TRANSMISSION_MASK = 0x3 << 4,
+		VERSIONS_PCB_TRANSMISSION_FIBER_10G = 0x0 << 4,
+		VERSIONS_PCB_TRANSMISSION_CAT_10G = 0x1 << 4,
+		VERSIONS_PCB_TRANSMISSION_FIBER_3G = 0x2 << 4,
+		VERSIONS_PCB_TRANSMISSION_CAT_1G = 0x3 << 4,
+		VERSIONS_HW_VER_MASK = 0xf << 0,
+	};
+	u16 raw_versions;
+
+	memset(versions, 0, sizeof(struct fpga_versions));
+
+	ihs_fpga_get(priv->map, versions, &raw_versions);
+
+	versions->video_channel = raw_versions & VERSIONS_FPGA_VIDEO_CHANNEL;
+	versions->con_side = raw_versions & VERSIONS_FPGA_CON_SIDE;
+
+	switch (raw_versions & VERSIONS_PCB_VIDEO_MASK) {
+	case VERSIONS_PCB_VIDEO_DP_1_2:
+		versions->pcb_video_type = PCB_DP_1_2;
+		break;
+
+	case VERSIONS_PCB_VIDEO_HDMI_2_0:
+		versions->pcb_video_type = PCB_HDMI_2_0;
+		break;
+	}
+
+	switch (raw_versions & VERSIONS_PCB_TRANSMISSION_MASK) {
+	case VERSIONS_PCB_TRANSMISSION_FIBER_10G:
+		versions->pcb_transmission_type = PCB_FIBER_10G;
+		break;
+
+	case VERSIONS_PCB_TRANSMISSION_CAT_10G:
+		versions->pcb_transmission_type = PCB_CAT_10G;
+		break;
+
+	case VERSIONS_PCB_TRANSMISSION_FIBER_3G:
+		versions->pcb_transmission_type = PCB_FIBER_3G;
+		break;
+
+	case VERSIONS_PCB_TRANSMISSION_CAT_1G:
+		versions->pcb_transmission_type = PCB_CAT_1G;
+		break;
+	}
+
+	versions->hw_version = raw_versions & VERSIONS_HW_VER_MASK;
+
+	return 0;
+}
+
+/**
+ * get_features() - Fill structure with info from features register.
+ * @dev:      FPGA device to be queried for information
+ * @features: Pointer to the structure to fill with information from the
+ *	      features register
+ * Return: 0
+ */
+static int get_features(struct udevice *dev, struct fpga_features *features)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	enum {
+		FEATURE_SPDIF_RX = BIT(15),
+		FEATURE_SPDIF_TX = BIT(14),
+		FEATURE_PCM_RX = BIT(13),
+		FEATURE_PCM_TX = BIT(12),
+		FEATURE_RAM_MASK = GENMASK(11, 8),
+		FEATURE_RAM_DDR2_32BIT_295MBPS = 0x0 << 8,
+		FEATURE_RAM_DDR3_32BIT_590MBPS = 0x1 << 8,
+		FEATURE_RAM_DDR3_48BIT_590MBPS = 0x2 << 8,
+		FEATURE_RAM_DDR3_64BIT_1800MBPS = 0x3 << 8,
+		FEATURE_RAM_DDR3_48BIT_1800MBPS = 0x4 << 8,
+		FEATURE_CARRIER_SPEED_MASK = GENMASK(7, 6),
+		FEATURE_CARRIER_SPEED_1G = 0x0 << 6,
+		FEATURE_CARRIER_SPEED_2_5G = 0x1 << 6,
+		FEATURE_CARRIER_SPEED_10G = 0x2 << 6,
+		FEATURE_CARRIERS_MASK = GENMASK(5, 4),
+		FEATURE_CARRIERS_0 = 0x0 << 4,
+		FEATURE_CARRIERS_1 = 0x1 << 4,
+		FEATURE_CARRIERS_2 = 0x2 << 4,
+		FEATURE_CARRIERS_4 = 0x3 << 4,
+		FEATURE_USB2 = BIT(3),
+		FEATURE_VIDEOCHANNELS_MASK = GENMASK(2, 0),
+		FEATURE_VIDEOCHANNELS_0 = 0x0 << 0,
+		FEATURE_VIDEOCHANNELS_1 = 0x1 << 0,
+		FEATURE_VIDEOCHANNELS_1_1 = 0x2 << 0,
+		FEATURE_VIDEOCHANNELS_2 = 0x3 << 0,
+	};
+
+	enum {
+		EXT_FEATURE_OSD = BIT(15),
+		EXT_FEATURE_ETHERNET = BIT(9),
+		EXT_FEATURE_INTERLACE = BIT(8),
+		EXT_FEATURE_RS232 = BIT(7),
+		EXT_FEATURE_COMPRESSION_PERF_MASK = GENMASK(6, 4),
+		EXT_FEATURE_COMPRESSION_PERF_1X = 0x0 << 4,
+		EXT_FEATURE_COMPRESSION_PERF_2X = 0x1 << 4,
+		EXT_FEATURE_COMPRESSION_PERF_4X = 0x2 << 4,
+		EXT_FEATURE_COMPRESSION_TYPE1 = BIT(0),
+		EXT_FEATURE_COMPRESSION_TYPE2 = BIT(1),
+		EXT_FEATURE_COMPRESSION_TYPE3 = BIT(2),
+	};
+
+	u16 raw_features;
+	u16 raw_extended_features;
+
+	memset(features, 0, sizeof(struct fpga_features));
+
+	ihs_fpga_get(priv->map, features, &raw_features);
+	ihs_fpga_get(priv->map, extended_features, &raw_extended_features);
+
+	switch (raw_features & FEATURE_VIDEOCHANNELS_MASK) {
+	case FEATURE_VIDEOCHANNELS_0:
+		features->video_channels = 0;
+		break;
+
+	case FEATURE_VIDEOCHANNELS_1:
+		features->video_channels = 1;
+		break;
+
+	case FEATURE_VIDEOCHANNELS_1_1:
+	case FEATURE_VIDEOCHANNELS_2:
+		features->video_channels = 2;
+		break;
+	};
+
+	switch (raw_features & FEATURE_CARRIERS_MASK) {
+	case FEATURE_CARRIERS_0:
+		features->carriers = 0;
+		break;
+
+	case FEATURE_CARRIERS_1:
+		features->carriers = 1;
+		break;
+
+	case FEATURE_CARRIERS_2:
+		features->carriers = 2;
+		break;
+
+	case FEATURE_CARRIERS_4:
+		features->carriers = 4;
+		break;
+	}
+
+	switch (raw_features & FEATURE_CARRIER_SPEED_MASK) {
+	case FEATURE_CARRIER_SPEED_1G:
+		features->carrier_speed = CARRIER_SPEED_1G;
+		break;
+	case FEATURE_CARRIER_SPEED_2_5G:
+		features->carrier_speed = CARRIER_SPEED_2_5G;
+		break;
+	case FEATURE_CARRIER_SPEED_10G:
+		features->carrier_speed = CARRIER_SPEED_10G;
+		break;
+	}
+
+	switch (raw_features & FEATURE_RAM_MASK) {
+	case FEATURE_RAM_DDR2_32BIT_295MBPS:
+		features->ram_config = RAM_DDR2_32BIT_295MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_32BIT_590MBPS:
+		features->ram_config = RAM_DDR3_32BIT_590MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_48BIT_590MBPS:
+		features->ram_config = RAM_DDR3_48BIT_590MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_64BIT_1800MBPS:
+		features->ram_config = RAM_DDR3_64BIT_1800MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_48BIT_1800MBPS:
+		features->ram_config = RAM_DDR3_48BIT_1800MBPS;
+		break;
+	}
+
+	features->pcm_tx = raw_features & FEATURE_PCM_TX;
+	features->pcm_rx = raw_features & FEATURE_PCM_RX;
+	features->spdif_tx = raw_features & FEATURE_SPDIF_TX;
+	features->spdif_rx = raw_features & FEATURE_SPDIF_RX;
+	features->usb2 = raw_features & FEATURE_USB2;
+	features->rs232 = raw_extended_features & EXT_FEATURE_RS232;
+	features->compression_type1 = raw_extended_features &
+					EXT_FEATURE_COMPRESSION_TYPE1;
+	features->compression_type2 = raw_extended_features &
+					EXT_FEATURE_COMPRESSION_TYPE2;
+	features->compression_type3 = raw_extended_features &
+					EXT_FEATURE_COMPRESSION_TYPE3;
+	features->interlace = raw_extended_features & EXT_FEATURE_INTERLACE;
+	features->osd = raw_extended_features & EXT_FEATURE_OSD;
+	features->compression_pipes = raw_extended_features &
+					EXT_FEATURE_COMPRESSION_PERF_MASK;
+
+	return 0;
+}
+
+#else
+
+/**
+ * get_versions() - Fill structure with info from version register.
+ * @fpga:     Identifier of the FPGA device to be queried for information
+ * @versions: Pointer to the structure to fill with information from the
+ *	      versions register
+ *
+ * This is the legacy version and should be considered deprecated for new
+ * devices.
+ *
+ * Return: 0
+ */
+static int get_versions(unsigned int fpga, struct fpga_versions *versions)
+{
+	enum {
+		/* HW version encoding is a mess, leave it for the moment */
+		VERSIONS_HW_VER_MASK = 0xf << 0,
+		VERSIONS_PIX_CLOCK_GEN_IDT8N3QV01 = BIT(4),
+		VERSIONS_SFP = BIT(5),
+		VERSIONS_VIDEO_MASK = 0x7 << 6,
+		VERSIONS_VIDEO_DVI = 0x0 << 6,
+		VERSIONS_VIDEO_DP_165 = 0x1 << 6,
+		VERSIONS_VIDEO_DP_300 = 0x2 << 6,
+		VERSIONS_VIDEO_HDMI = 0x3 << 6,
+		VERSIONS_UT_MASK = 0xf << 12,
+		VERSIONS_UT_MAIN_SERVER = 0x0 << 12,
+		VERSIONS_UT_MAIN_USER = 0x1 << 12,
+		VERSIONS_UT_VIDEO_SERVER = 0x2 << 12,
+		VERSIONS_UT_VIDEO_USER = 0x3 << 12,
+	};
+	u16 raw_versions;
+
+	memset(versions, 0, sizeof(struct fpga_versions));
+
+	FPGA_GET_REG(fpga, versions, &raw_versions);
+
+	switch (raw_versions & VERSIONS_UT_MASK) {
+	case VERSIONS_UT_MAIN_SERVER:
+		versions->video_channel = false;
+		versions->con_side = false;
+		break;
+
+	case VERSIONS_UT_MAIN_USER:
+		versions->video_channel = false;
+		versions->con_side = true;
+		break;
+
+	case VERSIONS_UT_VIDEO_SERVER:
+		versions->video_channel = true;
+		versions->con_side = false;
+		break;
+
+	case VERSIONS_UT_VIDEO_USER:
+		versions->video_channel = true;
+		versions->con_side = true;
+		break;
+	}
+
+	switch (raw_versions & VERSIONS_VIDEO_MASK) {
+	case VERSIONS_VIDEO_DVI:
+		versions->pcb_video_type = PCB_DVI_SL;
+		break;
+
+	case VERSIONS_VIDEO_DP_165:
+		versions->pcb_video_type = PCB_DP_165MPIX;
+		break;
+
+	case VERSIONS_VIDEO_DP_300:
+		versions->pcb_video_type = PCB_DP_300MPIX;
+		break;
+
+	case VERSIONS_VIDEO_HDMI:
+		versions->pcb_video_type = PCB_HDMI;
+		break;
+	}
+
+	versions->hw_version = raw_versions & VERSIONS_HW_VER_MASK;
+
+	if (raw_versions & VERSIONS_SFP)
+		versions->pcb_transmission_type = PCB_FIBER_3G;
+	else
+		versions->pcb_transmission_type = PCB_CAT_1G;
+
+	return 0;
+}
+
+/**
+ * get_features() - Fill structure with info from features register.
+ * @fpga:     Identifier of the FPGA device to be queried for information
+ * @features: Pointer to the structure to fill with information from the
+ *	      features register
+ *
+ * This is the legacy version and should be considered deprecated for new
+ * devices.
+ *
+ * Return: 0
+ */
+static int get_features(unsigned int fpga, struct fpga_features *features)
+{
+	enum {
+		FEATURE_CARRIER_SPEED_2_5 = BIT(4),
+		FEATURE_RAM_MASK = 0x7 << 5,
+		FEATURE_RAM_DDR2_32BIT = 0x0 << 5,
+		FEATURE_RAM_DDR3_32BIT = 0x1 << 5,
+		FEATURE_RAM_DDR3_48BIT = 0x2 << 5,
+		FEATURE_PCM_AUDIO_TX = BIT(9),
+		FEATURE_PCM_AUDIO_RX = BIT(10),
+		FEATURE_OSD = BIT(11),
+		FEATURE_USB20 = BIT(12),
+		FEATURE_COMPRESSION_MASK = 7 << 13,
+		FEATURE_COMPRESSION_TYPE1 = 0x1 << 13,
+		FEATURE_COMPRESSION_TYPE1_TYPE2 = 0x3 << 13,
+		FEATURE_COMPRESSION_TYPE1_TYPE2_TYPE3 = 0x7 << 13,
+	};
+
+	enum {
+		EXTENDED_FEATURE_SPDIF_AUDIO_TX = BIT(0),
+		EXTENDED_FEATURE_SPDIF_AUDIO_RX = BIT(1),
+		EXTENDED_FEATURE_RS232 = BIT(2),
+		EXTENDED_FEATURE_COMPRESSION_PIPES = BIT(3),
+		EXTENDED_FEATURE_INTERLACE = BIT(4),
+	};
+
+	u16 raw_features;
+	u16 raw_extended_features;
+
+	memset(features, 0, sizeof(struct fpga_features));
+
+	FPGA_GET_REG(fpga, fpga_features, &raw_features);
+	FPGA_GET_REG(fpga, fpga_ext_features, &raw_extended_features);
+
+	features->video_channels = raw_features & 0x3;
+	features->carriers = (raw_features >> 2) & 0x3;
+
+	features->carrier_speed = (raw_features & FEATURE_CARRIER_SPEED_2_5)
+		? CARRIER_SPEED_2_5G : CARRIER_SPEED_1G;
+
+	switch (raw_features & FEATURE_RAM_MASK) {
+	case FEATURE_RAM_DDR2_32BIT:
+		features->ram_config = RAM_DDR2_32BIT_295MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_32BIT:
+		features->ram_config = RAM_DDR3_32BIT_590MBPS;
+		break;
+
+	case FEATURE_RAM_DDR3_48BIT:
+		features->ram_config = RAM_DDR3_48BIT_590MBPS;
+		break;
+	}
+
+	features->pcm_tx = raw_features & FEATURE_PCM_AUDIO_TX;
+	features->pcm_rx = raw_features & FEATURE_PCM_AUDIO_RX;
+	features->spdif_tx = raw_extended_features &
+				EXTENDED_FEATURE_SPDIF_AUDIO_TX;
+	features->spdif_rx = raw_extended_features &
+				EXTENDED_FEATURE_SPDIF_AUDIO_RX;
+
+	features->usb2 = raw_features & FEATURE_USB20;
+	features->rs232 = raw_extended_features & EXTENDED_FEATURE_RS232;
+
+	features->compression_type1 = false;
+	features->compression_type2 = false;
+	features->compression_type3 = false;
+	switch (raw_features & FEATURE_COMPRESSION_MASK) {
+	case FEATURE_COMPRESSION_TYPE1_TYPE2_TYPE3:
+		features->compression_type3 = true;
+		/* fall-through */
+	case FEATURE_COMPRESSION_TYPE1_TYPE2:
+		features->compression_type2 = true;
+		/* fall-through */
+	case FEATURE_COMPRESSION_TYPE1:
+		features->compression_type1 = true;
+		break;
+	}
+
+	features->interlace = raw_extended_features &
+				EXTENDED_FEATURE_INTERLACE;
+	features->osd = raw_features & FEATURE_OSD;
+	features->compression_pipes = raw_extended_features &
+					EXTENDED_FEATURE_COMPRESSION_PIPES;
+
+	return 0;
+}
+
+#endif
+
+/**
+ * fpga_print_info() - Print information about FPGA device
+ * @dev: FPGA device to print information about
+ */
+static void fpga_print_info(struct udevice *dev)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	u16 fpga_version;
+	struct fpga_versions versions;
+	struct fpga_features features;
+
+	ihs_fpga_get(priv->map, fpga_version, &fpga_version);
+	get_versions(dev, &versions);
+	get_features(dev, &features);
+
+	if (versions.video_channel)
+		printf("Videochannel");
+	else
+		printf("Mainchannel");
+
+	if (versions.con_side)
+		printf(" User");
+	else
+		printf(" Server");
+
+	switch (versions.pcb_transmission_type) {
+	case PCB_CAT_1G:
+	case PCB_CAT_10G:
+		printf(" CAT");
+		break;
+	case PCB_FIBER_3G:
+	case PCB_FIBER_10G:
+		printf(" Fiber");
+		break;
+	};
+
+	switch (versions.pcb_video_type) {
+	case PCB_DVI_SL:
+		printf(" DVI,");
+		break;
+	case PCB_DP_165MPIX:
+		printf(" DP 165MPix/s,");
+		break;
+	case PCB_DP_300MPIX:
+		printf(" DP 300MPix/s,");
+		break;
+	case PCB_HDMI:
+		printf(" HDMI,");
+		break;
+	case PCB_DP_1_2:
+		printf(" DP 1.2,");
+		break;
+	case PCB_HDMI_2_0:
+		printf(" HDMI 2.0,");
+		break;
+	}
+
+	printf(" FPGA V %d.%02d\n       features: ",
+	       fpga_version / 100, fpga_version % 100);
+
+	if (!features.compression_type1 &&
+	    !features.compression_type2 &&
+	    !features.compression_type3)
+		printf("no compression, ");
+
+	if (features.compression_type1)
+		printf("type1, ");
+
+	if (features.compression_type2)
+		printf("type2, ");
+
+	if (features.compression_type3)
+		printf("type3, ");
+
+	printf("%sosd", features.osd ? "" : "no ");
+
+	if (features.pcm_rx && features.pcm_tx)
+		printf(", pcm rx+tx");
+	else if (features.pcm_rx)
+		printf(", pcm rx");
+	else if (features.pcm_tx)
+		printf(", pcm tx");
+
+	if (features.spdif_rx && features.spdif_tx)
+		printf(", spdif rx+tx");
+	else if (features.spdif_rx)
+		printf(", spdif rx");
+	else if (features.spdif_tx)
+		printf(", spdif tx");
+
+	puts(",\n       ");
+
+	switch (features.sysclock) {
+	case SYSCLK_147456:
+		printf("clock 147.456 MHz");
+		break;
+	}
+
+	switch (features.ram_config) {
+	case RAM_DDR2_32BIT_295MBPS:
+		printf(", RAM 32 bit DDR2");
+		break;
+	case RAM_DDR3_32BIT_590MBPS:
+		printf(", RAM 32 bit DDR3");
+		break;
+	case RAM_DDR3_48BIT_590MBPS:
+	case RAM_DDR3_48BIT_1800MBPS:
+		printf(", RAM 48 bit DDR3");
+		break;
+	case RAM_DDR3_64BIT_1800MBPS:
+		printf(", RAM 64 bit DDR3");
+		break;
+	}
+
+	printf(", %d carrier(s)", features.carriers);
+
+	switch (features.carrier_speed) {
+	case CARRIER_SPEED_1G:
+		printf(", 1Gbit/s");
+		break;
+	case CARRIER_SPEED_3G:
+		printf(", 3Gbit/s");
+		break;
+	case CARRIER_SPEED_10G:
+		printf(", 10Gbit/s");
+		break;
+	}
+
+	printf(", %d video channel(s)\n", features.video_channels);
+}
+
+/**
+ * do_reflection_test() - Run reflection test on a FPGA device
+ * @dev: FPGA device to run reflection test on
+ *
+ * Return: 0 if reflection test succeeded, -ve on error
+ */
+static int do_reflection_test(struct udevice *dev)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	int ctr = 0;
+
+	while (1) {
+		u16 val;
+
+		ihs_fpga_set(priv->map, reflection_low, REFLECTION_TESTPATTERN);
+
+		ihs_fpga_get(priv->map, reflection_low, &val);
+		if (val == (~REFLECTION_TESTPATTERN & 0xffff))
+			return -EIO;
+
+		mdelay(REFLECTION_TEST_DELAY);
+		if (ctr++ > REFLECTION_TEST_ROUNDS)
+			return 0;
+	}
+}
+
+/**
+ * wait_for_fpga_done() - Wait until 'done'-flag is set for FPGA device
+ * @dev: FPGA device whose done flag to wait for
+ *
+ * This function waits until it detects that the done-GPIO's value was changed
+ * to 1 by the FPGA, which indicates that the device is configured and ready to
+ * use.
+ *
+ * Return: 0 if done flag was detected, -ve on error
+ */
+static int wait_for_fpga_done(struct udevice *dev)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	int ctr = 0;
+	int done_val;
+
+	while (1) {
+		done_val = dm_gpio_get_value(&priv->done_gpio);
+		if (done_val < 0) {
+			debug("%s: Error while reading done-GPIO (err = %d)\n",
+			      dev->name, done_val);
+			return done_val;
+		}
+
+		if (done_val)
+			return 0;
+
+		mdelay(FPGA_DONE_WAIT_DELAY);
+		if (ctr++ > FPGA_DONE_WAIT_ROUND) {
+			debug("%s: FPGA init failed (done not detected)\n",
+			      dev->name);
+			return -EIO;
+		}
+	}
+}
+
+static int ihs_fpga_probe(struct udevice *dev)
+{
+	struct ihs_fpga_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	/* TODO(mario.six@gdsys.cc): Case of FPGA attached to MCLink bus */
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
+	if (ret) {
+		debug("%s: Could not initialize regmap (err = %d)",
+		      dev->name, ret);
+		return ret;
+	}
+
+	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio,
+				   GPIOD_IS_OUT);
+	if (ret) {
+		debug("%s: Could not get reset-GPIO (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	if (!priv->reset_gpio.dev) {
+		debug("%s: Could not get reset-GPIO\n", dev->name);
+		return -ENOENT;
+	}
+
+	ret = gpio_request_by_name(dev, "done-gpios", 0, &priv->done_gpio,
+				   GPIOD_IS_IN);
+	if (ret) {
+		debug("%s: Could not get done-GPIO (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	if (!priv->done_gpio.dev) {
+		debug("%s: Could not get done-GPIO\n", dev->name);
+		return -ENOENT;
+	}
+
+	ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+	if (ret) {
+		debug("%s: Error while setting reset-GPIO (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	/* If FPGA already runs, don't initialize again */
+	if (do_reflection_test(dev))
+		goto reflection_ok;
+
+	ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+	if (ret) {
+		debug("%s: Error while setting reset-GPIO (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	ret = wait_for_fpga_done(dev);
+	if (ret) {
+		debug("%s: Error while waiting for FPGA done (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	udelay(10);
+
+	ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+	if (ret) {
+		debug("%s: Error while setting reset-GPIO (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	if (!do_reflection_test(dev)) {
+		debug("%s: Reflection test FAILED\n", dev->name);
+		return -EIO;
+	}
+
+reflection_ok:
+	printf("%s: Reflection test passed.\n", dev->name);
+
+	fpga_print_info(dev);
+
+	return 0;
+}
+
+static const struct udevice_id ihs_fpga_ids[] = {
+	{ .compatible = "gdsys,iocon_fpga" },
+	{ .compatible = "gdsys,iocpu_fpga" },
+	{ }
+};
+
+U_BOOT_DRIVER(ihs_fpga_bus) = {
+	.name           = "ihs_fpga_bus",
+	.id             = UCLASS_MISC,
+	.of_match       = ihs_fpga_ids,
+	.probe          = ihs_fpga_probe,
+	.priv_auto_alloc_size = sizeof(struct ihs_fpga_priv),
+};
diff --git a/drivers/misc/ihs_fpga.h b/drivers/misc/ihs_fpga.h
new file mode 100644
index 0000000000000000000000000000000000000000..efb5dabb9c9410662b51d214d664fd1e040ff4fb
--- /dev/null
+++ b/drivers/misc/ihs_fpga.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+/**
+ * struct ihs_fpga_regs - IHS FPGA register map structure
+ * @reflection_low:	  Lower reflection register
+ * @versions:		  PCB versions register
+ * @fpga_version:	  FPGA versions register
+ * @features:		  FPGA features register
+ * @extended_features:	  FPGA extended features register
+ * @top_interrupt:	  Top interrupt register
+ * @top_interrupt_enable: Top interrupt enable register
+ * @status:		  FPGA status register
+ * @control:		  FPGA control register
+ * @extended_control:	  FPGA extended control register
+ */
+struct ihs_fpga_regs {
+	u16 reflection_low;
+	u16 versions;
+	u16 fpga_version;
+	u16 features;
+	u16 extended_features;
+	u16 top_interrupt;
+	u16 top_interrupt_enable;
+	u16 status;
+	u16 control;
+	u16 extended_control;
+};
+
+/**
+ * ihs_fpga_set() - Convenience macro to set values in FPGA register map
+ * @map:    Register map to set a value in
+ * @member: Name of member (described by ihs_fpga_regs) to set
+ * @val:    Value to set the member to
+ */
+#define ihs_fpga_set(map, member, val) \
+	regmap_set(map, struct ihs_fpga_regs, member, val)
+
+/**
+ * ihs_fpga_get() - Convenience macro to get values from FPGA register map
+ * @map:    Register map to read value from
+ * @member: Name of member (described by ihs_fpga_regs) to get
+ * @valp:   Pointe to variable to receive the value read
+ */
+#define ihs_fpga_get(map, member, valp) \
+	regmap_get(map, struct ihs_fpga_regs, member, valp)
diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c
index 0647ddf10321594624e14232f9b53fbca8dbbd62..b824ac79e6ddb53ed33e561f9a13645eae61c519 100644
--- a/drivers/misc/imx8/scu.c
+++ b/drivers/misc/imx8/scu.c
@@ -223,7 +223,7 @@ static int imx8_scu_bind(struct udevice *dev)
 	if (node < 0)
 		panic("No clk node found\n");
 
-	ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child);
+	ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child, true);
 	if (ret)
 		return ret;
 
@@ -234,7 +234,7 @@ static int imx8_scu_bind(struct udevice *dev)
 	if (node < 0)
 		panic("No iomuxc node found\n");
 
-	ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child);
+	ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child, true);
 	if (ret)
 		return ret;
 
diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c
index bffb809f14d9fdc87f6464e3a948dbd898cc2621..fa608cec1b92a0a9cd2a55586ddb135fd27df263 100644
--- a/drivers/misc/swap_case.c
+++ b/drivers/misc/swap_case.c
@@ -124,12 +124,21 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
 	case PCI_CAP_ID_PM_OFFSET:
 		*valuep = (PCI_CAP_ID_EXP_OFFSET << 8) | PCI_CAP_ID_PM;
 		break;
+	case PCI_CAP_ID_PM_OFFSET + PCI_CAP_LIST_NEXT:
+		*valuep = PCI_CAP_ID_EXP_OFFSET;
+		break;
 	case PCI_CAP_ID_EXP_OFFSET:
 		*valuep = (PCI_CAP_ID_MSIX_OFFSET << 8) | PCI_CAP_ID_EXP;
 		break;
+	case PCI_CAP_ID_EXP_OFFSET + PCI_CAP_LIST_NEXT:
+		*valuep = PCI_CAP_ID_MSIX_OFFSET;
+		break;
 	case PCI_CAP_ID_MSIX_OFFSET:
 		*valuep = PCI_CAP_ID_MSIX;
 		break;
+	case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
+		*valuep = 0;
+		break;
 	case PCI_EXT_CAP_ID_ERR_OFFSET:
 		*valuep = (PCI_EXT_CAP_ID_VC_OFFSET << 20) | PCI_EXT_CAP_ID_ERR;
 		break;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 585951cd78e917045c2d0ecb113cba06698817ff..d6b9cdc99229d8bfe0c8be3adfd6e5209a11601c 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -2444,9 +2444,6 @@ static int mmc_startup(struct mmc *mmc)
 	bdesc->product[0] = 0;
 	bdesc->revision[0] = 0;
 #endif
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
-	part_init(bdesc);
-#endif
 
 	return 0;
 }
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index ec853d063f0bbb19084367b31ed495a12607973a..5cb97eb02af85a1ef806dcc8e848a42c02a07d81 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -1953,6 +1953,8 @@ U_BOOT_DRIVER(omap_hsmmc) = {
 	.ops = &omap_hsmmc_ops,
 	.probe	= omap_hsmmc_probe,
 	.priv_auto_alloc_size = sizeof(struct omap_hsmmc_data),
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags	= DM_FLAG_PRE_RELOC,
+#endif
 };
 #endif
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index eb6fdeda5015b33ce7d52b5b62201d2d3d0dd729..1ee0a0aefb14e350d878e2237a29962af57d6834 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -664,7 +664,6 @@ static int nvme_blk_probe(struct udevice *udev)
 	sprintf(desc->vendor, "0x%.4x", pplat->vendor);
 	memcpy(desc->product, ndev->serial, sizeof(ndev->serial));
 	memcpy(desc->revision, ndev->firmware_rev, sizeof(ndev->firmware_rev));
-	part_init(desc);
 
 	return 0;
 }
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index da49c96ed5e365a3acb3298ca69b83dbb923288c..0c52337f33a2315a435109638f09aa4783f2b3ab 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -1344,26 +1344,14 @@ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
 	return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
 }
 
-int dm_pci_find_capability(struct udevice *dev, int cap)
+static int _dm_pci_find_next_capability(struct udevice *dev, u8 pos, int cap)
 {
-	u16 status;
-	u8 header_type;
 	int ttl = PCI_FIND_CAP_TTL;
 	u8 id;
 	u16 ent;
-	u8 pos;
-
-	dm_pci_read_config16(dev, PCI_STATUS, &status);
-	if (!(status & PCI_STATUS_CAP_LIST))
-		return 0;
-
-	dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
-	if ((header_type & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
-		pos = PCI_CB_CAPABILITY_LIST;
-	else
-		pos = PCI_CAPABILITY_LIST;
 
 	dm_pci_read_config8(dev, pos, &pos);
+
 	while (ttl--) {
 		if (pos < PCI_STD_HEADER_SIZEOF)
 			break;
@@ -1381,7 +1369,32 @@ int dm_pci_find_capability(struct udevice *dev, int cap)
 	return 0;
 }
 
-int dm_pci_find_ext_capability(struct udevice *dev, int cap)
+int dm_pci_find_next_capability(struct udevice *dev, u8 start, int cap)
+{
+	return _dm_pci_find_next_capability(dev, start + PCI_CAP_LIST_NEXT,
+					    cap);
+}
+
+int dm_pci_find_capability(struct udevice *dev, int cap)
+{
+	u16 status;
+	u8 header_type;
+	u8 pos;
+
+	dm_pci_read_config16(dev, PCI_STATUS, &status);
+	if (!(status & PCI_STATUS_CAP_LIST))
+		return 0;
+
+	dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
+	if ((header_type & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
+		pos = PCI_CB_CAPABILITY_LIST;
+	else
+		pos = PCI_CAPABILITY_LIST;
+
+	return _dm_pci_find_next_capability(dev, pos, cap);
+}
+
+int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap)
 {
 	u32 header;
 	int ttl;
@@ -1390,6 +1403,9 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap)
 	/* minimum 8 bytes per capability */
 	ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
 
+	if (start)
+		pos = start;
+
 	dm_pci_read_config32(dev, pos, &header);
 	/*
 	 * If we have no capabilities, this is indicated by cap ID,
@@ -1412,6 +1428,11 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap)
 	return 0;
 }
 
+int dm_pci_find_ext_capability(struct udevice *dev, int cap)
+{
+	return dm_pci_find_next_ext_capability(dev, 0, cap);
+}
+
 UCLASS_DRIVER(pci) = {
 	.id		= UCLASS_PCI,
 	.name		= "pci",
diff --git a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
index 891b4c25fdaf4c12764c079bd9c04f6655d7953a..3be080d29e37410b808cd84f2cb3ddc006ea3863 100644
--- a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
+++ b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
@@ -148,5 +148,7 @@ U_BOOT_DRIVER(pinctrl_bcm283x) = {
 	.priv_auto_alloc_size = sizeof(struct bcm283x_pinctrl_priv),
 	.ops		= &bcm283x_pinctrl_ops,
 	.probe		= bcm283x_pinctl_probe,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags		= DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/exynos/pinctrl-exynos7420.c b/drivers/pinctrl/exynos/pinctrl-exynos7420.c
index cb5975b252ee384f47e54d8a5ec81c262bfedc46..ff6d6c4143f571e439237c0371c71ae85b5e0c69 100644
--- a/drivers/pinctrl/exynos/pinctrl-exynos7420.c
+++ b/drivers/pinctrl/exynos/pinctrl-exynos7420.c
@@ -113,5 +113,4 @@ U_BOOT_DRIVER(pinctrl_exynos7420) = {
 	.priv_auto_alloc_size = sizeof(struct exynos_pinctrl_priv),
 	.ops		= &exynos7420_pinctrl_ops,
 	.probe		= exynos_pinctrl_probe,
-	.flags		= DM_FLAG_PRE_RELOC
 };
diff --git a/drivers/pinctrl/nxp/pinctrl-imx5.c b/drivers/pinctrl/nxp/pinctrl-imx5.c
index 5d1738091980bff17e6586aa93fc1ab535237d18..4e831b6f39e79cbbf79bf7e50acdde323de5b5de 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx5.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx5.c
@@ -40,5 +40,7 @@ U_BOOT_DRIVER(imx5_pinctrl) = {
 	.remove = imx_pinctrl_remove,
 	.priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv),
 	.ops = &imx_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/nxp/pinctrl-imx6.c b/drivers/pinctrl/nxp/pinctrl-imx6.c
index e63ecbdee6b6427a974c1aab0ef2269e87de2159..d7c95bb7387b443195c6791e363fb50e65a3f8b9 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx6.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx6.c
@@ -49,5 +49,7 @@ U_BOOT_DRIVER(imx6_pinctrl) = {
 	.remove = imx_pinctrl_remove,
 	.priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv),
 	.ops = &imx_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/nxp/pinctrl-imx7.c b/drivers/pinctrl/nxp/pinctrl-imx7.c
index 769d428ddadcfea07e62c542f6f988235010555c..8776fd9650493f6eeb80f076b6b35226879cb99a 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx7.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx7.c
@@ -37,5 +37,7 @@ U_BOOT_DRIVER(imx7_pinctrl) = {
 	.remove = imx_pinctrl_remove,
 	.priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv),
 	.ops = &imx_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
index 598bbfaf3505f8d3182c6d8467e5b758aa4be9f1..d778f82aac7be6a1101a34e318ddc9da38cc233e 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
@@ -41,5 +41,7 @@ U_BOOT_DRIVER(imx7ulp_pinctrl) = {
 	.remove = imx_pinctrl_remove,
 	.priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv),
 	.ops = &imx_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index d80c6eda70e44ca198df25f72ff9716d2beb08a1..9dec88c1aa01e3bf1e844f7bc2e3eda5689ec770 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -136,7 +136,6 @@ U_BOOT_DRIVER(single_pinctrl) = {
 	.id = UCLASS_PINCTRL,
 	.of_match = single_pinctrl_match,
 	.ops = &single_pinctrl_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 	.platdata_auto_alloc_size = sizeof(struct single_pdata),
 	.ofdata_to_platdata = single_ofdata_to_platdata,
 };
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
index a1da90baa447c7efc52d792688c8bb081b0f35ba..eb5978a1662c0cd1172cd2e88c2061f3f75109f5 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
@@ -171,5 +171,7 @@ U_BOOT_DRIVER(uniphier_pro4_pinctrl) = {
 	.probe = uniphier_pro4_pinctrl_probe,
 	.priv_auto_alloc_size = sizeof(struct uniphier_pinctrl_priv),
 	.ops = &uniphier_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
index 0ba2052b293051cf580c9f8bd9666fbcf19093bb..685d8be80c47def1e80a00174e1e14b1af79922b 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
@@ -153,5 +153,7 @@ U_BOOT_DRIVER(uniphier_pro5_pinctrl) = {
 	.probe = uniphier_pro5_pinctrl_probe,
 	.priv_auto_alloc_size = sizeof(struct uniphier_pinctrl_priv),
 	.ops = &uniphier_pinctrl_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
diff --git a/drivers/ram/bmips_ram.c b/drivers/ram/bmips_ram.c
index b5f19c983c80726ca0e767442a4ada19cc6abc44..3e1dd9e24101300b017866b61e07e18c07b92a2d 100644
--- a/drivers/ram/bmips_ram.c
+++ b/drivers/ram/bmips_ram.c
@@ -173,5 +173,4 @@ U_BOOT_DRIVER(bmips_ram) = {
 	.probe = bmips_ram_probe,
 	.priv_auto_alloc_size = sizeof(struct bmips_ram_priv),
 	.ops = &bmips_ram_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index bc6ac8cd327a63ff81dba132a2b4a8618ef230fd..df47e2fc78bdc5c611e11a2c952481fda0185ed9 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -592,7 +592,6 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose)
 	memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor));
 	memcpy(&bdesc->product, &bd.product, sizeof(bd.product));
 	memcpy(&bdesc->revision, &bd.revision,	sizeof(bd.revision));
-	part_init(bdesc);
 
 	if (verbose) {
 		printf("  Device %d: ", 0);
diff --git a/drivers/serial/altera_jtag_uart.c b/drivers/serial/altera_jtag_uart.c
index 61052a92d670aba70974ee3b3c1e2abd05542f2c..86c3de4e455d9cf86fe69d34aee400b9c7c59cd9 100644
--- a/drivers/serial/altera_jtag_uart.c
+++ b/drivers/serial/altera_jtag_uart.c
@@ -121,7 +121,6 @@ U_BOOT_DRIVER(altera_jtaguart) = {
 	.platdata_auto_alloc_size = sizeof(struct altera_jtaguart_platdata),
 	.probe = altera_jtaguart_probe,
 	.ops	= &altera_jtaguart_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_ALTERA_JTAGUART
diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c
index b7b0a13ca12ec456ce6045346c9094ba2fedf161..67d47199aa4c8d1dd6060f7ff2a663aa0c038eba 100644
--- a/drivers/serial/altera_uart.c
+++ b/drivers/serial/altera_uart.c
@@ -117,7 +117,6 @@ U_BOOT_DRIVER(altera_uart) = {
 	.platdata_auto_alloc_size = sizeof(struct altera_uart_platdata),
 	.probe = altera_uart_probe,
 	.ops	= &altera_uart_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_ALTERA_UART
diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c
index 43e8691a93a65a9cfe8245849654a6313d788809..dfcb6fd698122d4f38174f4d75dff465c57043f5 100644
--- a/drivers/serial/arm_dcc.c
+++ b/drivers/serial/arm_dcc.c
@@ -155,7 +155,6 @@ U_BOOT_DRIVER(serial_dcc) = {
 	.id	= UCLASS_SERIAL,
 	.of_match = arm_dcc_ids,
 	.ops	= &arm_dcc_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_ARM_DCC
diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c
index 9414f5f692618e637261d9682e98eaab464cd146..aa8cdff84082995a7e6c820ab52d9c4c345e72c6 100644
--- a/drivers/serial/atmel_usart.c
+++ b/drivers/serial/atmel_usart.c
@@ -294,7 +294,9 @@ U_BOOT_DRIVER(serial_atmel) = {
 #endif
 	.probe = atmel_serial_probe,
 	.ops	= &atmel_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 	.priv_auto_alloc_size	= sizeof(struct atmel_serial_priv),
 };
 #endif
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index f9041aa626d673ea107d7bed562cc2beb6e95545..1e6fc6c66839c844aaaf1c808633ba020b085fa2 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -267,12 +267,26 @@ static inline void _debug_uart_init(void)
 	serial_dout(&com_port->lcr, UART_LCRVAL);
 }
 
+static inline int NS16550_read_baud_divisor(struct NS16550 *com_port)
+{
+	int ret;
+
+	serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL);
+	ret = serial_din(&com_port->dll) & 0xff;
+	ret |= (serial_din(&com_port->dlm) & 0xff) << 8;
+	serial_dout(&com_port->lcr, UART_LCRVAL);
+
+	return ret;
+}
+
 static inline void _debug_uart_putc(int ch)
 {
 	struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
 
-	while (!(serial_din(&com_port->lsr) & UART_LSR_THRE))
-		;
+	while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) {
+		if (!NS16550_read_baud_divisor(com_port))
+			return;
+	}
 	serial_dout(&com_port->thr, ch);
 }
 
@@ -473,7 +487,9 @@ U_BOOT_DRIVER(ns16550_serial) = {
 	.priv_auto_alloc_size = sizeof(struct NS16550),
 	.probe = ns16550_serial_probe,
 	.ops	= &ns16550_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags	= DM_FLAG_PRE_RELOC,
+#endif
 };
 #endif
 #endif /* SERIAL_PRESENT */
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 665cca85cb5683f024cba340ab96d6742075fd4b..3ded62732d4b21324cd981153b71aee22a99a496 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -62,7 +62,7 @@ static int serial_check_stdout(const void *blob, struct udevice **devp)
 	 * anyway.
 	 */
 	if (node > 0 && !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node),
-					devp)) {
+					devp, false)) {
 		if (!device_probe(*devp))
 			return 0;
 	}
diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c
index e91a5f7b24bfb2f293283ca784212cbde10e4363..5249c553987112e07a61e925b18ce2fe73641c24 100644
--- a/drivers/serial/serial_ar933x.c
+++ b/drivers/serial/serial_ar933x.c
@@ -189,7 +189,6 @@ U_BOOT_DRIVER(serial_ar933x) = {
 	.priv_auto_alloc_size = sizeof(struct ar933x_serial_priv),
 	.probe = ar933x_serial_probe,
 	.ops    = &ar933x_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_AR933X
diff --git a/drivers/serial/serial_arc.c b/drivers/serial/serial_arc.c
index 925f0c2555446021ff24fca47f685273e2764acf..980b38d2a1ca7f54796f69c8daad47ba8d4a698b 100644
--- a/drivers/serial/serial_arc.c
+++ b/drivers/serial/serial_arc.c
@@ -128,7 +128,6 @@ U_BOOT_DRIVER(serial_arc) = {
 	.ofdata_to_platdata = arc_serial_ofdata_to_platdata,
 	.probe = arc_serial_probe,
 	.ops	= &arc_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_ARC_SERIAL
diff --git a/drivers/serial/serial_bcm283x_mu.c b/drivers/serial/serial_bcm283x_mu.c
index 1f87f0cb28ad74cb13b5b8089b9f2e62e7ca3131..bd1d89ec8393493cafc838318f238d43d18100f6 100644
--- a/drivers/serial/serial_bcm283x_mu.c
+++ b/drivers/serial/serial_bcm283x_mu.c
@@ -199,6 +199,8 @@ U_BOOT_DRIVER(serial_bcm283x_mu) = {
 	.platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata),
 	.probe = bcm283x_mu_serial_probe,
 	.ops = &bcm283x_mu_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 	.priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv),
 };
diff --git a/drivers/serial/serial_bcm283x_pl011.c b/drivers/serial/serial_bcm283x_pl011.c
index 54fc9b5b396e017742b80ee58815c28d3ef88021..2527bb8b1c850e01c76d13a1d5f9b0c5e15ded6e 100644
--- a/drivers/serial/serial_bcm283x_pl011.c
+++ b/drivers/serial/serial_bcm283x_pl011.c
@@ -90,6 +90,8 @@ U_BOOT_DRIVER(bcm283x_pl011_uart) = {
 	.platdata_auto_alloc_size = sizeof(struct pl01x_serial_platdata),
 	.probe	= pl01x_serial_probe,
 	.ops	= &bcm283x_pl011_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags	= DM_FLAG_PRE_RELOC,
+#endif
 	.priv_auto_alloc_size = sizeof(struct pl01x_priv),
 };
diff --git a/drivers/serial/serial_bcm6345.c b/drivers/serial/serial_bcm6345.c
index ee5d561bfd195f620f68d6bb93fe9250b59224f5..a0e709a11e90736ca313fa7f2020edcbb4ac6ea8 100644
--- a/drivers/serial/serial_bcm6345.c
+++ b/drivers/serial/serial_bcm6345.c
@@ -264,7 +264,6 @@ U_BOOT_DRIVER(bcm6345_serial) = {
 	.probe = bcm6345_serial_probe,
 	.priv_auto_alloc_size = sizeof(struct bcm6345_serial_priv),
 	.ops = &bcm6345_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_BCM6345
diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c
index 1b54d1880f93aa51723a86ebefad36d4abc3f6e1..dd3e511fc91d65f378fc2edd4e741cb343f50638 100644
--- a/drivers/serial/serial_efi.c
+++ b/drivers/serial/serial_efi.c
@@ -152,5 +152,4 @@ U_BOOT_DRIVER(serial_efi) = {
 	.priv_auto_alloc_size = sizeof(struct serial_efi_priv),
 	.probe = serial_efi_probe,
 	.ops	= &serial_efi_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/serial/serial_intel_mid.c b/drivers/serial/serial_intel_mid.c
index bdb5adb2a46a80ff71807a3c6195d0aadb978762..39bd40e68be106580e5269bb1d721eab70edafce 100644
--- a/drivers/serial/serial_intel_mid.c
+++ b/drivers/serial/serial_intel_mid.c
@@ -64,5 +64,4 @@ U_BOOT_DRIVER(serial_intel_mid) = {
 	.priv_auto_alloc_size = sizeof(struct NS16550),
 	.probe	= mid_serial_probe,
 	.ops	= &ns16550_serial_ops,
-	.flags	= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 6106c1f9ec83429b063c2d29b83e07dc82b9ca02..a357b00d28785eb07490d2126ddbbfa7cfbca31f 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -540,5 +540,4 @@ U_BOOT_DRIVER(serial_lpuart) = {
 	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
 	.probe = lpuart_serial_probe,
 	.ops	= &lpuart_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/serial/serial_meson.c b/drivers/serial/serial_meson.c
index dbb853735fbee2c3a82fdff9c862b5c5bed3b1e8..b3dad77aa28c21b2ae9494549e96366dbc869a3e 100644
--- a/drivers/serial/serial_meson.c
+++ b/drivers/serial/serial_meson.c
@@ -132,7 +132,6 @@ U_BOOT_DRIVER(serial_meson) = {
 	.of_match	= meson_serial_ids,
 	.probe		= meson_serial_probe,
 	.ops		= &meson_serial_ops,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.ofdata_to_platdata = meson_serial_ofdata_to_platdata,
 	.platdata_auto_alloc_size = sizeof(struct meson_serial_platdata),
 };
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c
index ce26d2bd155c0b66b923e2844239e7839018609e..7e4cd6c4b49b4d9dbe59b2096a337bd356f6b488 100644
--- a/drivers/serial/serial_mvebu_a3700.c
+++ b/drivers/serial/serial_mvebu_a3700.c
@@ -129,7 +129,6 @@ U_BOOT_DRIVER(serial_mvebu) = {
 	.platdata_auto_alloc_size = sizeof(struct mvebu_platdata),
 	.probe	= mvebu_serial_probe,
 	.ops	= &mvebu_serial_ops,
-	.flags	= DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_MVEBU_A3700_UART
diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c
index e586c18cf032c06b2727abe71c2a66dda4adb651..7e4e6d36b863c9d4f9c805a76ded6687da1a8ce6 100644
--- a/drivers/serial/serial_mxc.c
+++ b/drivers/serial/serial_mxc.c
@@ -354,7 +354,9 @@ U_BOOT_DRIVER(serial_mxc) = {
 #endif
 	.probe = mxc_serial_probe,
 	.ops	= &mxc_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
 #endif
 
diff --git a/drivers/serial/serial_omap.c b/drivers/serial/serial_omap.c
index af3c755f96311a66657152313a745805daec462b..ee6ad9c9e593a532627446c57f2cc6dff2183ab0 100644
--- a/drivers/serial/serial_omap.c
+++ b/drivers/serial/serial_omap.c
@@ -121,7 +121,9 @@ U_BOOT_DRIVER(omap_serial) = {
 	.priv_auto_alloc_size = sizeof(struct NS16550),
 	.probe = ns16550_serial_probe,
 	.ops	= &ns16550_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags	= DM_FLAG_PRE_RELOC,
+#endif
 };
 #endif
 #endif /* DM_SERIAL */
diff --git a/drivers/serial/serial_owl.c b/drivers/serial/serial_owl.c
index 6fd97e2502f217eef6c9a1f075f019e309b684c5..7ead73e6b7fd9401d5d8b0bd7b87238e12310450 100644
--- a/drivers/serial/serial_owl.c
+++ b/drivers/serial/serial_owl.c
@@ -132,5 +132,4 @@ U_BOOT_DRIVER(serial_owl) = {
 	.priv_auto_alloc_size =	sizeof(struct owl_serial_priv),
 	.probe = owl_serial_probe,
 	.ops = &owl_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c
index ba73978e259176729ec50260f6f2453a70e583c8..84600b12014bea12c17e885192a797d4ce74f792 100644
--- a/drivers/serial/serial_pic32.c
+++ b/drivers/serial/serial_pic32.c
@@ -176,7 +176,6 @@ U_BOOT_DRIVER(pic32_serial) = {
 	.of_match	= pic32_uart_ids,
 	.probe		= pic32_uart_probe,
 	.ops		= &pic32_uart_ops,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.priv_auto_alloc_size = sizeof(struct pic32_uart_priv),
 };
 
diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c
index 2a5f256184f2c195ed0088a211f31ca59540ab8c..12512f65782c76ddf7d5ed31e468a7095c1b794b 100644
--- a/drivers/serial/serial_pl01x.c
+++ b/drivers/serial/serial_pl01x.c
@@ -363,7 +363,9 @@ U_BOOT_DRIVER(serial_pl01x) = {
 	.platdata_auto_alloc_size = sizeof(struct pl01x_serial_platdata),
 	.probe = pl01x_serial_probe,
 	.ops	= &pl01x_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 	.priv_auto_alloc_size = sizeof(struct pl01x_priv),
 };
 
diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c
index faea6d4c995d3376eed1a11cd59beacca1b0e397..e3160cf1bd76104ea45b92b9a37cae53c1d8879a 100644
--- a/drivers/serial/serial_s5p.c
+++ b/drivers/serial/serial_s5p.c
@@ -211,7 +211,6 @@ U_BOOT_DRIVER(serial_s5p) = {
 	.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
 	.probe = s5p_serial_probe,
 	.ops	= &s5p_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 #endif
 
diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c
index b1534981f8cc4488e5df0b555b516253145ba162..c934d5f25a6792f6582911f2cf9d58ac08f78808 100644
--- a/drivers/serial/serial_sh.c
+++ b/drivers/serial/serial_sh.c
@@ -247,7 +247,9 @@ U_BOOT_DRIVER(serial_sh) = {
 	.platdata_auto_alloc_size = sizeof(struct sh_serial_platdata),
 	.probe	= sh_serial_probe,
 	.ops	= &sh_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags	= DM_FLAG_PRE_RELOC,
+#endif
 	.priv_auto_alloc_size = sizeof(struct uart_port),
 };
 
diff --git a/drivers/serial/serial_sti_asc.c b/drivers/serial/serial_sti_asc.c
index 5dfc6171eb542c33a0e65ba4f3aeda4fba6850ab..c972f1e9af5f8fa9d4a6fcd0ae2b3e72f8b687b1 100644
--- a/drivers/serial/serial_sti_asc.c
+++ b/drivers/serial/serial_sti_asc.c
@@ -205,6 +205,5 @@ U_BOOT_DRIVER(serial_sti_asc) = {
 	.ops = &sti_asc_serial_ops,
 	.probe = sti_asc_serial_probe,
 	.priv_auto_alloc_size = sizeof(struct sti_asc_serial),
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 66e02d5689d09ddc052b3d184c008a5b384ac50e..31b43ee88dd7ed7857b200cae5ab9ee345bb5ff7 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -230,7 +230,9 @@ U_BOOT_DRIVER(serial_stm32) = {
 	.platdata_auto_alloc_size = sizeof(struct stm32x7_serial_platdata),
 	.ops = &stm32_serial_ops,
 	.probe = stm32_serial_probe,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
 	.flags = DM_FLAG_PRE_RELOC,
+#endif
 };
 
 #ifdef CONFIG_DEBUG_UART_STM32
diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c
index cead3c62f5a144bce78913037441faf4cc4ad839..1be777bd3bb8b7d1bd1144ab9e833c9a6efdc291 100644
--- a/drivers/serial/serial_xuartlite.c
+++ b/drivers/serial/serial_xuartlite.c
@@ -109,7 +109,6 @@ U_BOOT_DRIVER(serial_uartlite) = {
 	.platdata_auto_alloc_size = sizeof(struct uartlite_platdata),
 	.probe = uartlite_serial_probe,
 	.ops	= &uartlite_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_UARTLITE
diff --git a/drivers/serial/serial_zynq.c b/drivers/serial/serial_zynq.c
index f689015b4ae7e96ebaf128802a821bdfc142bb7d..7e486a68ffc9c067595d161af185d2c857cebb8a 100644
--- a/drivers/serial/serial_zynq.c
+++ b/drivers/serial/serial_zynq.c
@@ -210,7 +210,6 @@ U_BOOT_DRIVER(serial_zynq) = {
 	.platdata_auto_alloc_size = sizeof(struct zynq_uart_platdata),
 	.probe = zynq_serial_probe,
 	.ops	= &zynq_serial_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
 #ifdef CONFIG_DEBUG_UART_ZYNQ
diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c
index 5943a63854cc541d63a8bcfe0bc4138c0916113d..20b958cfd4157ef05a506b5d9c1c0849438ae998 100644
--- a/drivers/sysreset/sysreset_x86.c
+++ b/drivers/sysreset/sysreset_x86.c
@@ -45,5 +45,4 @@ U_BOOT_DRIVER(x86_sysreset) = {
 	.id = UCLASS_SYSRESET,
 	.of_match = x86_sysreset_ids,
 	.ops = &x86_sysreset_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index d012cf71a96a5e28282006ce7e1d973469d9def2..d0cfc3530664b2151cc70387b97d6a7988d4b21c 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -40,6 +40,12 @@ config TIMER_EARLY
 	  use an early timer. These functions must be supported by your timer
 	  driver: timer_early_get_count() and timer_early_get_rate().
 
+config AG101P_TIMER
+	bool "AG101P timer support"
+	depends on TIMER && NDS32
+	help
+	  Select this to enable a timer for AG01P devices.
+
 config ALTERA_TIMER
 	bool "Altera timer support"
 	depends on TIMER
@@ -47,6 +53,34 @@ config ALTERA_TIMER
 	  Select this to enable a timer for Altera devices. Please find
 	  details on the "Embedded Peripherals IP User Guide" of Altera.
 
+config ARC_TIMER
+	bool "ARC timer support"
+	depends on TIMER && ARC && CLK
+	help
+	  Select this to enable built-in ARC timers.
+	  ARC cores may have up to 2 built-in timers: timer0 and timer1,
+	  usually at least one of them exists. Either of them is supported
+	  in U-Boot.
+
+config AST_TIMER
+	bool "Aspeed ast2400/ast2500 timer support"
+	depends on TIMER
+	default y if ARCH_ASPEED
+	help
+	  Select this to enable timer for Aspeed ast2400/ast2500 devices.
+	  This is a simple sys timer driver, it is compatible with lib/time.c,
+	  but does not support any interrupts. Even though SoC has 8 hardware
+	  counters, they are all treated as a single device by this driver.
+	  This is mostly because they all share several registers which
+	  makes it difficult to completely separate them.
+
+config ATCPIT100_TIMER
+	bool "ATCPIT100 timer support"
+	depends on TIMER
+	help
+	  Select this to enable a ATCPIT100 timer which will be embedded
+	  in AE3XX, AE250 boards.
+
 config ATMEL_PIT_TIMER
 	bool "Atmel periodic interval timer support"
 	depends on TIMER
@@ -69,18 +103,12 @@ config DESIGNWARE_APB_TIMER
 	  Enables support for the Designware APB Timer driver. This timer is
 	  present on Altera SoCFPGA SoCs.
 
-config SANDBOX_TIMER
-	bool "Sandbox timer support"
-	depends on SANDBOX && TIMER
-	help
-	  Select this to enable an emulated timer for sandbox. It gets
-	  time from host os.
-
-config X86_TSC_TIMER
-	bool "x86 Time-Stamp Counter (TSC) timer support"
-	depends on TIMER && X86
+config MPC83XX_TIMER
+	bool "MPC83xx timer support"
+	depends on TIMER
 	help
-	  Select this to enable Time-Stamp Counter (TSC) timer for x86.
+	  Select this to enable support for the timer found on
+	  devices based on the MPC83xx family of SoCs.
 
 config X86_TSC_TIMER_EARLY_FREQ
 	int "x86 TSC timer frequency in MHz when used as the early timer"
@@ -98,17 +126,19 @@ config OMAP_TIMER
 	help
 	  Select this to enable an timer for Omap devices.
 
-config AST_TIMER
-	bool "Aspeed ast2400/ast2500 timer support"
+config ROCKCHIP_TIMER
+	bool "Rockchip timer support"
 	depends on TIMER
-	default y if ARCH_ASPEED
 	help
-	  Select this to enable timer for Aspeed ast2400/ast2500 devices.
-	  This is a simple sys timer driver, it is compatible with lib/time.c,
-	  but does not support any interrupts. Even though SoC has 8 hardware
-	  counters, they are all treated as a single device by this driver.
-	  This is mostly because they all share several registers which
-	  makes it difficult to completely separate them.
+	  Select this to enable support for the timer found on
+	  Rockchip devices.
+
+config SANDBOX_TIMER
+	bool "Sandbox timer support"
+	depends on SANDBOX && TIMER
+	help
+	  Select this to enable an emulated timer for sandbox. It gets
+	  time from host os.
 
 config STI_TIMER
 	bool "STi timer support"
@@ -117,47 +147,17 @@ config STI_TIMER
 	help
 	  Select this to enable a timer for STi devices.
 
-config ARC_TIMER
-	bool "ARC timer support"
-	depends on TIMER && ARC && CLK
-	help
-	  Select this to enable built-in ARC timers.
-	  ARC cores may have up to 2 built-in timers: timer0 and timer1,
-	  usually at least one of them exists. Either of them is supported
-	  in U-Boot.
-
-config AG101P_TIMER
-	bool "AG101P timer support"
-	depends on TIMER && NDS32
-	help
-	  Select this to enable a timer for AG01P devices.
-
-config ATCPIT100_TIMER
-	bool "ATCPIT100 timer support"
-	depends on TIMER
-	help
-	  Select this to enable a ATCPIT100 timer which will be embeded
-		in AE3XX, AE250 boards.
-
-config ROCKCHIP_TIMER
-        bool "Rockchip timer support"
-	depends on TIMER
-	help
-	  Select this to enable support for the timer found on
-	  Rockchip devices.
-
 config STM32_TIMER
-        bool "STM32 timer support"
+	bool "STM32 timer support"
 	depends on TIMER
 	help
 	  Select this to enable support for the timer found on
 	  STM32 devices.
 
-config MPC83XX_TIMER
-        bool "MPC83xx timer support"
-	depends on TIMER
+config X86_TSC_TIMER
+	bool "x86 Time-Stamp Counter (TSC) timer support"
+	depends on TIMER && X86
 	help
-	  Select this to enable support for the timer found on
-	  devices based on the MPC83xx family of SoCs.
+	  Select this to enable Time-Stamp Counter (TSC) timer for x86.
 
 endmenu
diff --git a/drivers/timer/ag101p_timer.c b/drivers/timer/ag101p_timer.c
index 6e1ae68b32ecb969a0b5036a077b3d84f76a1e44..6e20b4fc337bacbd58d46ce665c19490ff1f0cef 100644
--- a/drivers/timer/ag101p_timer.c
+++ b/drivers/timer/ag101p_timer.c
@@ -115,5 +115,4 @@ U_BOOT_DRIVER(altera_timer) = {
 	.platdata_auto_alloc_size = sizeof(struct atftmr_timer_platdata),
 	.probe = atftmr_timer_probe,
 	.ops	= &ag101p_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/altera_timer.c b/drivers/timer/altera_timer.c
index bc76819674be7a2a1a6c7d08d13a21333c904260..6f504f7cc4d28d6c2429364f85d8047ca315f2e0 100644
--- a/drivers/timer/altera_timer.c
+++ b/drivers/timer/altera_timer.c
@@ -92,5 +92,4 @@ U_BOOT_DRIVER(altera_timer) = {
 	.platdata_auto_alloc_size = sizeof(struct altera_timer_platdata),
 	.probe = altera_timer_probe,
 	.ops	= &altera_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/arc_timer.c b/drivers/timer/arc_timer.c
index cf9671ebbe55726366f83477448f0d97323d609e..8c574ec5af1d8a46e02b50978b2f43f1f54b4b8b 100644
--- a/drivers/timer/arc_timer.c
+++ b/drivers/timer/arc_timer.c
@@ -107,6 +107,5 @@ U_BOOT_DRIVER(arc_timer) = {
 	.of_match = arc_timer_ids,
 	.probe = arc_timer_probe,
 	.ops	= &arc_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 	.priv_auto_alloc_size = sizeof(struct arc_timer_priv),
 };
diff --git a/drivers/timer/ast_timer.c b/drivers/timer/ast_timer.c
index 9973506f6b13d4daa297709867b2bd01cfe4cb91..21ffdbf575a18f08b708ff77f36b5d7bad1a58a8 100644
--- a/drivers/timer/ast_timer.c
+++ b/drivers/timer/ast_timer.c
@@ -90,5 +90,4 @@ U_BOOT_DRIVER(ast_timer) = {
 	.priv_auto_alloc_size = sizeof(struct ast_timer_priv),
 	.ofdata_to_platdata = ast_timer_ofdata_to_platdata,
 	.ops = &ast_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/atcpit100_timer.c b/drivers/timer/atcpit100_timer.c
index f650c1bf663510475ae15e07b807f9a80edd6c81..c5d43b4a4ae5e683d89cc9e14dc64fec9e1be98c 100644
--- a/drivers/timer/atcpit100_timer.c
+++ b/drivers/timer/atcpit100_timer.c
@@ -110,5 +110,4 @@ U_BOOT_DRIVER(atcpit100_timer) = {
 	.platdata_auto_alloc_size = sizeof(struct atcpit_timer_platdata),
 	.probe = atcpit_timer_probe,
 	.ops	= &atcpit_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/atmel_pit_timer.c b/drivers/timer/atmel_pit_timer.c
index 603563d3defe4063da5cdb694b0a84a77b43a919..009af2f9298c7c2172c17e68e11c4bf5fc58968e 100644
--- a/drivers/timer/atmel_pit_timer.c
+++ b/drivers/timer/atmel_pit_timer.c
@@ -85,5 +85,4 @@ U_BOOT_DRIVER(atmel_pit) = {
 	.platdata_auto_alloc_size = sizeof(struct atmel_pit_platdata),
 	.probe	= atmel_pit_probe,
 	.ops	= &atmel_pit_ops,
-	.flags	= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/cadence-ttc.c b/drivers/timer/cadence-ttc.c
index 4125a078b385d8d5c2b3f0ee55c4e79ae6804149..75263c5375e065dabd218c38620aec6910dd1b51 100644
--- a/drivers/timer/cadence-ttc.c
+++ b/drivers/timer/cadence-ttc.c
@@ -111,5 +111,4 @@ U_BOOT_DRIVER(cadence_ttc) = {
 	.priv_auto_alloc_size = sizeof(struct cadence_ttc_priv),
 	.probe = cadence_ttc_probe,
 	.ops = &cadence_ttc_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/dw-apb-timer.c b/drivers/timer/dw-apb-timer.c
index 031f429acb884f3fbcc17bb4ba1d3e514ce3ff51..085bfb02c531ede8c9f6c8e364a3ae4bf80c1b65 100644
--- a/drivers/timer/dw-apb-timer.c
+++ b/drivers/timer/dw-apb-timer.c
@@ -83,7 +83,6 @@ U_BOOT_DRIVER(dw_apb_timer) = {
 	.id		= UCLASS_TIMER,
 	.ops		= &dw_apb_timer_ops,
 	.probe		= dw_apb_timer_probe,
-	.flags		= DM_FLAG_PRE_RELOC,
 	.of_match	= dw_apb_timer_ids,
 	.ofdata_to_platdata = dw_apb_timer_ofdata_to_platdata,
 	.priv_auto_alloc_size = sizeof(struct dw_apb_timer_priv),
diff --git a/drivers/timer/mpc83xx_timer.c b/drivers/timer/mpc83xx_timer.c
index 84a9ab072a95641699cbf2145840b7fe177c3ae6..8e541109d4eaf69f4624fd93085eed4e1f1d0a70 100644
--- a/drivers/timer/mpc83xx_timer.c
+++ b/drivers/timer/mpc83xx_timer.c
@@ -244,6 +244,5 @@ U_BOOT_DRIVER(mpc83xx_timer) = {
 	.of_match = mpc83xx_timer_ids,
 	.probe = mpc83xx_timer_probe,
 	.ops	= &mpc83xx_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 	.priv_auto_alloc_size = sizeof(struct mpc83xx_timer_priv),
 };
diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c
index f10df69092a4500f1951705df5960e9dbfd65f2b..a13fb7116569a0f2addf0a588288cd379eea8ffe 100644
--- a/drivers/timer/omap-timer.c
+++ b/drivers/timer/omap-timer.c
@@ -104,5 +104,4 @@ U_BOOT_DRIVER(omap_timer) = {
 	.priv_auto_alloc_size = sizeof(struct omap_timer_priv),
 	.probe = omap_timer_probe,
 	.ops	= &omap_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/rockchip_timer.c b/drivers/timer/rockchip_timer.c
index 17bf6a44c38f83dfcbf93acd948b214be63d2348..69019740b0d1154c21e8c9cfa2b25b2b3f15cfea 100644
--- a/drivers/timer/rockchip_timer.c
+++ b/drivers/timer/rockchip_timer.c
@@ -163,7 +163,6 @@ U_BOOT_DRIVER(rockchip_rk3368_timer) = {
 	.of_match = rockchip_timer_ids,
 	.probe = rockchip_timer_probe,
 	.ops	= &rockchip_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 	.priv_auto_alloc_size = sizeof(struct rockchip_timer_priv),
 #if CONFIG_IS_ENABLED(OF_PLATDATA)
 	.platdata_auto_alloc_size = sizeof(struct rockchip_timer_plat),
diff --git a/drivers/timer/sti-timer.c b/drivers/timer/sti-timer.c
index f7f0e72fe16deae56062172b54001df8b6ecadd3..9def7e02f4bd88b61a2b064221825828a82c9501 100644
--- a/drivers/timer/sti-timer.c
+++ b/drivers/timer/sti-timer.c
@@ -74,5 +74,4 @@ U_BOOT_DRIVER(sti_timer) = {
 	.priv_auto_alloc_size = sizeof(struct sti_timer_priv),
 	.probe = sti_timer_probe,
 	.ops = &sti_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/timer/stm32_timer.c b/drivers/timer/stm32_timer.c
index 9a856b1b5afb498a9665f27fdbf1ff781ed155cf..76315100e22f2e3b2e77a57e1c4e2b63e2e21fdc 100644
--- a/drivers/timer/stm32_timer.c
+++ b/drivers/timer/stm32_timer.c
@@ -132,6 +132,5 @@ U_BOOT_DRIVER(stm32_timer) = {
 	.priv_auto_alloc_size = sizeof(struct stm32_timer_priv),
 	.probe = stm32_timer_probe,
 	.ops = &stm32_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
 
diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c
index fe73f71819914c948e3bcac65d90e49b321f825c..12ee6eb80434130eeeada1a9739e027b8d3580f1 100644
--- a/drivers/timer/timer-uclass.c
+++ b/drivers/timer/timer-uclass.c
@@ -108,7 +108,7 @@ int notrace dm_timer_init(void)
 		 * If the timer is not marked to be bound before
 		 * relocation, bind it anyway.
 		 */
-		if (!lists_bind_fdt(dm_root(), node, &dev)) {
+		if (!lists_bind_fdt(dm_root(), node, &dev, false)) {
 			ret = device_probe(dev);
 			if (ret)
 				return ret;
diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c
index da7c812908bd02ce0a47d9646a1f5d476a513a35..ba940ebf1cf6391bba892209761ac680139b9215 100644
--- a/drivers/timer/tsc_timer.c
+++ b/drivers/timer/tsc_timer.c
@@ -424,5 +424,4 @@ U_BOOT_DRIVER(tsc_timer) = {
 	.of_match = tsc_timer_ids,
 	.probe = tsc_timer_probe,
 	.ops	= &tsc_timer_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index 3b8da808bd32bd4ac6c88475062cd6b9cec5ca66..1679d20002434326b5ffe91576e8e8de2c218f32 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -68,5 +68,4 @@ U_BOOT_DRIVER(simple_video) = {
 	.id	= UCLASS_VIDEO,
 	.of_match = simple_video_ids,
 	.probe	= simple_video_probe,
-	.flags	= DM_FLAG_PRE_RELOC,
 };
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..a9d5fd07b775db571cb2586267182054642d671c
--- /dev/null
+++ b/drivers/virtio/Kconfig
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+#
+# VirtIO is a virtualization standard for network and disk device drivers
+# where just the guest's device driver "knows" it is running in a virtual
+# environment, and cooperates with the hypervisor. This enables guests to
+# get high performance network and disk operations, and gives most of the
+# performance benefits of paravirtualization. In the U-Boot case, the guest
+# is U-Boot itself, while the virtual environment are normally QEMU targets
+# like ARM, RISC-V and x86.
+#
+# See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
+# the VirtIO specification v1.0.
+
+menu "VirtIO Drivers"
+
+config VIRTIO
+	bool
+	help
+	  This option is selected by any driver which implements the virtio
+	  transport, such as CONFIG_VIRTIO_MMIO or CONFIG_VIRTIO_PCI.
+
+config VIRTIO_MMIO
+	bool "Platform bus driver for memory mapped virtio devices"
+	select VIRTIO
+	help
+	  This driver provides support for memory mapped virtio
+	  platform device driver.
+
+config VIRTIO_PCI
+	bool "PCI driver for virtio devices"
+	depends on DM_PCI
+	select VIRTIO
+	help
+	  This driver provides support for virtio based paravirtual device
+	  drivers over PCI.
+
+config VIRTIO_SANDBOX
+	bool "Sandbox driver for virtio devices"
+	depends on SANDBOX
+	select VIRTIO
+	help
+	  This driver provides support for Sandbox implementation of virtio
+	  transport driver which is used for testing purpose only.
+
+config VIRTIO_NET
+	bool "virtio net driver"
+	depends on VIRTIO
+	help
+	  This is the virtual net driver for virtio. It can be used with
+	  QEMU based targets.
+
+config VIRTIO_BLK
+	bool "virtio block driver"
+	depends on VIRTIO
+	help
+	  This is the virtual block driver for virtio. It can be used with
+	  QEMU based targets.
+
+endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4579044ae39fd1133a53a9c3b515ef1d976af5a2
--- /dev/null
+++ b/drivers/virtio/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+
+obj-y += virtio-uclass.o virtio_ring.o
+obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio_pci_legacy.o virtio_pci_modern.o
+obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o
+obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
+obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
new file mode 100644
index 0000000000000000000000000000000000000000..34397d7dbb11f7fde56f4a224b221061cdb6eca7
--- /dev/null
+++ b/drivers/virtio/virtio-uclass.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO is a virtualization standard for network and disk device drivers
+ * where just the guest's device driver "knows" it is running in a virtual
+ * environment, and cooperates with the hypervisor. This enables guests to
+ * get high performance network and disk operations, and gives most of the
+ * performance benefits of paravirtualization. In the U-Boot case, the guest
+ * is U-Boot itself, while the virtual environment are normally QEMU targets
+ * like ARM, RISC-V and x86.
+ *
+ * See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
+ * the VirtIO specification v1.0.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <dm/lists.h>
+
+static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
+	[VIRTIO_ID_NET]		= VIRTIO_NET_DRV_NAME,
+	[VIRTIO_ID_BLOCK]	= VIRTIO_BLK_DRV_NAME,
+};
+
+int virtio_get_config(struct udevice *vdev, unsigned int offset,
+		      void *buf, unsigned int len)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->get_config(vdev->parent, offset, buf, len);
+}
+
+int virtio_set_config(struct udevice *vdev, unsigned int offset,
+		      void *buf, unsigned int len)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->set_config(vdev->parent, offset, buf, len);
+}
+
+int virtio_generation(struct udevice *vdev, u32 *counter)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+	if (!ops->generation)
+		return -ENOSYS;
+
+	return ops->generation(vdev->parent, counter);
+}
+
+int virtio_get_status(struct udevice *vdev, u8 *status)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->get_status(vdev->parent, status);
+}
+
+int virtio_set_status(struct udevice *vdev, u8 status)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->set_status(vdev->parent, status);
+}
+
+int virtio_reset(struct udevice *vdev)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->reset(vdev->parent);
+}
+
+int virtio_get_features(struct udevice *vdev, u64 *features)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->get_features(vdev->parent, features);
+}
+
+int virtio_set_features(struct udevice *vdev)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->set_features(vdev->parent);
+}
+
+int virtio_find_vqs(struct udevice *vdev, unsigned int nvqs,
+		    struct virtqueue *vqs[])
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->find_vqs(vdev->parent, nvqs, vqs);
+}
+
+int virtio_del_vqs(struct udevice *vdev)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->del_vqs(vdev->parent);
+}
+
+int virtio_notify(struct udevice *vdev, struct virtqueue *vq)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = virtio_get_ops(vdev->parent);
+
+	return ops->notify(vdev->parent, vq);
+}
+
+void virtio_add_status(struct udevice *vdev, u8 status)
+{
+	u8 old;
+
+	if (!virtio_get_status(vdev, &old))
+		virtio_set_status(vdev, old | status);
+}
+
+int virtio_finalize_features(struct udevice *vdev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
+	u8 status;
+	int ret;
+
+	ret = virtio_set_features(vdev);
+	if (ret)
+		return ret;
+
+	if (uc_priv->legacy)
+		return 0;
+
+	virtio_add_status(vdev, VIRTIO_CONFIG_S_FEATURES_OK);
+	ret = virtio_get_status(vdev, &status);
+	if (ret)
+		return ret;
+	if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
+		debug("(%s): device refuses features %x\n", vdev->name, status);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+void virtio_driver_features_init(struct virtio_dev_priv *priv,
+				 const u32 *feature,
+				 u32 feature_size,
+				 const u32 *feature_legacy,
+				 u32 feature_legacy_size)
+{
+	priv->feature_table = feature;
+	priv->feature_table_size = feature_size;
+	priv->feature_table_legacy = feature_legacy;
+	priv->feature_table_size_legacy = feature_legacy_size;
+}
+
+int virtio_init(void)
+{
+	struct udevice *bus;
+	int ret;
+
+	/* Enumerate all known virtio devices */
+	ret = uclass_first_device(UCLASS_VIRTIO, &bus);
+	if (ret)
+		return ret;
+
+	while (bus) {
+		ret = uclass_next_device(&bus);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int virtio_uclass_pre_probe(struct udevice *udev)
+{
+	struct dm_virtio_ops *ops;
+
+	ops = (struct dm_virtio_ops *)(udev->driver->ops);
+
+	/*
+	 * Check virtio transport driver ops here so that we don't need
+	 * check these ops each time when the virtio_xxx APIs are called.
+	 *
+	 * Only generation op is optional. All other ops are must-have.
+	 */
+	if (!ops->get_config || !ops->set_config ||
+	    !ops->get_status || !ops->set_status ||
+	    !ops->get_features || !ops->set_features ||
+	    !ops->find_vqs || !ops->del_vqs ||
+	    !ops->reset || !ops->notify)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int virtio_uclass_post_probe(struct udevice *udev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	char dev_name[30], *str;
+	struct udevice *vdev;
+	int ret;
+
+	if (uc_priv->device > VIRTIO_ID_MAX_NUM) {
+		debug("(%s): virtio device ID %d exceeds maximum num\n",
+		      udev->name, uc_priv->device);
+		return 0;
+	}
+
+	if (!virtio_drv_name[uc_priv->device]) {
+		debug("(%s): underlying virtio device driver unavailable\n",
+		      udev->name);
+		return 0;
+	}
+
+	snprintf(dev_name, sizeof(dev_name), "%s#%d",
+		 virtio_drv_name[uc_priv->device], udev->seq);
+	str = strdup(dev_name);
+	if (!str)
+		return -ENOMEM;
+
+	ret = device_bind_driver(udev, virtio_drv_name[uc_priv->device],
+				 str, &vdev);
+	if (ret == -ENOENT) {
+		debug("(%s): no driver configured\n", udev->name);
+		return 0;
+	}
+	if (ret) {
+		free(str);
+		return ret;
+	}
+	device_set_name_alloced(vdev);
+
+	INIT_LIST_HEAD(&uc_priv->vqs);
+
+	return 0;
+}
+
+static int virtio_uclass_child_post_bind(struct udevice *vdev)
+{
+	/* Acknowledge that we've seen the device */
+	virtio_add_status(vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
+
+	return 0;
+}
+
+static int virtio_uclass_child_pre_probe(struct udevice *vdev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
+	u64 device_features;
+	u64 driver_features;
+	u64 driver_features_legacy;
+	int i;
+	int ret;
+
+	/*
+	 * Save the real virtio device (eg: virtio-net, virtio-blk) to
+	 * the transport (parent) device's uclass priv for future use.
+	 */
+	uc_priv->vdev = vdev;
+
+	/*
+	 * We always start by resetting the device, in case a previous driver
+	 * messed it up. This also tests that code path a little.
+	 */
+	ret = virtio_reset(vdev);
+	if (ret)
+		goto err;
+
+	/* We have a driver! */
+	virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER);
+
+	/* Figure out what features the device supports */
+	virtio_get_features(vdev, &device_features);
+	debug("(%s) plain device features supported %016llx\n",
+	      vdev->name, device_features);
+	if (!(device_features & (1ULL << VIRTIO_F_VERSION_1)))
+		uc_priv->legacy = true;
+
+	/* Figure out what features the driver supports */
+	driver_features = 0;
+	for (i = 0; i < uc_priv->feature_table_size; i++) {
+		unsigned int f = uc_priv->feature_table[i];
+
+		WARN_ON(f >= 64);
+		driver_features |= (1ULL << f);
+	}
+
+	/* Some drivers have a separate feature table for virtio v1.0 */
+	if (uc_priv->feature_table_legacy) {
+		driver_features_legacy = 0;
+		for (i = 0; i < uc_priv->feature_table_size_legacy; i++) {
+			unsigned int f = uc_priv->feature_table_legacy[i];
+
+			WARN_ON(f >= 64);
+			driver_features_legacy |= (1ULL << f);
+		}
+	} else {
+		driver_features_legacy = driver_features;
+	}
+
+	if (uc_priv->legacy) {
+		debug("(%s): legacy virtio device\n", vdev->name);
+		uc_priv->features = driver_features_legacy & device_features;
+	} else {
+		debug("(%s): v1.0 complaint virtio device\n", vdev->name);
+		uc_priv->features = driver_features & device_features;
+	}
+
+	/* Transport features always preserved to pass to finalize_features */
+	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
+		if ((device_features & (1ULL << i)) &&
+		    (i == VIRTIO_F_VERSION_1))
+			__virtio_set_bit(vdev->parent, i);
+
+	debug("(%s) final negotiated features supported %016llx\n",
+	      vdev->name, uc_priv->features);
+	ret = virtio_finalize_features(vdev);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	virtio_add_status(vdev, VIRTIO_CONFIG_S_FAILED);
+	return ret;
+}
+
+static int virtio_uclass_child_post_probe(struct udevice *vdev)
+{
+	/* Indicates that the driver is set up and ready to drive the device */
+	virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER_OK);
+
+	return 0;
+}
+
+UCLASS_DRIVER(virtio) = {
+	.name	= "virtio",
+	.id	= UCLASS_VIRTIO,
+	.flags	= DM_UC_FLAG_SEQ_ALIAS,
+	.pre_probe = virtio_uclass_pre_probe,
+	.post_probe = virtio_uclass_post_probe,
+	.child_post_bind = virtio_uclass_child_post_bind,
+	.child_pre_probe = virtio_uclass_child_pre_probe,
+	.child_post_probe = virtio_uclass_child_post_probe,
+	.per_device_auto_alloc_size = sizeof(struct virtio_dev_priv),
+};
diff --git a/drivers/virtio/virtio_blk.c b/drivers/virtio/virtio_blk.c
new file mode 100644
index 0000000000000000000000000000000000000000..e793e34e836640c283ecb524d4dfe1e71b0fa50a
--- /dev/null
+++ b/drivers/virtio/virtio_blk.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include "virtio_blk.h"
+
+struct virtio_blk_priv {
+	struct virtqueue *vq;
+};
+
+static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
+			       lbaint_t blkcnt, void *buffer, u32 type)
+{
+	struct virtio_blk_priv *priv = dev_get_priv(dev);
+	unsigned int num_out = 0, num_in = 0;
+	struct virtio_sg *sgs[3];
+	u8 status;
+	int ret;
+
+	struct virtio_blk_outhdr out_hdr = {
+		.type = cpu_to_virtio32(dev, type),
+		.sector = cpu_to_virtio64(dev, sector),
+	};
+	struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) };
+	struct virtio_sg data_sg = { buffer, blkcnt * 512 };
+	struct virtio_sg status_sg = { &status, sizeof(status) };
+
+	sgs[num_out++] = &hdr_sg;
+
+	if (type & VIRTIO_BLK_T_OUT)
+		sgs[num_out++] = &data_sg;
+	else
+		sgs[num_out + num_in++] = &data_sg;
+
+	sgs[num_out + num_in++] = &status_sg;
+
+	ret = virtqueue_add(priv->vq, sgs, num_out, num_in);
+	if (ret)
+		return ret;
+
+	virtqueue_kick(priv->vq);
+
+	while (!virtqueue_get_buf(priv->vq, NULL))
+		;
+
+	return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO;
+}
+
+static ulong virtio_blk_read(struct udevice *dev, lbaint_t start,
+			     lbaint_t blkcnt, void *buffer)
+{
+	return virtio_blk_do_req(dev, start, blkcnt, buffer,
+				 VIRTIO_BLK_T_IN);
+}
+
+static ulong virtio_blk_write(struct udevice *dev, lbaint_t start,
+			      lbaint_t blkcnt, const void *buffer)
+{
+	return virtio_blk_do_req(dev, start, blkcnt, (void *)buffer,
+				 VIRTIO_BLK_T_OUT);
+}
+
+static int virtio_blk_bind(struct udevice *dev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+	struct blk_desc *desc = dev_get_uclass_platdata(dev);
+	int devnum;
+
+	desc->if_type = IF_TYPE_VIRTIO;
+	/*
+	 * Initialize the devnum to -ENODEV. This is to make sure that
+	 * blk_next_free_devnum() works as expected, since the default
+	 * value 0 is a valid devnum.
+	 */
+	desc->devnum = -ENODEV;
+	devnum = blk_next_free_devnum(IF_TYPE_VIRTIO);
+	if (devnum < 0)
+		return devnum;
+	desc->devnum = devnum;
+	desc->part_type = PART_TYPE_UNKNOWN;
+	/*
+	 * virtio mmio transport supplies string identification for us,
+	 * while pci trnasport uses a 2-byte subvendor value.
+	 */
+	if (uc_priv->vendor >> 16)
+		sprintf(desc->vendor, "%s", (char *)&uc_priv->vendor);
+	else
+		sprintf(desc->vendor, "%04x", uc_priv->vendor);
+	desc->bdev = dev;
+
+	/* Indicate what driver features we support */
+	virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
+
+	return 0;
+}
+
+static int virtio_blk_probe(struct udevice *dev)
+{
+	struct virtio_blk_priv *priv = dev_get_priv(dev);
+	struct blk_desc *desc = dev_get_uclass_platdata(dev);
+	u64 cap;
+	int ret;
+
+	ret = virtio_find_vqs(dev, 1, &priv->vq);
+	if (ret)
+		return ret;
+
+	desc->blksz = 512;
+	virtio_cread(dev, struct virtio_blk_config, capacity, &cap);
+	desc->lba = cap;
+
+	return 0;
+}
+
+static const struct blk_ops virtio_blk_ops = {
+	.read	= virtio_blk_read,
+	.write	= virtio_blk_write,
+};
+
+U_BOOT_DRIVER(virtio_blk) = {
+	.name	= VIRTIO_BLK_DRV_NAME,
+	.id	= UCLASS_BLK,
+	.ops	= &virtio_blk_ops,
+	.bind	= virtio_blk_bind,
+	.probe	= virtio_blk_probe,
+	.remove	= virtio_reset,
+	.priv_auto_alloc_size = sizeof(struct virtio_blk_priv),
+	.flags	= DM_FLAG_ACTIVE_DMA,
+};
diff --git a/drivers/virtio/virtio_blk.h b/drivers/virtio/virtio_blk.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d8e02fa2eab9aadbd1d3dd2ee39e3e4279541a4
--- /dev/null
+++ b/drivers/virtio/virtio_blk.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_blk.h
+ */
+
+#ifndef _LINUX_VIRTIO_BLK_H
+#define _LINUX_VIRTIO_BLK_H
+
+/* Feature bits */
+#define VIRTIO_BLK_F_SIZE_MAX	1	/* Indicates maximum segment size */
+#define VIRTIO_BLK_F_SEG_MAX	2	/* Indicates maximum # of segments */
+#define VIRTIO_BLK_F_GEOMETRY	4	/* Legacy geometry available */
+#define VIRTIO_BLK_F_RO		5	/* Disk is read-only */
+#define VIRTIO_BLK_F_BLK_SIZE	6	/* Block size of disk is available */
+#define VIRTIO_BLK_F_TOPOLOGY	10	/* Topology information is available */
+#define VIRTIO_BLK_F_MQ		12	/* Support more than one vq */
+
+/* Legacy feature bits */
+#ifndef VIRTIO_BLK_NO_LEGACY
+#define VIRTIO_BLK_F_BARRIER	0	/* Does host support barriers? */
+#define VIRTIO_BLK_F_SCSI	7	/* Supports scsi command passthru */
+#define VIRTIO_BLK_F_FLUSH	9	/* Flush command supported */
+#define VIRTIO_BLK_F_CONFIG_WCE	11	/* Writeback mode available in config */
+#ifndef __KERNEL__
+/* Old (deprecated) name for VIRTIO_BLK_F_FLUSH */
+#define VIRTIO_BLK_F_WCE	VIRTIO_BLK_F_FLUSH
+#endif
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+#define VIRTIO_BLK_ID_BYTES	20	/* ID string length */
+
+struct __packed virtio_blk_config {
+	/* The capacity (in 512-byte sectors) */
+	__u64 capacity;
+	/* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
+	__u32 size_max;
+	/* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
+	__u32 seg_max;
+	/* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
+	struct virtio_blk_geometry {
+		__u16 cylinders;
+		__u8 heads;
+		__u8 sectors;
+	} geometry;
+
+	/* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+	__u32 blk_size;
+
+	/* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */
+	/* exponent for physical block per logical block */
+	__u8 physical_block_exp;
+	/* alignment offset in logical blocks */
+	__u8 alignment_offset;
+	/* minimum I/O size without performance penalty in logical blocks */
+	__u16 min_io_size;
+	/* optimal sustained I/O size in logical blocks */
+	__u32 opt_io_size;
+
+	/* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
+	__u8 wce;
+	__u8 unused;
+
+	/* number of vqs, only available when VIRTIO_BLK_F_MQ is set */
+	__u16 num_queues;
+};
+
+/*
+ * Command types
+ *
+ * Usage is a bit tricky as some bits are used as flags and some are not.
+ *
+ * Rules:
+ *   VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or
+ *   VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own
+ *   and may not be combined with any of the other flags.
+ */
+
+/* These two define direction */
+#define VIRTIO_BLK_T_IN		0
+#define VIRTIO_BLK_T_OUT	1
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+/* This bit says it's a scsi command, not an actual read or write */
+#define VIRTIO_BLK_T_SCSI_CMD	2
+#endif /* VIRTIO_BLK_NO_LEGACY */
+
+/* Cache flush command */
+#define VIRTIO_BLK_T_FLUSH	4
+
+/* Get device ID command */
+#define VIRTIO_BLK_T_GET_ID	8
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+/* Barrier before this op */
+#define VIRTIO_BLK_T_BARRIER	0x80000000
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+/*
+ * This comes first in the read scatter-gather list.
+ * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated,
+ * this is the first element of the read scatter-gather list.
+ */
+struct virtio_blk_outhdr {
+	/* VIRTIO_BLK_T* */
+	__virtio32 type;
+	/* io priority */
+	__virtio32 ioprio;
+	/* Sector (ie. 512 byte offset) */
+	__virtio64 sector;
+};
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+struct virtio_scsi_inhdr {
+	__virtio32 errors;
+	__virtio32 data_len;
+	__virtio32 sense_len;
+	__virtio32 residual;
+};
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+/* And this is the final byte of the write scatter-gather list */
+#define VIRTIO_BLK_S_OK		0
+#define VIRTIO_BLK_S_IOERR	1
+#define VIRTIO_BLK_S_UNSUPP	2
+
+#endif /* _LINUX_VIRTIO_BLK_H */
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b738703b827b10a09bdd72afa23b9605837b369
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO memory-maped I/O transport driver
+ * Ported from Linux drivers/virtio/virtio_mmio.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include "virtio_mmio.h"
+
+static int virtio_mmio_get_config(struct udevice *udev, unsigned int offset,
+				  void *buf, unsigned int len)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+	void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
+	u8 b;
+	__le16 w;
+	__le32 l;
+
+	if (priv->version == 1) {
+		u8 *ptr = buf;
+		int i;
+
+		for (i = 0; i < len; i++)
+			ptr[i] = readb(base + offset + i);
+
+		return 0;
+	}
+
+	switch (len) {
+	case 1:
+		b = readb(base + offset);
+		memcpy(buf, &b, sizeof(b));
+		break;
+	case 2:
+		w = cpu_to_le16(readw(base + offset));
+		memcpy(buf, &w, sizeof(w));
+		break;
+	case 4:
+		l = cpu_to_le32(readl(base + offset));
+		memcpy(buf, &l, sizeof(l));
+		break;
+	case 8:
+		l = cpu_to_le32(readl(base + offset));
+		memcpy(buf, &l, sizeof(l));
+		l = cpu_to_le32(readl(base + offset + sizeof(l)));
+		memcpy(buf + sizeof(l), &l, sizeof(l));
+		break;
+	default:
+		WARN_ON(true);
+	}
+
+	return 0;
+}
+
+static int virtio_mmio_set_config(struct udevice *udev, unsigned int offset,
+				  const void *buf, unsigned int len)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+	void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
+	u8 b;
+	__le16 w;
+	__le32 l;
+
+	if (priv->version == 1) {
+		const u8 *ptr = buf;
+		int i;
+
+		for (i = 0; i < len; i++)
+			writeb(ptr[i], base + offset + i);
+
+		return 0;
+	}
+
+	switch (len) {
+	case 1:
+		memcpy(&b, buf, sizeof(b));
+		writeb(b, base + offset);
+		break;
+	case 2:
+		memcpy(&w, buf, sizeof(w));
+		writew(le16_to_cpu(w), base + offset);
+		break;
+	case 4:
+		memcpy(&l, buf, sizeof(l));
+		writel(le32_to_cpu(l), base + offset);
+		break;
+	case 8:
+		memcpy(&l, buf, sizeof(l));
+		writel(le32_to_cpu(l), base + offset);
+		memcpy(&l, buf + sizeof(l), sizeof(l));
+		writel(le32_to_cpu(l), base + offset + sizeof(l));
+		break;
+	default:
+		WARN_ON(true);
+	}
+
+	return 0;
+}
+
+static int virtio_mmio_generation(struct udevice *udev, u32 *counter)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	if (priv->version == 1)
+		*counter = 0;
+	else
+		*counter = readl(priv->base + VIRTIO_MMIO_CONFIG_GENERATION);
+
+	return 0;
+}
+
+static int virtio_mmio_get_status(struct udevice *udev, u8 *status)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	*status = readl(priv->base + VIRTIO_MMIO_STATUS) & 0xff;
+
+	return 0;
+}
+
+static int virtio_mmio_set_status(struct udevice *udev, u8 status)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	/* We should never be setting status to 0 */
+	WARN_ON(status == 0);
+
+	writel(status, priv->base + VIRTIO_MMIO_STATUS);
+
+	return 0;
+}
+
+static int virtio_mmio_reset(struct udevice *udev)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	/* 0 status means a reset */
+	writel(0, priv->base + VIRTIO_MMIO_STATUS);
+
+	return 0;
+}
+
+static int virtio_mmio_get_features(struct udevice *udev, u64 *features)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	writel(1, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
+	*features = readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
+	*features <<= 32;
+
+	writel(0, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
+	*features |= readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
+
+	return 0;
+}
+
+static int virtio_mmio_set_features(struct udevice *udev)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* Make sure there is are no mixed devices */
+	if (priv->version == 2 && uc_priv->legacy) {
+		debug("New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n");
+		return -EINVAL;
+	}
+
+	writel(1, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
+	writel((u32)(uc_priv->features >> 32),
+	       priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
+
+	writel(0, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
+	writel((u32)uc_priv->features,
+	       priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
+
+	return 0;
+}
+
+static struct virtqueue *virtio_mmio_setup_vq(struct udevice *udev,
+					      unsigned int index)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+	struct virtqueue *vq;
+	unsigned int num;
+	int err;
+
+	/* Select the queue we're interested in */
+	writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
+
+	/* Queue shouldn't already be set up */
+	if (readl(priv->base + (priv->version == 1 ?
+	    VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) {
+		err = -ENOENT;
+		goto error_available;
+	}
+
+	num = readl(priv->base + VIRTIO_MMIO_QUEUE_NUM_MAX);
+	if (num == 0) {
+		err = -ENOENT;
+		goto error_new_virtqueue;
+	}
+
+	/* Create the vring */
+	vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, udev);
+	if (!vq) {
+		err = -ENOMEM;
+		goto error_new_virtqueue;
+	}
+
+	/* Activate the queue */
+	writel(virtqueue_get_vring_size(vq),
+	       priv->base + VIRTIO_MMIO_QUEUE_NUM);
+	if (priv->version == 1) {
+		u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT;
+
+		/*
+		 * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something
+		 * that doesn't fit in 32bit, fail the setup rather than
+		 * pretending to be successful.
+		 */
+		if (q_pfn >> 32) {
+			debug("platform bug: legacy virtio-mmio must not be used with RAM above 0x%llxGB\n",
+			      0x1ULL << (32 + PAGE_SHIFT - 30));
+			err = -E2BIG;
+			goto error_bad_pfn;
+		}
+
+		writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_QUEUE_ALIGN);
+		writel(q_pfn, priv->base + VIRTIO_MMIO_QUEUE_PFN);
+	} else {
+		u64 addr;
+
+		addr = virtqueue_get_desc_addr(vq);
+		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
+		writel((u32)(addr >> 32),
+		       priv->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
+
+		addr = virtqueue_get_avail_addr(vq);
+		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW);
+		writel((u32)(addr >> 32),
+		       priv->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH);
+
+		addr = virtqueue_get_used_addr(vq);
+		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_USED_LOW);
+		writel((u32)(addr >> 32),
+		       priv->base + VIRTIO_MMIO_QUEUE_USED_HIGH);
+
+		writel(1, priv->base + VIRTIO_MMIO_QUEUE_READY);
+	}
+
+	return vq;
+
+error_bad_pfn:
+	vring_del_virtqueue(vq);
+
+error_new_virtqueue:
+	if (priv->version == 1) {
+		writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
+	} else {
+		writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
+		WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
+	}
+
+error_available:
+	return ERR_PTR(err);
+}
+
+static void virtio_mmio_del_vq(struct virtqueue *vq)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(vq->vdev);
+	unsigned int index = vq->index;
+
+	/* Select and deactivate the queue */
+	writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
+	if (priv->version == 1) {
+		writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
+	} else {
+		writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
+		WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
+	}
+
+	vring_del_virtqueue(vq);
+}
+
+static int virtio_mmio_del_vqs(struct udevice *udev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
+		virtio_mmio_del_vq(vq);
+
+	return 0;
+}
+
+static int virtio_mmio_find_vqs(struct udevice *udev, unsigned int nvqs,
+				struct virtqueue *vqs[])
+{
+	int i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = virtio_mmio_setup_vq(udev, i);
+		if (IS_ERR(vqs[i])) {
+			virtio_mmio_del_vqs(udev);
+			return PTR_ERR(vqs[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int virtio_mmio_notify(struct udevice *udev, struct virtqueue *vq)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	/*
+	 * We write the queue's selector into the notification register
+	 * to signal the other end
+	 */
+	writel(vq->index, priv->base + VIRTIO_MMIO_QUEUE_NOTIFY);
+
+	return 0;
+}
+
+static int virtio_mmio_ofdata_to_platdata(struct udevice *udev)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+
+	priv->base = (void __iomem *)(ulong)dev_read_addr(udev);
+	if (priv->base == (void __iomem *)FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int virtio_mmio_probe(struct udevice *udev)
+{
+	struct virtio_mmio_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	u32 magic;
+
+	/* Check magic value */
+	magic = readl(priv->base + VIRTIO_MMIO_MAGIC_VALUE);
+	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
+		debug("(%s): wrong magic value 0x%08x!\n", udev->name, magic);
+		return 0;
+	}
+
+	/* Check device version */
+	priv->version = readl(priv->base + VIRTIO_MMIO_VERSION);
+	if (priv->version < 1 || priv->version > 2) {
+		debug("(%s): version %d not supported!\n",
+		      udev->name, priv->version);
+		return 0;
+	}
+
+	/* Check devicd ID */
+	uc_priv->device = readl(priv->base + VIRTIO_MMIO_DEVICE_ID);
+	if (uc_priv->device == 0) {
+		/*
+		 * virtio-mmio device with an ID 0 is a (dummy) placeholder
+		 * with no function. End probing now with no error reported.
+		 */
+		return 0;
+	}
+	uc_priv->vendor = readl(priv->base + VIRTIO_MMIO_VENDOR_ID);
+
+	if (priv->version == 1)
+		writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+
+	debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name,
+	      uc_priv->device, uc_priv->vendor, priv->version);
+
+	return 0;
+}
+
+static const struct dm_virtio_ops virtio_mmio_ops = {
+	.get_config	= virtio_mmio_get_config,
+	.set_config	= virtio_mmio_set_config,
+	.generation	= virtio_mmio_generation,
+	.get_status	= virtio_mmio_get_status,
+	.set_status	= virtio_mmio_set_status,
+	.reset		= virtio_mmio_reset,
+	.get_features	= virtio_mmio_get_features,
+	.set_features	= virtio_mmio_set_features,
+	.find_vqs	= virtio_mmio_find_vqs,
+	.del_vqs	= virtio_mmio_del_vqs,
+	.notify		= virtio_mmio_notify,
+};
+
+static const struct udevice_id virtio_mmio_ids[] = {
+	{ .compatible = "virtio,mmio" },
+	{ }
+};
+
+U_BOOT_DRIVER(virtio_mmio) = {
+	.name	= "virtio-mmio",
+	.id	= UCLASS_VIRTIO,
+	.of_match = virtio_mmio_ids,
+	.ops	= &virtio_mmio_ops,
+	.probe	= virtio_mmio_probe,
+	.ofdata_to_platdata = virtio_mmio_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct virtio_mmio_priv),
+};
diff --git a/drivers/virtio/virtio_mmio.h b/drivers/virtio/virtio_mmio.h
new file mode 100644
index 0000000000000000000000000000000000000000..b3408828a5926d07d8063e035764c30fdbc348ae
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_mmio.h
+ */
+
+#ifndef _LINUX_VIRTIO_MMIO_H
+#define _LINUX_VIRTIO_MMIO_H
+
+/* Control registers */
+
+/* Magic value ("virt" string) - Read Only */
+#define VIRTIO_MMIO_MAGIC_VALUE		0x000
+
+/* Virtio device version - Read Only */
+#define VIRTIO_MMIO_VERSION		0x004
+
+/* Virtio device ID - Read Only */
+#define VIRTIO_MMIO_DEVICE_ID		0x008
+
+/* Virtio vendor ID - Read Only */
+#define VIRTIO_MMIO_VENDOR_ID		0x00c
+
+/*
+ * Bitmask of the features supported by the device (host)
+ * (32 bits per set) - Read Only
+ */
+#define VIRTIO_MMIO_DEVICE_FEATURES	0x010
+
+/* Device (host) features set selector - Write Only */
+#define VIRTIO_MMIO_DEVICE_FEATURES_SEL	0x014
+
+/*
+ * Bitmask of features activated by the driver (guest)
+ * (32 bits per set) - Write Only
+ */
+#define VIRTIO_MMIO_DRIVER_FEATURES	0x020
+
+/* Activated features set selector - Write Only */
+#define VIRTIO_MMIO_DRIVER_FEATURES_SEL	0x024
+
+#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */
+
+/* Guest's memory page size in bytes - Write Only */
+#define VIRTIO_MMIO_GUEST_PAGE_SIZE	0x028
+
+#endif
+
+/* Queue selector - Write Only */
+#define VIRTIO_MMIO_QUEUE_SEL		0x030
+
+/* Maximum size of the currently selected queue - Read Only */
+#define VIRTIO_MMIO_QUEUE_NUM_MAX	0x034
+
+/* Queue size for the currently selected queue - Write Only */
+#define VIRTIO_MMIO_QUEUE_NUM		0x038
+
+#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */
+
+/* Used Ring alignment for the currently selected queue - Write Only */
+#define VIRTIO_MMIO_QUEUE_ALIGN		0x03c
+
+/* Guest's PFN for the currently selected queue - Read Write */
+#define VIRTIO_MMIO_QUEUE_PFN		0x040
+
+#endif
+
+/* Ready bit for the currently selected queue - Read Write */
+#define VIRTIO_MMIO_QUEUE_READY		0x044
+
+/* Queue notifier - Write Only */
+#define VIRTIO_MMIO_QUEUE_NOTIFY	0x050
+
+/* Interrupt status - Read Only */
+#define VIRTIO_MMIO_INTERRUPT_STATUS	0x060
+
+/* Interrupt acknowledge - Write Only */
+#define VIRTIO_MMIO_INTERRUPT_ACK	0x064
+
+/* Device status register - Read Write */
+#define VIRTIO_MMIO_STATUS		0x070
+
+/* Selected queue's Descriptor Table address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_DESC_LOW	0x080
+#define VIRTIO_MMIO_QUEUE_DESC_HIGH	0x084
+
+/* Selected queue's Available Ring address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_AVAIL_LOW	0x090
+#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH	0x094
+
+/* Selected queue's Used Ring address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_USED_LOW	0x0a0
+#define VIRTIO_MMIO_QUEUE_USED_HIGH	0x0a4
+
+/* Configuration atomicity value */
+#define VIRTIO_MMIO_CONFIG_GENERATION	0x0fc
+
+/*
+ * The config space is defined by each driver as
+ * the per-driver configuration space - Read Write
+ */
+#define VIRTIO_MMIO_CONFIG		0x100
+
+/* Interrupt flags (re: interrupt status & acknowledge registers) */
+
+#define VIRTIO_MMIO_INT_VRING		BIT(0)
+#define VIRTIO_MMIO_INT_CONFIG		BIT(1)
+
+/*
+ * The alignment to use between consumer and producer parts of vring.
+ * Currently hardcoded to the page size.
+ */
+#define PAGE_SHIFT			12
+#define VIRTIO_MMIO_VRING_ALIGN		PAGE_SIZE
+
+/**
+ * virtio mmio transport driver private data
+ *
+ * @base:		mmio transport device register base
+ * @version:		mmio transport device version
+ */
+struct virtio_mmio_priv {
+	void __iomem *base;
+	u32 version;
+};
+
+#endif /* _LINUX_VIRTIO_MMIO_H */
diff --git a/drivers/virtio/virtio_net.c b/drivers/virtio/virtio_net.c
new file mode 100644
index 0000000000000000000000000000000000000000..0dbbd7802308ad369febdcb86be2e5ea52a7c4eb
--- /dev/null
+++ b/drivers/virtio/virtio_net.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <net.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include "virtio_net.h"
+
+/* Amount of buffers to keep in the RX virtqueue */
+#define VIRTIO_NET_NUM_RX_BUFS	32
+
+/*
+ * This value comes from the VirtIO spec: 1500 for maximum packet size,
+ * 14 for the Ethernet header, 12 for virtio_net_hdr. In total 1526 bytes.
+ */
+#define VIRTIO_NET_RX_BUF_SIZE	1526
+
+struct virtio_net_priv {
+	union {
+		struct virtqueue *vqs[2];
+		struct {
+			struct virtqueue *rx_vq;
+			struct virtqueue *tx_vq;
+		};
+	};
+
+	char rx_buff[VIRTIO_NET_NUM_RX_BUFS][VIRTIO_NET_RX_BUF_SIZE];
+	bool rx_running;
+	int net_hdr_len;
+};
+
+/*
+ * For simplicity, the driver only negotiates the VIRTIO_NET_F_MAC feature.
+ * For the VIRTIO_NET_F_STATUS feature, we don't negotiate it, hence per spec
+ * we should assume the link is always active.
+ */
+static const u32 feature[] = {
+	VIRTIO_NET_F_MAC
+};
+
+static const u32 feature_legacy[] = {
+	VIRTIO_NET_F_MAC
+};
+
+static int virtio_net_start(struct udevice *dev)
+{
+	struct virtio_net_priv *priv = dev_get_priv(dev);
+	struct virtio_sg sg;
+	struct virtio_sg *sgs[] = { &sg };
+	int i;
+
+	if (!priv->rx_running) {
+		/* receive buffer length is always 1526 */
+		sg.length = VIRTIO_NET_RX_BUF_SIZE;
+
+		/* setup the receive buffer address */
+		for (i = 0; i < VIRTIO_NET_NUM_RX_BUFS; i++) {
+			sg.addr = priv->rx_buff[i];
+			virtqueue_add(priv->rx_vq, sgs, 0, 1);
+		}
+
+		virtqueue_kick(priv->rx_vq);
+
+		/* setup the receive queue only once */
+		priv->rx_running = true;
+	}
+
+	return 0;
+}
+
+static int virtio_net_send(struct udevice *dev, void *packet, int length)
+{
+	struct virtio_net_priv *priv = dev_get_priv(dev);
+	struct virtio_net_hdr hdr;
+	struct virtio_net_hdr_v1 hdr_v1;
+	struct virtio_sg hdr_sg;
+	struct virtio_sg data_sg = { packet, length };
+	struct virtio_sg *sgs[] = { &hdr_sg, &data_sg };
+	int ret;
+
+	if (priv->net_hdr_len == sizeof(struct virtio_net_hdr))
+		hdr_sg.addr = &hdr;
+	else
+		hdr_sg.addr = &hdr_v1;
+	hdr_sg.length = priv->net_hdr_len;
+
+	memset(hdr_sg.addr, 0, priv->net_hdr_len);
+
+	ret = virtqueue_add(priv->tx_vq, sgs, 2, 0);
+	if (ret)
+		return ret;
+
+	virtqueue_kick(priv->tx_vq);
+
+	while (1) {
+		if (virtqueue_get_buf(priv->tx_vq, NULL))
+			break;
+	}
+
+	return 0;
+}
+
+static int virtio_net_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct virtio_net_priv *priv = dev_get_priv(dev);
+	unsigned int len;
+	void *buf;
+
+	buf = virtqueue_get_buf(priv->rx_vq, &len);
+	if (!buf)
+		return -EAGAIN;
+
+	*packetp = buf + priv->net_hdr_len;
+	return len - priv->net_hdr_len;
+}
+
+static int virtio_net_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+	struct virtio_net_priv *priv = dev_get_priv(dev);
+	void *buf = packet - priv->net_hdr_len;
+	struct virtio_sg sg = { buf, VIRTIO_NET_RX_BUF_SIZE };
+	struct virtio_sg *sgs[] = { &sg };
+
+	/* Put the buffer back to the rx ring */
+	virtqueue_add(priv->rx_vq, sgs, 0, 1);
+
+	return 0;
+}
+
+static void virtio_net_stop(struct udevice *dev)
+{
+	/*
+	 * There is no way to stop the queue from running, unless we issue
+	 * a reset to the virtio device, and re-do the queue initialization
+	 * from the beginning.
+	 */
+}
+
+static int virtio_net_write_hwaddr(struct udevice *dev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	int i;
+
+	/*
+	 * v1.0 compliant device's MAC address is set through control channel,
+	 * which we don't support for now.
+	 */
+	if (!uc_priv->legacy)
+		return -ENOSYS;
+
+	for (i = 0; i < sizeof(pdata->enetaddr); i++) {
+		virtio_cwrite8(dev,
+			       offsetof(struct virtio_net_config, mac) + i,
+			       pdata->enetaddr[i]);
+	}
+
+	return 0;
+}
+
+static int virtio_net_read_rom_hwaddr(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+
+	if (!pdata)
+		return -ENOSYS;
+
+	if (virtio_has_feature(dev, VIRTIO_NET_F_MAC)) {
+		virtio_cread_bytes(dev,
+				   offsetof(struct virtio_net_config, mac),
+				   pdata->enetaddr, sizeof(pdata->enetaddr));
+	}
+
+	return 0;
+}
+
+static int virtio_net_bind(struct udevice *dev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+
+	/* Indicate what driver features we support */
+	virtio_driver_features_init(uc_priv, feature, ARRAY_SIZE(feature),
+				    feature_legacy, ARRAY_SIZE(feature_legacy));
+
+	return 0;
+}
+
+static int virtio_net_probe(struct udevice *dev)
+{
+	struct virtio_net_priv *priv = dev_get_priv(dev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+	int ret;
+
+	ret = virtio_find_vqs(dev, 2, priv->vqs);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * For v1.0 compliant device, it always assumes the member
+	 * 'num_buffers' exists in the struct virtio_net_hdr while
+	 * the legacy driver only presented 'num_buffers' when
+	 * VIRTIO_NET_F_MRG_RXBUF was negotiated. Without that feature
+	 * the structure was 2 bytes shorter.
+	 */
+	if (uc_priv->legacy)
+		priv->net_hdr_len = sizeof(struct virtio_net_hdr);
+	else
+		priv->net_hdr_len = sizeof(struct virtio_net_hdr_v1);
+
+	return 0;
+}
+
+static const struct eth_ops virtio_net_ops = {
+	.start = virtio_net_start,
+	.send = virtio_net_send,
+	.recv = virtio_net_recv,
+	.free_pkt = virtio_net_free_pkt,
+	.stop = virtio_net_stop,
+	.write_hwaddr = virtio_net_write_hwaddr,
+	.read_rom_hwaddr = virtio_net_read_rom_hwaddr,
+};
+
+U_BOOT_DRIVER(virtio_net) = {
+	.name	= VIRTIO_NET_DRV_NAME,
+	.id	= UCLASS_ETH,
+	.bind	= virtio_net_bind,
+	.probe	= virtio_net_probe,
+	.remove = virtio_reset,
+	.ops	= &virtio_net_ops,
+	.priv_auto_alloc_size = sizeof(struct virtio_net_priv),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+	.flags	= DM_FLAG_ACTIVE_DMA,
+};
diff --git a/drivers/virtio/virtio_net.h b/drivers/virtio/virtio_net.h
new file mode 100644
index 0000000000000000000000000000000000000000..c92bae526906e2e80e600feaba9dfac6d8e6ee70
--- /dev/null
+++ b/drivers/virtio/virtio_net.h
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_net.h
+ */
+
+#ifndef _LINUX_VIRTIO_NET_H
+#define _LINUX_VIRTIO_NET_H
+
+/* TODO: needs to be removed! */
+#define ETH_ALEN				6
+
+/* The feature bitmap for virtio net */
+
+/* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_F_CSUM			0
+/* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_F_GUEST_CSUM			1
+/* Dynamic offload configuration */
+#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS	2
+/* Initial MTU advice */
+#define VIRTIO_NET_F_MTU			3
+/* Host has given MAC address */
+#define VIRTIO_NET_F_MAC			5
+/* Guest can handle TSOv4 in */
+#define VIRTIO_NET_F_GUEST_TSO4			7
+/* Guest can handle TSOv6 in */
+#define VIRTIO_NET_F_GUEST_TSO6			8
+/* Guest can handle TSO[6] w/ ECN in */
+#define VIRTIO_NET_F_GUEST_ECN			9
+/* Guest can handle UFO in */
+#define VIRTIO_NET_F_GUEST_UFO			10
+/* Host can handle TSOv4 in */
+#define VIRTIO_NET_F_HOST_TSO4			11
+/* Host can handle TSOv6 in */
+#define VIRTIO_NET_F_HOST_TSO6			12
+/* Host can handle TSO[6] w/ ECN in */
+#define VIRTIO_NET_F_HOST_ECN			13
+/* Host can handle UFO in */
+#define VIRTIO_NET_F_HOST_UFO			14
+/* Host can merge receive buffers */
+#define VIRTIO_NET_F_MRG_RXBUF			15
+/* virtio_net_config.status available */
+#define VIRTIO_NET_F_STATUS			16
+/* Control channel available */
+#define VIRTIO_NET_F_CTRL_VQ			17
+/* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_RX			18
+/* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_VLAN			19
+/* Extra RX mode control support */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA		20
+/* Guest can announce device on the network */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE		21
+/* Device supports receive flow steering */
+#define VIRTIO_NET_F_MQ				22
+/* Set MAC address */
+#define VIRTIO_NET_F_CTRL_MAC_ADDR		23
+/* Device set linkspeed and duplex */
+#define VIRTIO_NET_F_SPEED_DUPLEX		63
+
+#ifndef VIRTIO_NET_NO_LEGACY
+/* Host handles pkts w/ any GSO type */
+#define VIRTIO_NET_F_GSO			6
+#endif /* VIRTIO_NET_NO_LEGACY */
+
+#define VIRTIO_NET_S_LINK_UP			1 /* Link is up */
+#define VIRTIO_NET_S_ANNOUNCE			2 /* Announcement is needed */
+
+struct __packed virtio_net_config {
+	/* The config defining mac address (if VIRTIO_NET_F_MAC) */
+	__u8 mac[ETH_ALEN];
+	/* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
+	__u16 status;
+	/*
+	 * Maximum number of each of transmit and receive queues;
+	 * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ.
+	 * Legal values are between 1 and 0x8000
+	 */
+	__u16 max_virtqueue_pairs;
+	/* Default maximum transmit unit advice */
+	__u16 mtu;
+	/*
+	 * speed, in units of 1Mb. All values 0 to INT_MAX are legal.
+	 * Any other value stands for unknown.
+	 */
+	__u32 speed;
+	/*
+	 * 0x00 - half duplex
+	 * 0x01 - full duplex
+	 * Any other value stands for unknown.
+	 */
+	__u8 duplex;
+};
+
+/*
+ * This header comes first in the scatter-gather list. If you don't
+ * specify GSO or CSUM features, you can simply ignore the header.
+ *
+ * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf,
+ * only flattened.
+ */
+struct virtio_net_hdr_v1 {
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM	0x01 /* Use csum_start, csum_offset */
+#define VIRTIO_NET_HDR_F_DATA_VALID	0x02 /* Csum is valid */
+	__u8 flags;
+#define VIRTIO_NET_HDR_GSO_NONE		0x00 /* Not a GSO frame */
+#define VIRTIO_NET_HDR_GSO_TCPV4	0x01 /* GSO frame, IPv4 TCP (TSO) */
+#define VIRTIO_NET_HDR_GSO_UDP		0x03 /* GSO frame, IPv4 UDP (UFO) */
+#define VIRTIO_NET_HDR_GSO_TCPV6	0x04 /* GSO frame, IPv6 TCP */
+#define VIRTIO_NET_HDR_GSO_ECN		0x80 /* TCP has ECN set */
+	__u8 gso_type;
+	__virtio16 hdr_len;	/* Ethernet + IP + tcp/udp hdrs */
+	__virtio16 gso_size;	/* Bytes to append to hdr_len per frame */
+	__virtio16 csum_start;	/* Position to start checksumming from */
+	__virtio16 csum_offset;	/* Offset after that to place checksum */
+	__virtio16 num_buffers;	/* Number of merged rx buffers */
+};
+
+#ifndef VIRTIO_NET_NO_LEGACY
+/*
+ * This header comes first in the scatter-gather list.
+ *
+ * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
+ * be the first element of the scatter-gather list. If you don't
+ * specify GSO or CSUM features, you can simply ignore the header.
+ */
+struct virtio_net_hdr {
+	/* See VIRTIO_NET_HDR_F_* */
+	__u8 flags;
+	/* See VIRTIO_NET_HDR_GSO_* */
+	__u8 gso_type;
+	__virtio16 hdr_len;	/* Ethernet + IP + tcp/udp hdrs */
+	__virtio16 gso_size;	/* Bytes to append to hdr_len per frame */
+	__virtio16 csum_start;	/* Position to start checksumming from */
+	__virtio16 csum_offset;	/* Offset after that to place checksum */
+};
+
+/*
+ * This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated.
+ */
+struct virtio_net_hdr_mrg_rxbuf {
+	struct virtio_net_hdr hdr;
+	__virtio16 num_buffers;	/* Number of merged rx buffers */
+};
+#endif /* ...VIRTIO_NET_NO_LEGACY */
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry.  Data for the
+ * command goes in between.
+ */
+struct __packed virtio_net_ctrl_hdr {
+	__u8 class;
+	__u8 cmd;
+};
+
+typedef __u8 virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK				0
+#define VIRTIO_NET_ERR				1
+
+/*
+ * Control the RX mode, ie. promisucous, allmulti, etc...
+ *
+ * All commands require an "out" sg entry containing a 1 byte state value,
+ * zero = disable, non-zero = enable.
+ *
+ * Commands 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX			0
+#define VIRTIO_NET_CTRL_RX_PROMISC		0
+#define VIRTIO_NET_CTRL_RX_ALLMULTI		1
+#define VIRTIO_NET_CTRL_RX_ALLUNI		2
+#define VIRTIO_NET_CTRL_RX_NOMULTI		3
+#define VIRTIO_NET_CTRL_RX_NOUNI		4
+#define VIRTIO_NET_CTRL_RX_NOBCAST		5
+
+/*
+ * Control the MAC
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should assume
+ * the size is infinite. Filtering should be considered non-perfect, ie. based
+ * on hypervisor resources, the guest may received packets from sources not
+ * specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires two
+ * out scatterlists. Each contains a 4 byte count of entries followed by a
+ * concatenated byte stream of the ETH_ALEN MAC addresses.  The first sg list
+ * contains unicast addresses, the second is for multicast. This functionality
+ * is present if the VIRTIO_NET_F_CTRL_RX feature is available.
+ *
+ * The ADDR_SET command requests one out scatterlist, it contains a 6 bytes MAC
+ * address. This functionality is present if the VIRTIO_NET_F_CTRL_MAC_ADDR
+ * feature is available.
+ */
+struct __packed virtio_net_ctrl_mac {
+	__virtio32 entries;
+	__u8 macs[][ETH_ALEN];
+};
+
+#define VIRTIO_NET_CTRL_MAC			1
+#define VIRTIO_NET_CTRL_MAC_TABLE_SET		0
+#define VIRTIO_NET_CTRL_MAC_ADDR_SET		1
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface. VLAN IDs
+ * not added may be filterd by the hypervisor. Del is the opposite of add. Both
+ * commands expect an out entry containing a 2 byte VLAN ID. VLAN filterting is
+ * available with the VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN			2
+#define VIRTIO_NET_CTRL_VLAN_ADD		0
+#define VIRTIO_NET_CTRL_VLAN_DEL		1
+
+/*
+ * Control link announce acknowledgment
+ *
+ * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that driver has
+ * recevied the notification; device would clear the VIRTIO_NET_S_ANNOUNCE bit
+ * in the status field after it receives this command.
+ */
+#define VIRTIO_NET_CTRL_ANNOUNCE		3
+#define VIRTIO_NET_CTRL_ANNOUNCE_ACK		0
+
+/*
+ * Control receive flow steering
+ *
+ * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET enables receive flow steering,
+ * specifying the number of the transmit and receive queues that will be used.
+ * After the command is consumed and acked by the device, the device will not
+ * steer new packets on receive virtqueues other than specified nor read from
+ * transmit virtqueues other than specified. Accordingly, driver should not
+ * transmit new packets  on virtqueues other than specified.
+ */
+struct virtio_net_ctrl_mq {
+	__virtio16 virtqueue_pairs;
+};
+
+#define VIRTIO_NET_CTRL_MQ			4
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET		0
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN		1
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX		0x8000
+
+/*
+ * Control network offloads
+ *
+ * Reconfigures the network offloads that guest can handle.
+ *
+ * Available with the VIRTIO_NET_F_CTRL_GUEST_OFFLOADS feature bit.
+ *
+ * Command data format matches the feature bit mask exactly.
+ *
+ * See VIRTIO_NET_F_GUEST_* for the list of offloads
+ * that can be enabled/disabled.
+ */
+#define VIRTIO_NET_CTRL_GUEST_OFFLOADS		5
+#define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET	0
+
+#endif /* _LINUX_VIRTIO_NET_H */
diff --git a/drivers/virtio/virtio_pci.h b/drivers/virtio/virtio_pci.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc753ed7b3d6c5c62e934590dcfc5028aff09dfa
--- /dev/null
+++ b/drivers/virtio/virtio_pci.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_pci.h
+ */
+
+#ifndef _LINUX_VIRTIO_PCI_H
+#define _LINUX_VIRTIO_PCI_H
+
+#ifndef VIRTIO_PCI_NO_LEGACY
+
+/* A 32-bit r/o bitmask of the features supported by the host */
+#define VIRTIO_PCI_HOST_FEATURES	0
+
+/* A 32-bit r/w bitmask of features activated by the guest */
+#define VIRTIO_PCI_GUEST_FEATURES	4
+
+/* A 32-bit r/w PFN for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_PFN		8
+
+/* A 16-bit r/o queue size for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_NUM		12
+
+/* A 16-bit r/w queue selector */
+#define VIRTIO_PCI_QUEUE_SEL		14
+
+/* A 16-bit r/w queue notifier */
+#define VIRTIO_PCI_QUEUE_NOTIFY		16
+
+/* An 8-bit device status register */
+#define VIRTIO_PCI_STATUS		18
+
+/*
+ * An 8-bit r/o interrupt status register. Reading the value will return the
+ * current contents of the ISR and will also clear it. This is effectively
+ * a read-and-acknowledge.
+ */
+#define VIRTIO_PCI_ISR			19
+
+/* MSI-X registers: only enabled if MSI-X is enabled */
+
+/* A 16-bit vector for configuration changes */
+#define VIRTIO_MSI_CONFIG_VECTOR	20
+/* A 16-bit vector for selected queue notifications */
+#define VIRTIO_MSI_QUEUE_VECTOR		22
+
+/*
+ * The remaining space is defined by each driver as the per-driver
+ * configuration space
+ */
+#define VIRTIO_PCI_CONFIG_OFF(msix)	((msix) ? 24 : 20)
+
+/* Virtio ABI version, this must match exactly */
+#define VIRTIO_PCI_ABI_VERSION		0
+
+/*
+ * How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size.
+ */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT	12
+
+/*
+ * The alignment to use between consumer and producer parts of vring.
+ * x86 pagesize again.
+ */
+#define VIRTIO_PCI_VRING_ALIGN		4096
+
+#endif /* VIRTIO_PCI_NO_LEGACY */
+
+/* The bit of the ISR which indicates a device configuration change */
+#define VIRTIO_PCI_ISR_CONFIG		0x2
+/* Vector value used to disable MSI for queue */
+#define VIRTIO_MSI_NO_VECTOR		0xffff
+
+#ifndef VIRTIO_PCI_NO_MODERN
+
+/* IDs for different capabilities.  Must all exist. */
+
+/* Common configuration */
+#define VIRTIO_PCI_CAP_COMMON_CFG	1
+/* Notifications */
+#define VIRTIO_PCI_CAP_NOTIFY_CFG	2
+/* ISR access */
+#define VIRTIO_PCI_CAP_ISR_CFG		3
+/* Device specific configuration */
+#define VIRTIO_PCI_CAP_DEVICE_CFG	4
+/* PCI configuration access */
+#define VIRTIO_PCI_CAP_PCI_CFG		5
+
+/* This is the PCI capability header: */
+struct virtio_pci_cap {
+	__u8 cap_vndr;		/* Generic PCI field: PCI_CAP_ID_VNDR */
+	__u8 cap_next;		/* Generic PCI field: next ptr */
+	__u8 cap_len;		/* Generic PCI field: capability length */
+	__u8 cfg_type;		/* Identifies the structure */
+	__u8 bar;		/* Where to find it */
+	__u8 padding[3];	/* Pad to full dword */
+	__le32 offset;		/* Offset within bar */
+	__le32 length;		/* Length of the structure, in bytes */
+};
+
+struct virtio_pci_notify_cap {
+	struct virtio_pci_cap cap;
+	__le32 notify_off_multiplier;	/* Multiplier for queue_notify_off */
+};
+
+/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
+struct virtio_pci_common_cfg {
+	/* About the whole device */
+	__le32 device_feature_select;	/* read-write */
+	__le32 device_feature;		/* read-only */
+	__le32 guest_feature_select;	/* read-write */
+	__le32 guest_feature;		/* read-write */
+	__le16 msix_config;		/* read-write */
+	__le16 num_queues;		/* read-only */
+	__u8 device_status;		/* read-write */
+	__u8 config_generation;		/* read-only */
+
+	/* About a specific virtqueue */
+	__le16 queue_select;		/* read-write */
+	__le16 queue_size;		/* read-write, power of 2 */
+	__le16 queue_msix_vector;	/* read-write */
+	__le16 queue_enable;		/* read-write */
+	__le16 queue_notify_off;	/* read-only */
+	__le32 queue_desc_lo;		/* read-write */
+	__le32 queue_desc_hi;		/* read-write */
+	__le32 queue_avail_lo;		/* read-write */
+	__le32 queue_avail_hi;		/* read-write */
+	__le32 queue_used_lo;		/* read-write */
+	__le32 queue_used_hi;		/* read-write */
+};
+
+/* Fields in VIRTIO_PCI_CAP_PCI_CFG: */
+struct virtio_pci_cfg_cap {
+	struct virtio_pci_cap cap;
+	__u8 pci_cfg_data[4];		/* Data for BAR access */
+};
+
+/* Macro versions of offsets for the Old Timers! */
+#define VIRTIO_PCI_CAP_VNDR		0
+#define VIRTIO_PCI_CAP_NEXT		1
+#define VIRTIO_PCI_CAP_LEN		2
+#define VIRTIO_PCI_CAP_CFG_TYPE		3
+#define VIRTIO_PCI_CAP_BAR		4
+#define VIRTIO_PCI_CAP_OFFSET		8
+#define VIRTIO_PCI_CAP_LENGTH		12
+
+#define VIRTIO_PCI_NOTIFY_CAP_MULT	16
+
+#define VIRTIO_PCI_COMMON_DFSELECT	0
+#define VIRTIO_PCI_COMMON_DF		4
+#define VIRTIO_PCI_COMMON_GFSELECT	8
+#define VIRTIO_PCI_COMMON_GF		12
+#define VIRTIO_PCI_COMMON_MSIX		16
+#define VIRTIO_PCI_COMMON_NUMQ		18
+#define VIRTIO_PCI_COMMON_STATUS	20
+#define VIRTIO_PCI_COMMON_CFGGENERATION	21
+#define VIRTIO_PCI_COMMON_Q_SELECT	22
+#define VIRTIO_PCI_COMMON_Q_SIZE	24
+#define VIRTIO_PCI_COMMON_Q_MSIX	26
+#define VIRTIO_PCI_COMMON_Q_ENABLE	28
+#define VIRTIO_PCI_COMMON_Q_NOFF	30
+#define VIRTIO_PCI_COMMON_Q_DESCLO	32
+#define VIRTIO_PCI_COMMON_Q_DESCHI	36
+#define VIRTIO_PCI_COMMON_Q_AVAILLO	40
+#define VIRTIO_PCI_COMMON_Q_AVAILHI	44
+#define VIRTIO_PCI_COMMON_Q_USEDLO	48
+#define VIRTIO_PCI_COMMON_Q_USEDHI	52
+
+#endif /* VIRTIO_PCI_NO_MODERN */
+
+#endif /* _LINUX_VIRTIO_PCI_H */
diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c
new file mode 100644
index 0000000000000000000000000000000000000000..08764ee6f2929a1bbe486f64b6a80a56d6c85936
--- /dev/null
+++ b/drivers/virtio/virtio_pci_legacy.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO PCI bus transport driver
+ * Ported from Linux drivers/virtio/virtio_pci*.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <dm/device.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include "virtio_pci.h"
+
+#define VIRTIO_PCI_DRV_NAME	"virtio-pci.l"
+
+/* PCI device ID in the range 0x1000 to 0x103f */
+#define VIRTIO_PCI_VENDOR_ID	0x1af4
+#define VIRTIO_PCI_DEVICE_ID00	0x1000
+#define VIRTIO_PCI_DEVICE_ID01	0x1001
+#define VIRTIO_PCI_DEVICE_ID02	0x1002
+#define VIRTIO_PCI_DEVICE_ID03	0x1003
+#define VIRTIO_PCI_DEVICE_ID04	0x1004
+#define VIRTIO_PCI_DEVICE_ID05	0x1005
+#define VIRTIO_PCI_DEVICE_ID06	0x1006
+#define VIRTIO_PCI_DEVICE_ID07	0x1007
+#define VIRTIO_PCI_DEVICE_ID08	0x1008
+#define VIRTIO_PCI_DEVICE_ID09	0x1009
+#define VIRTIO_PCI_DEVICE_ID0A	0x100a
+#define VIRTIO_PCI_DEVICE_ID0B	0x100b
+#define VIRTIO_PCI_DEVICE_ID0C	0x100c
+#define VIRTIO_PCI_DEVICE_ID0D	0x100d
+#define VIRTIO_PCI_DEVICE_ID0E	0x100e
+#define VIRTIO_PCI_DEVICE_ID0F	0x100f
+#define VIRTIO_PCI_DEVICE_ID10	0x1010
+#define VIRTIO_PCI_DEVICE_ID11	0x1011
+#define VIRTIO_PCI_DEVICE_ID12	0x1012
+#define VIRTIO_PCI_DEVICE_ID13	0x1013
+#define VIRTIO_PCI_DEVICE_ID14	0x1014
+#define VIRTIO_PCI_DEVICE_ID15	0x1015
+#define VIRTIO_PCI_DEVICE_ID16	0x1016
+#define VIRTIO_PCI_DEVICE_ID17	0x1017
+#define VIRTIO_PCI_DEVICE_ID18	0x1018
+#define VIRTIO_PCI_DEVICE_ID19	0x1019
+#define VIRTIO_PCI_DEVICE_ID1A	0x101a
+#define VIRTIO_PCI_DEVICE_ID1B	0x101b
+#define VIRTIO_PCI_DEVICE_ID1C	0x101c
+#define VIRTIO_PCI_DEVICE_ID1D	0x101d
+#define VIRTIO_PCI_DEVICE_ID1E	0x101e
+#define VIRTIO_PCI_DEVICE_ID1F	0x101f
+#define VIRTIO_PCI_DEVICE_ID20	0x1020
+#define VIRTIO_PCI_DEVICE_ID21	0x1021
+#define VIRTIO_PCI_DEVICE_ID22	0x1022
+#define VIRTIO_PCI_DEVICE_ID23	0x1023
+#define VIRTIO_PCI_DEVICE_ID24	0x1024
+#define VIRTIO_PCI_DEVICE_ID25	0x1025
+#define VIRTIO_PCI_DEVICE_ID26	0x1026
+#define VIRTIO_PCI_DEVICE_ID27	0x1027
+#define VIRTIO_PCI_DEVICE_ID28	0x1028
+#define VIRTIO_PCI_DEVICE_ID29	0x1029
+#define VIRTIO_PCI_DEVICE_ID2A	0x102a
+#define VIRTIO_PCI_DEVICE_ID2B	0x102b
+#define VIRTIO_PCI_DEVICE_ID2C	0x102c
+#define VIRTIO_PCI_DEVICE_ID2D	0x102d
+#define VIRTIO_PCI_DEVICE_ID2E	0x102e
+#define VIRTIO_PCI_DEVICE_ID2F	0x102f
+#define VIRTIO_PCI_DEVICE_ID30	0x1030
+#define VIRTIO_PCI_DEVICE_ID31	0x1031
+#define VIRTIO_PCI_DEVICE_ID32	0x1032
+#define VIRTIO_PCI_DEVICE_ID33	0x1033
+#define VIRTIO_PCI_DEVICE_ID34	0x1034
+#define VIRTIO_PCI_DEVICE_ID35	0x1035
+#define VIRTIO_PCI_DEVICE_ID36	0x1036
+#define VIRTIO_PCI_DEVICE_ID37	0x1037
+#define VIRTIO_PCI_DEVICE_ID38	0x1038
+#define VIRTIO_PCI_DEVICE_ID39	0x1039
+#define VIRTIO_PCI_DEVICE_ID3A	0x103a
+#define VIRTIO_PCI_DEVICE_ID3B	0x103b
+#define VIRTIO_PCI_DEVICE_ID3C	0x103c
+#define VIRTIO_PCI_DEVICE_ID3D	0x103d
+#define VIRTIO_PCI_DEVICE_ID3E	0x103e
+#define VIRTIO_PCI_DEVICE_ID3F	0x103f
+
+/**
+ * virtio pci transport driver private data
+ *
+ * @ioaddr:	pci transport device register base
+ * @version:	pci transport device version
+ */
+struct virtio_pci_priv {
+	void __iomem *ioaddr;
+};
+
+static int virtio_pci_get_config(struct udevice *udev, unsigned int offset,
+				 void *buf, unsigned int len)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false);
+	u8 *ptr = buf;
+	int i;
+
+	for (i = 0; i < len; i++)
+		ptr[i] = ioread8(ioaddr + i);
+
+	return 0;
+}
+
+static int virtio_pci_set_config(struct udevice *udev, unsigned int offset,
+				 const void *buf, unsigned int len)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false);
+	const u8 *ptr = buf;
+	int i;
+
+	for (i = 0; i < len; i++)
+		iowrite8(ptr[i], ioaddr + i);
+
+	return 0;
+}
+
+static int virtio_pci_get_status(struct udevice *udev, u8 *status)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	*status = ioread8(priv->ioaddr + VIRTIO_PCI_STATUS);
+
+	return 0;
+}
+
+static int virtio_pci_set_status(struct udevice *udev, u8 status)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/* We should never be setting status to 0 */
+	WARN_ON(status == 0);
+
+	iowrite8(status, priv->ioaddr + VIRTIO_PCI_STATUS);
+
+	return 0;
+}
+
+static int virtio_pci_reset(struct udevice *udev)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/* 0 status means a reset */
+	iowrite8(0, priv->ioaddr + VIRTIO_PCI_STATUS);
+
+	/*
+	 * Flush out the status write, and flush in device writes,
+	 * including MSI-X interrupts, if any.
+	 */
+	ioread8(priv->ioaddr + VIRTIO_PCI_STATUS);
+
+	return 0;
+}
+
+static int virtio_pci_get_features(struct udevice *udev, u64 *features)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/*
+	 * When someone needs more than 32 feature bits, we'll need to
+	 * steal a bit to indicate that the rest are somewhere else.
+	 */
+	*features = ioread32(priv->ioaddr + VIRTIO_PCI_HOST_FEATURES);
+
+	return 0;
+}
+
+static int virtio_pci_set_features(struct udevice *udev)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* Make sure we don't have any features > 32 bits! */
+	WARN_ON((u32)uc_priv->features != uc_priv->features);
+
+	/* We only support 32 feature bits */
+	iowrite32(uc_priv->features, priv->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
+
+	return 0;
+}
+
+static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev,
+					     unsigned int index)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	struct virtqueue *vq;
+	unsigned int num;
+	int err;
+
+	/* Select the queue we're interested in */
+	iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+	/* Check if queue is either not available or already active */
+	num = ioread16(priv->ioaddr + VIRTIO_PCI_QUEUE_NUM);
+	if (!num || ioread32(priv->ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
+		err = -ENOENT;
+		goto error_available;
+	}
+
+	/* Create the vring */
+	vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev);
+	if (!vq) {
+		err = -ENOMEM;
+		goto error_available;
+	}
+
+	/* Activate the queue */
+	iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
+		  priv->ioaddr + VIRTIO_PCI_QUEUE_PFN);
+
+	return vq;
+
+error_available:
+	return ERR_PTR(err);
+}
+
+static void virtio_pci_del_vq(struct virtqueue *vq)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(vq->vdev);
+	unsigned int index = vq->index;
+
+	iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+	/* Select and deactivate the queue */
+	iowrite32(0, priv->ioaddr + VIRTIO_PCI_QUEUE_PFN);
+
+	vring_del_virtqueue(vq);
+}
+
+static int virtio_pci_del_vqs(struct udevice *udev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
+		virtio_pci_del_vq(vq);
+
+	return 0;
+}
+
+static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs,
+			       struct virtqueue *vqs[])
+{
+	int i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = virtio_pci_setup_vq(udev, i);
+		if (IS_ERR(vqs[i])) {
+			virtio_pci_del_vqs(udev);
+			return PTR_ERR(vqs[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/*
+	 * We write the queue's selector into the notification register
+	 * to signal the other end
+	 */
+	iowrite16(vq->index, priv->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
+
+	return 0;
+}
+
+static int virtio_pci_bind(struct udevice *udev)
+{
+	static int num_devs;
+	char name[20];
+
+	/* Create a unique device name for PCI type devices */
+	sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++);
+	device_set_name(udev, name);
+
+	return 0;
+}
+
+static int virtio_pci_probe(struct udevice *udev)
+{
+	struct pci_child_platdata *pplat = dev_get_parent_platdata(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	u16 subvendor, subdevice;
+	u8 revision;
+
+	/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
+	if (pplat->device < 0x1000 || pplat->device > 0x103f)
+		return -ENODEV;
+
+	/* Transitional devices must have a PCI revision ID of 0 */
+	dm_pci_read_config8(udev, PCI_REVISION_ID, &revision);
+	if (revision != VIRTIO_PCI_ABI_VERSION) {
+		printf("(%s): virtio_pci expected ABI version %d, got %d\n",
+		       udev->name, VIRTIO_PCI_ABI_VERSION, revision);
+		return -ENODEV;
+	}
+
+	/*
+	 * Transitional devices must have the PCI subsystem device ID matching
+	 * the virtio device ID
+	 */
+	dm_pci_read_config16(udev, PCI_SUBSYSTEM_ID, &subdevice);
+	dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
+	uc_priv->device = subdevice;
+	uc_priv->vendor = subvendor;
+
+	priv->ioaddr = dm_pci_map_bar(udev, PCI_BASE_ADDRESS_0, PCI_REGION_IO);
+	if (!priv->ioaddr)
+		return -ENXIO;
+	debug("(%s): virtio legacy device reg base %04lx\n",
+	      udev->name, (ulong)priv->ioaddr);
+
+	debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name,
+	      uc_priv->device, uc_priv->vendor, revision);
+
+	return 0;
+}
+
+static const struct dm_virtio_ops virtio_pci_ops = {
+	.get_config	= virtio_pci_get_config,
+	.set_config	= virtio_pci_set_config,
+	.get_status	= virtio_pci_get_status,
+	.set_status	= virtio_pci_set_status,
+	.reset		= virtio_pci_reset,
+	.get_features	= virtio_pci_get_features,
+	.set_features	= virtio_pci_set_features,
+	.find_vqs	= virtio_pci_find_vqs,
+	.del_vqs	= virtio_pci_del_vqs,
+	.notify		= virtio_pci_notify,
+};
+
+U_BOOT_DRIVER(virtio_pci_legacy) = {
+	.name	= VIRTIO_PCI_DRV_NAME,
+	.id	= UCLASS_VIRTIO,
+	.ops	= &virtio_pci_ops,
+	.bind	= virtio_pci_bind,
+	.probe	= virtio_pci_probe,
+	.priv_auto_alloc_size = sizeof(struct virtio_pci_priv),
+};
+
+static struct pci_device_id virtio_pci_supported[] = {
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) },
+	{},
+};
+
+U_BOOT_PCI_DEVICE(virtio_pci_legacy, virtio_pci_supported);
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
new file mode 100644
index 0000000000000000000000000000000000000000..da76aea8d178401a498fda0dcae9081695855f52
--- /dev/null
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -0,0 +1,609 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO PCI bus transport driver
+ * Ported from Linux drivers/virtio/virtio_pci*.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <dm/device.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include "virtio_pci.h"
+
+#define VIRTIO_PCI_DRV_NAME	"virtio-pci.m"
+
+/* PCI device ID in the range 0x1040 to 0x107f */
+#define VIRTIO_PCI_VENDOR_ID	0x1af4
+#define VIRTIO_PCI_DEVICE_ID00	0x1040
+#define VIRTIO_PCI_DEVICE_ID01	0x1041
+#define VIRTIO_PCI_DEVICE_ID02	0x1042
+#define VIRTIO_PCI_DEVICE_ID03	0x1043
+#define VIRTIO_PCI_DEVICE_ID04	0x1044
+#define VIRTIO_PCI_DEVICE_ID05	0x1045
+#define VIRTIO_PCI_DEVICE_ID06	0x1046
+#define VIRTIO_PCI_DEVICE_ID07	0x1047
+#define VIRTIO_PCI_DEVICE_ID08	0x1048
+#define VIRTIO_PCI_DEVICE_ID09	0x1049
+#define VIRTIO_PCI_DEVICE_ID0A	0x104a
+#define VIRTIO_PCI_DEVICE_ID0B	0x104b
+#define VIRTIO_PCI_DEVICE_ID0C	0x104c
+#define VIRTIO_PCI_DEVICE_ID0D	0x104d
+#define VIRTIO_PCI_DEVICE_ID0E	0x104e
+#define VIRTIO_PCI_DEVICE_ID0F	0x104f
+#define VIRTIO_PCI_DEVICE_ID10	0x1050
+#define VIRTIO_PCI_DEVICE_ID11	0x1051
+#define VIRTIO_PCI_DEVICE_ID12	0x1052
+#define VIRTIO_PCI_DEVICE_ID13	0x1053
+#define VIRTIO_PCI_DEVICE_ID14	0x1054
+#define VIRTIO_PCI_DEVICE_ID15	0x1055
+#define VIRTIO_PCI_DEVICE_ID16	0x1056
+#define VIRTIO_PCI_DEVICE_ID17	0x1057
+#define VIRTIO_PCI_DEVICE_ID18	0x1058
+#define VIRTIO_PCI_DEVICE_ID19	0x1059
+#define VIRTIO_PCI_DEVICE_ID1A	0x105a
+#define VIRTIO_PCI_DEVICE_ID1B	0x105b
+#define VIRTIO_PCI_DEVICE_ID1C	0x105c
+#define VIRTIO_PCI_DEVICE_ID1D	0x105d
+#define VIRTIO_PCI_DEVICE_ID1E	0x105e
+#define VIRTIO_PCI_DEVICE_ID1F	0x105f
+#define VIRTIO_PCI_DEVICE_ID20	0x1060
+#define VIRTIO_PCI_DEVICE_ID21	0x1061
+#define VIRTIO_PCI_DEVICE_ID22	0x1062
+#define VIRTIO_PCI_DEVICE_ID23	0x1063
+#define VIRTIO_PCI_DEVICE_ID24	0x1064
+#define VIRTIO_PCI_DEVICE_ID25	0x1065
+#define VIRTIO_PCI_DEVICE_ID26	0x1066
+#define VIRTIO_PCI_DEVICE_ID27	0x1067
+#define VIRTIO_PCI_DEVICE_ID28	0x1068
+#define VIRTIO_PCI_DEVICE_ID29	0x1069
+#define VIRTIO_PCI_DEVICE_ID2A	0x106a
+#define VIRTIO_PCI_DEVICE_ID2B	0x106b
+#define VIRTIO_PCI_DEVICE_ID2C	0x106c
+#define VIRTIO_PCI_DEVICE_ID2D	0x106d
+#define VIRTIO_PCI_DEVICE_ID2E	0x106e
+#define VIRTIO_PCI_DEVICE_ID2F	0x106f
+#define VIRTIO_PCI_DEVICE_ID30	0x1070
+#define VIRTIO_PCI_DEVICE_ID31	0x1071
+#define VIRTIO_PCI_DEVICE_ID32	0x1072
+#define VIRTIO_PCI_DEVICE_ID33	0x1073
+#define VIRTIO_PCI_DEVICE_ID34	0x1074
+#define VIRTIO_PCI_DEVICE_ID35	0x1075
+#define VIRTIO_PCI_DEVICE_ID36	0x1076
+#define VIRTIO_PCI_DEVICE_ID37	0x1077
+#define VIRTIO_PCI_DEVICE_ID38	0x1078
+#define VIRTIO_PCI_DEVICE_ID39	0x1079
+#define VIRTIO_PCI_DEVICE_ID3A	0x107a
+#define VIRTIO_PCI_DEVICE_ID3B	0x107b
+#define VIRTIO_PCI_DEVICE_ID3C	0x107c
+#define VIRTIO_PCI_DEVICE_ID3D	0x107d
+#define VIRTIO_PCI_DEVICE_ID3E	0x107e
+#define VIRTIO_PCI_DEVICE_ID3F	0x107f
+
+/**
+ * virtio pci transport driver private data
+ *
+ * @common: pci transport device common register block base
+ * @notify_base: pci transport device notify register block base
+ * @device: pci transport device device-specific register block base
+ * @device_len: pci transport device device-specific register block length
+ * @notify_offset_multiplier: multiply queue_notify_off by this value
+ */
+struct virtio_pci_priv {
+	struct virtio_pci_common_cfg __iomem *common;
+	void __iomem *notify_base;
+	void __iomem *device;
+	u32 device_len;
+	u32 notify_offset_multiplier;
+};
+
+static int virtio_pci_get_config(struct udevice *udev, unsigned int offset,
+				 void *buf, unsigned int len)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	u8 b;
+	__le16 w;
+	__le32 l;
+
+	WARN_ON(offset + len > priv->device_len);
+
+	switch (len) {
+	case 1:
+		b = ioread8(priv->device + offset);
+		memcpy(buf, &b, sizeof(b));
+		break;
+	case 2:
+		w = cpu_to_le16(ioread16(priv->device + offset));
+		memcpy(buf, &w, sizeof(w));
+		break;
+	case 4:
+		l = cpu_to_le32(ioread32(priv->device + offset));
+		memcpy(buf, &l, sizeof(l));
+		break;
+	case 8:
+		l = cpu_to_le32(ioread32(priv->device + offset));
+		memcpy(buf, &l, sizeof(l));
+		l = cpu_to_le32(ioread32(priv->device + offset + sizeof(l)));
+		memcpy(buf + sizeof(l), &l, sizeof(l));
+		break;
+	default:
+		WARN_ON(true);
+	}
+
+	return 0;
+}
+
+static int virtio_pci_set_config(struct udevice *udev, unsigned int offset,
+				 const void *buf, unsigned int len)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	u8 b;
+	__le16 w;
+	__le32 l;
+
+	WARN_ON(offset + len > priv->device_len);
+
+	switch (len) {
+	case 1:
+		memcpy(&b, buf, sizeof(b));
+		iowrite8(b, priv->device + offset);
+		break;
+	case 2:
+		memcpy(&w, buf, sizeof(w));
+		iowrite16(le16_to_cpu(w), priv->device + offset);
+		break;
+	case 4:
+		memcpy(&l, buf, sizeof(l));
+		iowrite32(le32_to_cpu(l), priv->device + offset);
+		break;
+	case 8:
+		memcpy(&l, buf, sizeof(l));
+		iowrite32(le32_to_cpu(l), priv->device + offset);
+		memcpy(&l, buf + sizeof(l), sizeof(l));
+		iowrite32(le32_to_cpu(l), priv->device + offset + sizeof(l));
+		break;
+	default:
+		WARN_ON(true);
+	}
+
+	return 0;
+}
+
+static int virtio_pci_generation(struct udevice *udev, u32 *counter)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	*counter = ioread8(&priv->common->config_generation);
+
+	return 0;
+}
+
+static int virtio_pci_get_status(struct udevice *udev, u8 *status)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	*status = ioread8(&priv->common->device_status);
+
+	return 0;
+}
+
+static int virtio_pci_set_status(struct udevice *udev, u8 status)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/* We should never be setting status to 0 */
+	WARN_ON(status == 0);
+
+	iowrite8(status, &priv->common->device_status);
+
+	return 0;
+}
+
+static int virtio_pci_reset(struct udevice *udev)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	/* 0 status means a reset */
+	iowrite8(0, &priv->common->device_status);
+
+	/*
+	 * After writing 0 to device_status, the driver MUST wait for a read
+	 * of device_status to return 0 before reinitializing the device.
+	 * This will flush out the status write, and flush in device writes,
+	 * including MSI-X interrupts, if any.
+	 */
+	while (ioread8(&priv->common->device_status))
+		udelay(1000);
+
+	return 0;
+}
+
+static int virtio_pci_get_features(struct udevice *udev, u64 *features)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+
+	iowrite32(0, &priv->common->device_feature_select);
+	*features = ioread32(&priv->common->device_feature);
+	iowrite32(1, &priv->common->device_feature_select);
+	*features |= ((u64)ioread32(&priv->common->device_feature) << 32);
+
+	return 0;
+}
+
+static int virtio_pci_set_features(struct udevice *udev)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	if (!__virtio_test_bit(udev, VIRTIO_F_VERSION_1)) {
+		debug("virtio: device uses modern interface but does not have VIRTIO_F_VERSION_1\n");
+		return -EINVAL;
+	}
+
+	iowrite32(0, &priv->common->guest_feature_select);
+	iowrite32((u32)uc_priv->features, &priv->common->guest_feature);
+	iowrite32(1, &priv->common->guest_feature_select);
+	iowrite32(uc_priv->features >> 32, &priv->common->guest_feature);
+
+	return 0;
+}
+
+static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev,
+					     unsigned int index)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	struct virtio_pci_common_cfg __iomem *cfg = priv->common;
+	struct virtqueue *vq;
+	u16 num;
+	u64 addr;
+	int err;
+
+	if (index >= ioread16(&cfg->num_queues))
+		return ERR_PTR(-ENOENT);
+
+	/* Select the queue we're interested in */
+	iowrite16(index, &cfg->queue_select);
+
+	/* Check if queue is either not available or already active */
+	num = ioread16(&cfg->queue_size);
+	if (!num || ioread16(&cfg->queue_enable))
+		return ERR_PTR(-ENOENT);
+
+	if (num & (num - 1)) {
+		printf("(%s): bad queue size %u", udev->name, num);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Create the vring */
+	vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev);
+	if (!vq) {
+		err = -ENOMEM;
+		goto error_available;
+	}
+
+	/* Activate the queue */
+	iowrite16(virtqueue_get_vring_size(vq), &cfg->queue_size);
+
+	addr = virtqueue_get_desc_addr(vq);
+	iowrite32((u32)addr, &cfg->queue_desc_lo);
+	iowrite32(addr >> 32, &cfg->queue_desc_hi);
+
+	addr = virtqueue_get_avail_addr(vq);
+	iowrite32((u32)addr, &cfg->queue_avail_lo);
+	iowrite32(addr >> 32, &cfg->queue_avail_hi);
+
+	addr = virtqueue_get_used_addr(vq);
+	iowrite32((u32)addr, &cfg->queue_used_lo);
+	iowrite32(addr >> 32, &cfg->queue_used_hi);
+
+	iowrite16(1, &cfg->queue_enable);
+
+	return vq;
+
+error_available:
+	return ERR_PTR(err);
+}
+
+static void virtio_pci_del_vq(struct virtqueue *vq)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(vq->vdev);
+	unsigned int index = vq->index;
+
+	iowrite16(index, &priv->common->queue_select);
+
+	/* Select and deactivate the queue */
+	iowrite16(0, &priv->common->queue_enable);
+
+	vring_del_virtqueue(vq);
+}
+
+static int virtio_pci_del_vqs(struct udevice *udev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
+		virtio_pci_del_vq(vq);
+
+	return 0;
+}
+
+static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs,
+			       struct virtqueue *vqs[])
+{
+	int i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = virtio_pci_setup_vq(udev, i);
+		if (IS_ERR(vqs[i])) {
+			virtio_pci_del_vqs(udev);
+			return PTR_ERR(vqs[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
+{
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	u16 off;
+
+	/* Select the queue we're interested in */
+	iowrite16(vq->index, &priv->common->queue_select);
+
+	/* get offset of notification word for this vq */
+	off = ioread16(&priv->common->queue_notify_off);
+
+	/*
+	 * We write the queue's selector into the notification register
+	 * to signal the other end
+	 */
+	iowrite16(vq->index,
+		  priv->notify_base + off * priv->notify_offset_multiplier);
+
+	return 0;
+}
+
+/**
+ * virtio_pci_find_capability - walk capabilities to find device info
+ *
+ * @udev:	the transport device
+ * @cfg_type:	the VIRTIO_PCI_CAP_* value we seek
+ *
+ * @return offset of the configuration structure
+ */
+static int virtio_pci_find_capability(struct udevice *udev, u8 cfg_type)
+{
+	int pos;
+	int offset;
+	u8 type, bar;
+
+	for (pos = dm_pci_find_capability(udev, PCI_CAP_ID_VNDR);
+	     pos > 0;
+	     pos = dm_pci_find_next_capability(udev, pos, PCI_CAP_ID_VNDR)) {
+		offset = pos + offsetof(struct virtio_pci_cap, cfg_type);
+		dm_pci_read_config8(udev, offset, &type);
+		offset = pos + offsetof(struct virtio_pci_cap, bar);
+		dm_pci_read_config8(udev, offset, &bar);
+
+		/* Ignore structures with reserved BAR values */
+		if (bar > 0x5)
+			continue;
+
+		if (type == cfg_type)
+			return pos;
+	}
+
+	return 0;
+}
+
+/**
+ * virtio_pci_map_capability - map base address of the capability
+ *
+ * @udev:	the transport device
+ * @off:	offset of the configuration structure
+ *
+ * @return base address of the capability
+ */
+static void __iomem *virtio_pci_map_capability(struct udevice *udev, int off)
+{
+	u8 bar;
+	u32 offset;
+	ulong base;
+	void __iomem *p;
+
+	if (!off)
+		return NULL;
+
+	offset = off + offsetof(struct virtio_pci_cap, bar);
+	dm_pci_read_config8(udev, offset, &bar);
+	offset = off + offsetof(struct virtio_pci_cap, offset);
+	dm_pci_read_config32(udev, offset, &offset);
+
+	/*
+	 * TODO: adding 64-bit BAR support
+	 *
+	 * Per spec, the BAR is permitted to be either 32-bit or 64-bit.
+	 * For simplicity, only read the BAR address as 32-bit.
+	 */
+	base = dm_pci_read_bar32(udev, bar);
+	p = (void __iomem *)base + offset;
+
+	return p;
+}
+
+static int virtio_pci_bind(struct udevice *udev)
+{
+	static int num_devs;
+	char name[20];
+
+	/* Create a unique device name  */
+	sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++);
+	device_set_name(udev, name);
+
+	return 0;
+}
+
+static int virtio_pci_probe(struct udevice *udev)
+{
+	struct pci_child_platdata *pplat = dev_get_parent_platdata(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtio_pci_priv *priv = dev_get_priv(udev);
+	u16 subvendor;
+	u8 revision;
+	int common, notify, device;
+	int offset;
+
+	/* We only own devices >= 0x1040 and <= 0x107f: leave the rest. */
+	if (pplat->device < 0x1040 || pplat->device > 0x107f)
+		return -ENODEV;
+
+	/* Transitional devices must not have a PCI revision ID of 0 */
+	dm_pci_read_config8(udev, PCI_REVISION_ID, &revision);
+
+	/* Modern devices: simply use PCI device id, but start from 0x1040. */
+	uc_priv->device = pplat->device - 0x1040;
+	dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
+	uc_priv->vendor = subvendor;
+
+	/* Check for a common config: if not, use legacy mode (bar 0) */
+	common = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_COMMON_CFG);
+	if (!common) {
+		printf("(%s): leaving for legacy driver\n", udev->name);
+		return -ENODEV;
+	}
+
+	/* If common is there, notify should be too */
+	notify = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_NOTIFY_CFG);
+	if (!notify) {
+		printf("(%s): missing capabilities %i/%i\n", udev->name,
+		       common, notify);
+		return -EINVAL;
+	}
+
+	/*
+	 * Device capability is only mandatory for devices that have
+	 * device-specific configuration.
+	 */
+	device = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_DEVICE_CFG);
+	if (device) {
+		offset = notify + offsetof(struct virtio_pci_cap, length);
+		dm_pci_read_config32(udev, offset, &priv->device_len);
+	}
+
+	/* Map configuration structures */
+	priv->common = virtio_pci_map_capability(udev, common);
+	priv->notify_base = virtio_pci_map_capability(udev, notify);
+	priv->device = virtio_pci_map_capability(udev, device);
+	debug("(%p): common @ %p, notify base @ %p, device @ %p\n",
+	      udev, priv->common, priv->notify_base, priv->device);
+
+	/* Read notify_off_multiplier from config space */
+	offset = notify + offsetof(struct virtio_pci_notify_cap,
+				   notify_off_multiplier);
+	dm_pci_read_config32(udev, offset, &priv->notify_offset_multiplier);
+
+	debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name,
+	      uc_priv->device, uc_priv->vendor, revision);
+
+	return 0;
+}
+
+static const struct dm_virtio_ops virtio_pci_ops = {
+	.get_config	= virtio_pci_get_config,
+	.set_config	= virtio_pci_set_config,
+	.generation	= virtio_pci_generation,
+	.get_status	= virtio_pci_get_status,
+	.set_status	= virtio_pci_set_status,
+	.reset		= virtio_pci_reset,
+	.get_features	= virtio_pci_get_features,
+	.set_features	= virtio_pci_set_features,
+	.find_vqs	= virtio_pci_find_vqs,
+	.del_vqs	= virtio_pci_del_vqs,
+	.notify		= virtio_pci_notify,
+};
+
+U_BOOT_DRIVER(virtio_pci_modern) = {
+	.name	= VIRTIO_PCI_DRV_NAME,
+	.id	= UCLASS_VIRTIO,
+	.ops	= &virtio_pci_ops,
+	.bind	= virtio_pci_bind,
+	.probe	= virtio_pci_probe,
+	.priv_auto_alloc_size = sizeof(struct virtio_pci_priv),
+};
+
+static struct pci_device_id virtio_pci_supported[] = {
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) },
+	{ PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) },
+	{},
+};
+
+U_BOOT_PCI_DEVICE(virtio_pci_modern, virtio_pci_supported);
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
new file mode 100644
index 0000000000000000000000000000000000000000..0eeb3501c20536db7a7e42eac36cc9a837dcccce
--- /dev/null
+++ b/drivers/virtio/virtio_ring.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * virtio ring implementation
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+
+int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
+		  unsigned int out_sgs, unsigned int in_sgs)
+{
+	struct vring_desc *desc;
+	unsigned int total_sg = out_sgs + in_sgs;
+	unsigned int i, n, avail, descs_used, uninitialized_var(prev);
+	int head;
+
+	WARN_ON(total_sg == 0);
+
+	head = vq->free_head;
+
+	desc = vq->vring.desc;
+	i = head;
+	descs_used = total_sg;
+
+	if (vq->num_free < descs_used) {
+		debug("Can't add buf len %i - avail = %i\n",
+		      descs_used, vq->num_free);
+		/*
+		 * FIXME: for historical reasons, we force a notify here if
+		 * there are outgoing parts to the buffer.  Presumably the
+		 * host should service the ring ASAP.
+		 */
+		if (out_sgs)
+			virtio_notify(vq->vdev, vq);
+		return -ENOSPC;
+	}
+
+	for (n = 0; n < out_sgs; n++) {
+		struct virtio_sg *sg = sgs[n];
+
+		desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
+		desc[i].addr = cpu_to_virtio64(vq->vdev, (u64)(size_t)sg->addr);
+		desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
+
+		prev = i;
+		i = virtio16_to_cpu(vq->vdev, desc[i].next);
+	}
+	for (; n < (out_sgs + in_sgs); n++) {
+		struct virtio_sg *sg = sgs[n];
+
+		desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT |
+						VRING_DESC_F_WRITE);
+		desc[i].addr = cpu_to_virtio64(vq->vdev,
+					       (u64)(uintptr_t)sg->addr);
+		desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
+
+		prev = i;
+		i = virtio16_to_cpu(vq->vdev, desc[i].next);
+	}
+	/* Last one doesn't continue */
+	desc[prev].flags &= cpu_to_virtio16(vq->vdev, ~VRING_DESC_F_NEXT);
+
+	/* We're using some buffers from the free list. */
+	vq->num_free -= descs_used;
+
+	/* Update free pointer */
+	vq->free_head = i;
+
+	/*
+	 * Put entry in available array (but don't update avail->idx
+	 * until they do sync).
+	 */
+	avail = vq->avail_idx_shadow & (vq->vring.num - 1);
+	vq->vring.avail->ring[avail] = cpu_to_virtio16(vq->vdev, head);
+
+	/*
+	 * Descriptors and available array need to be set before we expose the
+	 * new available array entries.
+	 */
+	virtio_wmb();
+	vq->avail_idx_shadow++;
+	vq->vring.avail->idx = cpu_to_virtio16(vq->vdev, vq->avail_idx_shadow);
+	vq->num_added++;
+
+	/*
+	 * This is very unlikely, but theoretically possible.
+	 * Kick just in case.
+	 */
+	if (unlikely(vq->num_added == (1 << 16) - 1))
+		virtqueue_kick(vq);
+
+	return 0;
+}
+
+static bool virtqueue_kick_prepare(struct virtqueue *vq)
+{
+	u16 new, old;
+	bool needs_kick;
+
+	/*
+	 * We need to expose available array entries before checking
+	 * avail event.
+	 */
+	virtio_mb();
+
+	old = vq->avail_idx_shadow - vq->num_added;
+	new = vq->avail_idx_shadow;
+	vq->num_added = 0;
+
+	if (vq->event) {
+		needs_kick = vring_need_event(virtio16_to_cpu(vq->vdev,
+				vring_avail_event(&vq->vring)), new, old);
+	} else {
+		needs_kick = !(vq->vring.used->flags & cpu_to_virtio16(vq->vdev,
+				VRING_USED_F_NO_NOTIFY));
+	}
+
+	return needs_kick;
+}
+
+void virtqueue_kick(struct virtqueue *vq)
+{
+	if (virtqueue_kick_prepare(vq))
+		virtio_notify(vq->vdev, vq);
+}
+
+static void detach_buf(struct virtqueue *vq, unsigned int head)
+{
+	unsigned int i;
+	__virtio16 nextflag = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
+
+	/* Put back on free list: unmap first-level descriptors and find end */
+	i = head;
+
+	while (vq->vring.desc[i].flags & nextflag) {
+		i = virtio16_to_cpu(vq->vdev, vq->vring.desc[i].next);
+		vq->num_free++;
+	}
+
+	vq->vring.desc[i].next = cpu_to_virtio16(vq->vdev, vq->free_head);
+	vq->free_head = head;
+
+	/* Plus final descriptor */
+	vq->num_free++;
+}
+
+static inline bool more_used(const struct virtqueue *vq)
+{
+	return vq->last_used_idx != virtio16_to_cpu(vq->vdev,
+			vq->vring.used->idx);
+}
+
+void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
+{
+	unsigned int i;
+	u16 last_used;
+
+	if (!more_used(vq)) {
+		debug("(%s.%d): No more buffers in queue\n",
+		      vq->vdev->name, vq->index);
+		return NULL;
+	}
+
+	/* Only get used array entries after they have been exposed by host */
+	virtio_rmb();
+
+	last_used = (vq->last_used_idx & (vq->vring.num - 1));
+	i = virtio32_to_cpu(vq->vdev, vq->vring.used->ring[last_used].id);
+	if (len) {
+		*len = virtio32_to_cpu(vq->vdev,
+				       vq->vring.used->ring[last_used].len);
+		debug("(%s.%d): last used idx %u with len %u\n",
+		      vq->vdev->name, vq->index, i, *len);
+	}
+
+	if (unlikely(i >= vq->vring.num)) {
+		printf("(%s.%d): id %u out of range\n",
+		       vq->vdev->name, vq->index, i);
+		return NULL;
+	}
+
+	detach_buf(vq, i);
+	vq->last_used_idx++;
+	/*
+	 * If we expect an interrupt for the next entry, tell host
+	 * by writing event index and flush out the write before
+	 * the read in the next get_buf call.
+	 */
+	if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
+		virtio_store_mb(&vring_used_event(&vq->vring),
+				cpu_to_virtio16(vq->vdev, vq->last_used_idx));
+
+	return (void *)(uintptr_t)virtio64_to_cpu(vq->vdev,
+						  vq->vring.desc[i].addr);
+}
+
+static struct virtqueue *__vring_new_virtqueue(unsigned int index,
+					       struct vring vring,
+					       struct udevice *udev)
+{
+	unsigned int i;
+	struct virtqueue *vq;
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct udevice *vdev = uc_priv->vdev;
+
+	vq = malloc(sizeof(*vq));
+	if (!vq)
+		return NULL;
+
+	vq->vdev = vdev;
+	vq->index = index;
+	vq->num_free = vring.num;
+	vq->vring = vring;
+	vq->last_used_idx = 0;
+	vq->avail_flags_shadow = 0;
+	vq->avail_idx_shadow = 0;
+	vq->num_added = 0;
+	list_add_tail(&vq->list, &uc_priv->vqs);
+
+	vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
+
+	/* Tell other side not to bother us */
+	vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+	if (!vq->event)
+		vq->vring.avail->flags = cpu_to_virtio16(vdev,
+				vq->avail_flags_shadow);
+
+	/* Put everything in free lists */
+	vq->free_head = 0;
+	for (i = 0; i < vring.num - 1; i++)
+		vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
+
+	return vq;
+}
+
+struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
+					 unsigned int vring_align,
+					 struct udevice *udev)
+{
+	struct virtqueue *vq;
+	void *queue = NULL;
+	struct vring vring;
+
+	/* We assume num is a power of 2 */
+	if (num & (num - 1)) {
+		printf("Bad virtqueue length %u\n", num);
+		return NULL;
+	}
+
+	/* TODO: allocate each queue chunk individually */
+	for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
+		queue = memalign(PAGE_SIZE, vring_size(num, vring_align));
+		if (queue)
+			break;
+	}
+
+	if (!num)
+		return NULL;
+
+	if (!queue) {
+		/* Try to get a single page. You are my only hope! */
+		queue = memalign(PAGE_SIZE, vring_size(num, vring_align));
+	}
+	if (!queue)
+		return NULL;
+
+	memset(queue, 0, vring_size(num, vring_align));
+	vring_init(&vring, num, queue, vring_align);
+
+	vq = __vring_new_virtqueue(index, vring, udev);
+	if (!vq) {
+		free(queue);
+		return NULL;
+	}
+	debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
+	      queue, vq, num);
+
+	return vq;
+}
+
+void vring_del_virtqueue(struct virtqueue *vq)
+{
+	free(vq->vring.desc);
+	list_del(&vq->list);
+	free(vq);
+}
+
+unsigned int virtqueue_get_vring_size(struct virtqueue *vq)
+{
+	return vq->vring.num;
+}
+
+ulong virtqueue_get_desc_addr(struct virtqueue *vq)
+{
+	return (ulong)vq->vring.desc;
+}
+
+ulong virtqueue_get_avail_addr(struct virtqueue *vq)
+{
+	return (ulong)vq->vring.desc +
+	       ((char *)vq->vring.avail - (char *)vq->vring.desc);
+}
+
+ulong virtqueue_get_used_addr(struct virtqueue *vq)
+{
+	return (ulong)vq->vring.desc +
+	       ((char *)vq->vring.used - (char *)vq->vring.desc);
+}
+
+bool virtqueue_poll(struct virtqueue *vq, u16 last_used_idx)
+{
+	virtio_mb();
+
+	return last_used_idx != virtio16_to_cpu(vq->vdev, vq->vring.used->idx);
+}
+
+void virtqueue_dump(struct virtqueue *vq)
+{
+	unsigned int i;
+
+	printf("virtqueue %p for dev %s:\n", vq, vq->vdev->name);
+	printf("\tindex %u, phys addr %p num %u\n",
+	       vq->index, vq->vring.desc, vq->vring.num);
+	printf("\tfree_head %u, num_added %u, num_free %u\n",
+	       vq->free_head, vq->num_added, vq->num_free);
+	printf("\tlast_used_idx %u, avail_flags_shadow %u, avail_idx_shadow %u\n",
+	       vq->last_used_idx, vq->avail_flags_shadow, vq->avail_idx_shadow);
+
+	printf("Descriptor dump:\n");
+	for (i = 0; i < vq->vring.num; i++) {
+		printf("\tdesc[%u] = { 0x%llx, len %u, flags %u, next %u }\n",
+		       i, vq->vring.desc[i].addr, vq->vring.desc[i].len,
+		       vq->vring.desc[i].flags, vq->vring.desc[i].next);
+	}
+
+	printf("Avail ring dump:\n");
+	printf("\tflags %u, idx %u\n",
+	       vq->vring.avail->flags, vq->vring.avail->idx);
+	for (i = 0; i < vq->vring.num; i++) {
+		printf("\tavail[%u] = %u\n",
+		       i, vq->vring.avail->ring[i]);
+	}
+
+	printf("Used ring dump:\n");
+	printf("\tflags %u, idx %u\n",
+	       vq->vring.used->flags, vq->vring.used->idx);
+	for (i = 0; i < vq->vring.num; i++) {
+		printf("\tused[%u] = { %u, %u }\n", i,
+		       vq->vring.used->ring[i].id, vq->vring.used->ring[i].len);
+	}
+}
diff --git a/drivers/virtio/virtio_sandbox.c b/drivers/virtio/virtio_sandbox.c
new file mode 100644
index 0000000000000000000000000000000000000000..2addb1ebc5e2f30fea82dfff0979bde6c17fe016
--- /dev/null
+++ b/drivers/virtio/virtio_sandbox.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO Sandbox transport driver, for testing purpose only
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+
+struct virtio_sandbox_priv {
+	u8 id;
+	u8 status;
+	u64 device_features;
+	u64 driver_features;
+	ulong queue_desc;
+	ulong queue_available;
+	ulong queue_used;
+};
+
+static int virtio_sandbox_get_config(struct udevice *udev, unsigned int offset,
+				     void *buf, unsigned int len)
+{
+	return 0;
+}
+
+static int virtio_sandbox_set_config(struct udevice *udev, unsigned int offset,
+				     const void *buf, unsigned int len)
+{
+	return 0;
+}
+
+static int virtio_sandbox_get_status(struct udevice *udev, u8 *status)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+	*status = priv->status;
+
+	return 0;
+}
+
+static int virtio_sandbox_set_status(struct udevice *udev, u8 status)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+	/* We should never be setting status to 0 */
+	WARN_ON(status == 0);
+
+	priv->status = status;
+
+	return 0;
+}
+
+static int virtio_sandbox_reset(struct udevice *udev)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+	/* 0 status means a reset */
+	priv->status = 0;
+
+	return 0;
+}
+
+static int virtio_sandbox_get_features(struct udevice *udev, u64 *features)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+	*features = priv->device_features;
+
+	return 0;
+}
+
+static int virtio_sandbox_set_features(struct udevice *udev)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	priv->driver_features = uc_priv->features;
+
+	return 0;
+}
+
+static struct virtqueue *virtio_sandbox_setup_vq(struct udevice *udev,
+						 unsigned int index)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+	struct virtqueue *vq;
+	ulong addr;
+	int err;
+
+	/* Create the vring */
+	vq = vring_create_virtqueue(index, 4, 4096, udev);
+	if (!vq) {
+		err = -ENOMEM;
+		goto error_new_virtqueue;
+	}
+
+	addr = virtqueue_get_desc_addr(vq);
+	priv->queue_desc = addr;
+
+	addr = virtqueue_get_avail_addr(vq);
+	priv->queue_available = addr;
+
+	addr = virtqueue_get_used_addr(vq);
+	priv->queue_used = addr;
+
+	return vq;
+
+error_new_virtqueue:
+	return ERR_PTR(err);
+}
+
+static void virtio_sandbox_del_vq(struct virtqueue *vq)
+{
+	vring_del_virtqueue(vq);
+}
+
+static int virtio_sandbox_del_vqs(struct udevice *udev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
+		virtio_sandbox_del_vq(vq);
+
+	return 0;
+}
+
+static int virtio_sandbox_find_vqs(struct udevice *udev, unsigned int nvqs,
+				   struct virtqueue *vqs[])
+{
+	int i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = virtio_sandbox_setup_vq(udev, i);
+		if (IS_ERR(vqs[i])) {
+			virtio_sandbox_del_vqs(udev);
+			return PTR_ERR(vqs[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int virtio_sandbox_notify(struct udevice *udev, struct virtqueue *vq)
+{
+	return 0;
+}
+
+static int virtio_sandbox_probe(struct udevice *udev)
+{
+	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* fake some information for testing */
+	priv->device_features = VIRTIO_F_VERSION_1;
+	uc_priv->device = VIRTIO_ID_BLOCK;
+	uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't';
+
+	return 0;
+}
+
+/* check virtio device driver's remove routine was called to reset the device */
+static int virtio_sandbox_child_post_remove(struct udevice *vdev)
+{
+	u8 status;
+
+	virtio_get_status(vdev, &status);
+	if (status)
+		panic("virtio device was not reset\n");
+
+	return 0;
+}
+
+static const struct dm_virtio_ops virtio_sandbox1_ops = {
+	.get_config	= virtio_sandbox_get_config,
+	.set_config	= virtio_sandbox_set_config,
+	.get_status	= virtio_sandbox_get_status,
+	.set_status	= virtio_sandbox_set_status,
+	.reset		= virtio_sandbox_reset,
+	.get_features	= virtio_sandbox_get_features,
+	.set_features	= virtio_sandbox_set_features,
+	.find_vqs	= virtio_sandbox_find_vqs,
+	.del_vqs	= virtio_sandbox_del_vqs,
+	.notify		= virtio_sandbox_notify,
+};
+
+static const struct udevice_id virtio_sandbox1_ids[] = {
+	{ .compatible = "sandbox,virtio1" },
+	{ }
+};
+
+U_BOOT_DRIVER(virtio_sandbox1) = {
+	.name	= "virtio-sandbox1",
+	.id	= UCLASS_VIRTIO,
+	.of_match = virtio_sandbox1_ids,
+	.ops	= &virtio_sandbox1_ops,
+	.probe	= virtio_sandbox_probe,
+	.child_post_remove = virtio_sandbox_child_post_remove,
+	.priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
+};
+
+/* this one without notify op */
+static const struct dm_virtio_ops virtio_sandbox2_ops = {
+	.get_config	= virtio_sandbox_get_config,
+	.set_config	= virtio_sandbox_set_config,
+	.get_status	= virtio_sandbox_get_status,
+	.set_status	= virtio_sandbox_set_status,
+	.reset		= virtio_sandbox_reset,
+	.get_features	= virtio_sandbox_get_features,
+	.set_features	= virtio_sandbox_set_features,
+	.find_vqs	= virtio_sandbox_find_vqs,
+	.del_vqs	= virtio_sandbox_del_vqs,
+};
+
+static const struct udevice_id virtio_sandbox2_ids[] = {
+	{ .compatible = "sandbox,virtio2" },
+	{ }
+};
+
+U_BOOT_DRIVER(virtio_sandbox2) = {
+	.name	= "virtio-sandbox2",
+	.id	= UCLASS_VIRTIO,
+	.of_match = virtio_sandbox2_ids,
+	.ops	= &virtio_sandbox2_ops,
+	.probe	= virtio_sandbox_probe,
+	.priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
+};
diff --git a/drivers/watchdog/ast_wdt.c b/drivers/watchdog/ast_wdt.c
index 59afa21efa4b5f77a1eacefc3007d79085fb8cdd..523484b1fff945c335d2da11a8b977f93d7e6633 100644
--- a/drivers/watchdog/ast_wdt.c
+++ b/drivers/watchdog/ast_wdt.c
@@ -119,5 +119,4 @@ U_BOOT_DRIVER(ast_wdt) = {
 	.priv_auto_alloc_size = sizeof(struct ast_wdt_priv),
 	.ofdata_to_platdata = ast_wdt_ofdata_to_platdata,
 	.ops = &ast_wdt_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/include/blk.h b/include/blk.h
index 6af219681cd1b52caee57fbb5dc07511933ed165..d0c033aece0fb7986a614cef5a9176dc6bca2f44 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -33,6 +33,7 @@ enum if_type {
 	IF_TYPE_HOST,
 	IF_TYPE_NVME,
 	IF_TYPE_EFI,
+	IF_TYPE_VIRTIO,
 
 	IF_TYPE_COUNT,			/* Number of interface types */
 };
@@ -356,16 +357,6 @@ int blk_create_devicef(struct udevice *parent, const char *drv_name,
 		       const char *name, int if_type, int devnum, int blksz,
 		       lbaint_t lba, struct udevice **devp);
 
-/**
- * blk_prepare_device() - Prepare a block device for use
- *
- * This reads partition information from the device if supported.
- *
- * @dev:	Device to prepare
- * @return 0 if ok, -ve on error
- */
-int blk_prepare_device(struct udevice *dev);
-
 /**
  * blk_unbind_all() - Unbind all device of the given interface type
  *
@@ -388,6 +379,17 @@ int blk_unbind_all(int if_type);
  */
 int blk_find_max_devnum(enum if_type if_type);
 
+/**
+ * blk_next_free_devnum() - get the next device number for an interface type
+ *
+ * Finds the next number that is safe to use for a newly allocated device for
+ * an interface type @if_type.
+ *
+ * @if_type:	Interface type to scan
+ * @return next device number safe to use, or -ve on error
+ */
+int blk_next_free_devnum(enum if_type if_type);
+
 /**
  * blk_select_hwpart() - select a hardware partition
  *
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 02ac4c79528d41e842dc3aa98c972105e989e1e5..ee2b24a62a2d7a60d2ffb621cb92383a95b743ae 100644
--- a/include/dm/device-internal.h
+++ b/include/dm/device-internal.h
@@ -74,8 +74,8 @@ int device_bind_with_driver_data(struct udevice *parent,
  * tree.
  *
  * @parent: Pointer to device's parent
- * @pre_reloc_only: If true, bind the driver only if its DM_INIT_F flag is set.
- * If false bind the driver always.
+ * @pre_reloc_only: If true, bind the driver only if its DM_FLAG_PRE_RELOC flag
+ * is set. If false bind the driver always.
  * @info: Name and platdata for this device
  * @devp: if non-NULL, returns a pointer to the bound device
  * @return 0 if OK, -ve on error
diff --git a/include/dm/lists.h b/include/dm/lists.h
index 13d1516a121934ca280fdba32ee772248ab4e3fc..810e244d9ef3d74a179373f516c040a75ab3f39f 100644
--- a/include/dm/lists.h
+++ b/include/dm/lists.h
@@ -39,8 +39,8 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id);
  * each one. The devices will have @parent as their parent.
  *
  * @parent: parent device (root)
- * @early_only: If true, bind only drivers with the DM_INIT_F flag. If false
- * bind all drivers.
+ * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC flag.
+ * If false bind all drivers.
  */
 int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only);
 
@@ -53,10 +53,13 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only);
  * @parent: parent device (root)
  * @node: device tree node to bind
  * @devp: if non-NULL, returns a pointer to the bound device
+ * @pre_reloc_only: If true, bind only nodes with special devicetree properties,
+ * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers.
  * @return 0 if device was bound, -EINVAL if the device tree is invalid,
  * other -ve value on error
  */
-int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp);
+int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
+		   bool pre_reloc_only);
 
 /**
  * device_bind_driver() - bind a device to a driver
diff --git a/include/dm/root.h b/include/dm/root.h
index 2b9c6da4165d808a7df0034459674b0eb014bdc3..c8d629ba9bfba8c6df3a3137aadeddd105895614 100644
--- a/include/dm/root.h
+++ b/include/dm/root.h
@@ -48,8 +48,8 @@ int dm_scan_platdata(bool pre_reloc_only);
  * the top-level subnodes are examined.
  *
  * @blob: Pointer to device tree blob
- * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
- * flag. If false bind all drivers.
+ * @pre_reloc_only: If true, bind only nodes with special devicetree properties,
+ * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers.
  * @return 0 if OK, -ve on error
  */
 int dm_scan_fdt(const void *blob, bool pre_reloc_only);
@@ -62,8 +62,8 @@ int dm_scan_fdt(const void *blob, bool pre_reloc_only);
  * of "clocks" node.
  *
  * @blob: Pointer to device tree blob
- * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
- * flag. If false bind all drivers.
+ * @pre_reloc_only: If true, bind only nodes with special devicetree properties,
+ * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers.
  * @return 0 if OK, -ve on error
  */
 int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only);
@@ -76,8 +76,9 @@ int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only);
  * programmaticaly. They should do this by calling device_bind() on each
  * device.
  *
- * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
- * flag. If false bind all drivers.
+ * @pre_reloc_only: If true, bind only nodes with special devicetree properties,
+ * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers.
+ * @return 0 if OK, -ve on error
  */
 int dm_scan_other(bool pre_reloc_only);
 
@@ -88,8 +89,8 @@ int dm_scan_other(bool pre_reloc_only);
  * then scans and binds available devices from platform data and the FDT.
  * This calls dm_init() to set up Driver Model structures.
  *
- * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
- * flag. If false bind all drivers.
+ * @pre_reloc_only: If true, bind only nodes with special devicetree properties,
+ * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers.
  * @return 0 if OK, -ve on error
  */
 int dm_init_and_scan(bool pre_reloc_only);
diff --git a/include/dm/test.h b/include/dm/test.h
index 83418eb48225b933067ac8f9e5cc2cc0cc531306..07385cd531fa2c9af660010ad4a439aab0ec75e0 100644
--- a/include/dm/test.h
+++ b/include/dm/test.h
@@ -69,6 +69,7 @@ struct dm_test_priv {
 	int op_count[DM_TEST_OP_COUNT];
 	int uclass_flag;
 	int uclass_total;
+	int uclass_postp;
 };
 
 /**
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 269a2c6e722831d704ed2bc8de9d728a957b342f..c91dca1f824cb1034410b75c937027e9c9812f6c 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -96,6 +96,7 @@ enum uclass_id {
 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */
 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */
 	UCLASS_VIDEO_OSD,	/* On-screen display */
+	UCLASS_VIRTIO,		/* VirtIO transport device */
 	UCLASS_W1,		/* Dallas 1-Wire bus */
 	UCLASS_W1_EEPROM,	/* one-wire EEPROMs */
 	UCLASS_WDT,		/* Watchdot Timer driver */
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index eebf2d5614c40a1f360081f95aca499b6cfe9b83..4ef0d0f0c01285cb314473b9a883cd525deafc86 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -61,7 +61,8 @@ struct udevice;
  * @post_probe: Called after a new device is probed
  * @pre_remove: Called before a device is removed
  * @child_post_bind: Called after a child is bound to a device in this uclass
- * @child_pre_probe: Called before a child is probed in this uclass
+ * @child_pre_probe: Called before a child in this uclass is probed
+ * @child_post_probe: Called after a child in this uclass is probed
  * @init: Called to set up the uclass
  * @destroy: Called to destroy the uclass
  * @priv_auto_alloc_size: If non-zero this is the size of the private data
@@ -94,6 +95,7 @@ struct uclass_driver {
 	int (*pre_remove)(struct udevice *dev);
 	int (*child_post_bind)(struct udevice *dev);
 	int (*child_pre_probe)(struct udevice *dev);
+	int (*child_post_probe)(struct udevice *dev);
 	int (*init)(struct uclass *class);
 	int (*destroy)(struct uclass *class);
 	int priv_auto_alloc_size;
diff --git a/include/dm/util.h b/include/dm/util.h
index 898822e4459f548de5257d6a14a72339001e5abd..9ff6531d1b25f3380ed35bdb9c21ec620086f027 100644
--- a/include/dm/util.h
+++ b/include/dm/util.h
@@ -55,7 +55,7 @@ static inline void dm_dump_devres(void)
  * There are 3 settings currently in use
  * -
  * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL
- *   Existing platforms only use it to indicate nodes needee in
+ *   Existing platforms only use it to indicate nodes needed in
  *   SPL. Should probably be replaced by u-boot,dm-spl for
  *   existing platforms.
  * @blob: devicetree
@@ -65,4 +65,29 @@ static inline void dm_dump_devres(void)
  */
 bool dm_fdt_pre_reloc(const void *blob, int offset);
 
+/**
+ * Check if an of node should be or was bound before relocation.
+ *
+ * Devicetree nodes can be marked as needed to be bound
+ * in the loader stages via special devicetree properties.
+ *
+ * Before relocation this function can be used to check if nodes
+ * are required in either SPL or TPL stages.
+ *
+ * After relocation and jumping into the real U-Boot binary
+ * it is possible to determine if a node was bound in one of
+ * SPL/TPL stages.
+ *
+ * There are 3 settings currently in use
+ * -
+ * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL
+ *   Existing platforms only use it to indicate nodes needed in
+ *   SPL. Should probably be replaced by u-boot,dm-spl for
+ *   existing platforms.
+ * @node: of node
+ *
+ * Returns true if node is needed in SPL/TL, false otherwise.
+ */
+bool dm_ofnode_pre_reloc(ofnode node);
+
 #endif
diff --git a/include/init.h b/include/init.h
index a58d7a6917f23625d59e24200d7448a90c56a03d..afc953d51e2d07cdbcde9459b7c2d275c3e60dee 100644
--- a/include/init.h
+++ b/include/init.h
@@ -109,7 +109,14 @@ int arch_reserve_stacks(void);
  */
 int init_cache_f_r(void);
 
+#if !CONFIG_IS_ENABLED(CPU)
+/**
+ * print_cpuinfo() - Display information about the CPU
+ *
+ * Return: 0 if OK, -ve on error
+ */
 int print_cpuinfo(void);
+#endif
 int timer_init(void);
 int reserve_mmu(void);
 int misc_init_f(void);
diff --git a/include/linux/io.h b/include/linux/io.h
index d1b3efed9daa77b952e4edca0897d79b0eb68cc3..9badab49b0bdb25fca8ce038b1beba9d3b9af1d8 100644
--- a/include/linux/io.h
+++ b/include/linux/io.h
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 #include <asm/io.h>
 
+#ifndef CONFIG_HAVE_ARCH_IOMAP
 static inline u8 ioread8(const volatile void __iomem *addr)
 {
 	return readb(addr);
@@ -21,6 +22,7 @@ static inline u32 ioread32(const volatile void __iomem *addr)
 {
 	return readl(addr);
 }
+#endif /* !CONFIG_HAVE_ARCH_IOMAP */
 
 #ifdef CONFIG_64BIT
 static inline u64 ioread64(const volatile void __iomem *addr)
@@ -29,6 +31,7 @@ static inline u64 ioread64(const volatile void __iomem *addr)
 }
 #endif /* CONFIG_64BIT */
 
+#ifndef CONFIG_HAVE_ARCH_IOMAP
 static inline void iowrite8(u8 value, volatile void __iomem *addr)
 {
 	writeb(value, addr);
@@ -43,6 +46,7 @@ static inline void iowrite32(u32 value, volatile void __iomem *addr)
 {
 	writel(value, addr);
 }
+#endif /* !CONFIG_HAVE_ARCH_IOMAP */
 
 #ifdef CONFIG_64BIT
 static inline void iowrite64(u64 value, volatile void __iomem *addr)
diff --git a/include/pci.h b/include/pci.h
index 938a8390cbfca66f8ca7683801f83a220c3a3e7b..785d7d28b7e44c3500587624665349dcae90c493 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -1312,6 +1312,29 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr,
  */
 void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);
 
+/**
+ * dm_pci_find_next_capability() - find a capability starting from an offset
+ *
+ * Tell if a device supports a given PCI capability. Returns the
+ * address of the requested capability structure within the device's
+ * PCI configuration space or 0 in case the device does not support it.
+ *
+ * Possible values for @cap:
+ *
+ *  %PCI_CAP_ID_MSI	Message Signalled Interrupts
+ *  %PCI_CAP_ID_PCIX	PCI-X
+ *  %PCI_CAP_ID_EXP	PCI Express
+ *  %PCI_CAP_ID_MSIX	MSI-X
+ *
+ * See PCI_CAP_ID_xxx for the complete capability ID codes.
+ *
+ * @dev:	PCI device to query
+ * @start:	offset to start from
+ * @cap:	capability code
+ * @return:	capability address or 0 if not supported
+ */
+int dm_pci_find_next_capability(struct udevice *dev, u8 start, int cap);
+
 /**
  * dm_pci_find_capability() - find a capability
  *
@@ -1334,6 +1357,31 @@ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);
  */
 int dm_pci_find_capability(struct udevice *dev, int cap);
 
+/**
+ * dm_pci_find_next_ext_capability() - find an extended capability
+ *				       starting from an offset
+ *
+ * Tell if a device supports a given PCI express extended capability.
+ * Returns the address of the requested extended capability structure
+ * within the device's PCI configuration space or 0 in case the device
+ * does not support it.
+ *
+ * Possible values for @cap:
+ *
+ *  %PCI_EXT_CAP_ID_ERR	Advanced Error Reporting
+ *  %PCI_EXT_CAP_ID_VC	Virtual Channel
+ *  %PCI_EXT_CAP_ID_DSN	Device Serial Number
+ *  %PCI_EXT_CAP_ID_PWR	Power Budgeting
+ *
+ * See PCI_EXT_CAP_ID_xxx for the complete extended capability ID codes.
+ *
+ * @dev:	PCI device to query
+ * @start:	offset to start from
+ * @cap:	extended capability code
+ * @return:	extended capability address or 0 if not supported
+ */
+int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap);
+
 /**
  * dm_pci_find_ext_capability() - find an extended capability
  *
diff --git a/include/regmap.h b/include/regmap.h
index 6a574eaa41294a740a688f3bc717c0ba68afcf8f..b2b733fda6813c207725a9958a5f2ac05e857f91 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -7,6 +7,61 @@
 #ifndef __REGMAP_H
 #define __REGMAP_H
 
+/**
+ * DOC: Overview
+ *
+ * Regmaps are an abstraction mechanism that allows device drivers to access
+ * register maps irrespective of the underlying bus architecture. This entails
+ * that for devices that support multiple busses (e.g. I2C and SPI for a GPIO
+ * expander chip) only one driver has to be written. This driver will
+ * instantiate a regmap with a backend depending on the bus the device is
+ * attached to, and use the regmap API to access the register map through that
+ * bus transparently.
+ *
+ * Read and write functions are supplied, which can read/write data of
+ * arbitrary length from/to the regmap.
+ *
+ * The endianness of regmap accesses is selectable for each map through device
+ * tree settings via the boolean "little-endian", "big-endian", and
+ * "native-endian" properties.
+ *
+ * Furthermore, the register map described by a regmap can be split into
+ * multiple disjoint areas called ranges. In this way, register maps with
+ * "holes", i.e. areas of addressable memory that are not part of the register
+ * map, can be accessed in a concise manner.
+ *
+ * Currently, only a bare "mem" backend for regmaps is supported, which
+ * accesses the register map as regular IO-mapped memory.
+ */
+
+/**
+ * enum regmap_size_t - Access sizes for regmap reads and writes
+ *
+ * @REGMAP_SIZE_8: 8-bit read/write access size
+ * @REGMAP_SIZE_16: 16-bit read/write access size
+ * @REGMAP_SIZE_32: 32-bit read/write access size
+ * @REGMAP_SIZE_64: 64-bit read/write access size
+ */
+enum regmap_size_t {
+	REGMAP_SIZE_8 = 1,
+	REGMAP_SIZE_16 = 2,
+	REGMAP_SIZE_32 = 4,
+	REGMAP_SIZE_64 = 8,
+};
+
+/**
+ * enum regmap_endianness_t - Endianness for regmap reads and writes
+ *
+ * @REGMAP_NATIVE_ENDIAN: Native endian read/write accesses
+ * @REGMAP_LITTLE_ENDIAN: Little endian read/write accesses
+ * @REGMAP_BIG_ENDIAN: Big endian read/write accesses
+ */
+enum regmap_endianness_t {
+	REGMAP_NATIVE_ENDIAN,
+	REGMAP_LITTLE_ENDIAN,
+	REGMAP_BIG_ENDIAN,
+};
+
 /**
  * struct regmap_range - a register map range
  *
@@ -21,10 +76,11 @@ struct regmap_range {
 /**
  * struct regmap - a way of accessing hardware/bus registers
  *
- * @range_count: Number of ranges available within the map
- * @ranges:	Array of ranges
+ * @range_count:	Number of ranges available within the map
+ * @ranges:		Array of ranges
  */
 struct regmap {
+	enum regmap_endianness_t endianness;
 	int range_count;
 	struct regmap_range ranges[0];
 };
@@ -33,14 +89,155 @@ struct regmap {
  * Interface to provide access to registers either through a direct memory
  * bus or through a peripheral bus like I2C, SPI.
  */
+
+/**
+ * regmap_write() - Write a 32-bit value to a regmap
+ *
+ * @map:	Regmap to write to
+ * @offset:	Offset in the regmap to write to
+ * @val:	Data to write to the regmap at the specified offset
+ *
+ * Note that this function will only write values of 32 bit width to the
+ * regmap; if the size of data to be read is different, the regmap_raw_write
+ * function can be used.
+ *
+ * Return: 0 if OK, -ve on error
+ */
 int regmap_write(struct regmap *map, uint offset, uint val);
+
+/**
+ * regmap_read() - Read a 32-bit value from a regmap
+ *
+ * @map:	Regmap to read from
+ * @offset:	Offset in the regmap to read from
+ * @valp:	Pointer to the buffer to receive the data read from the regmap
+ *		at the specified offset
+ *
+ * Note that this function will only read values of 32 bit width from the
+ * regmap; if the size of data to be read is different, the regmap_raw_read
+ * function can be used.
+ *
+ * Return: 0 if OK, -ve on error
+ */
 int regmap_read(struct regmap *map, uint offset, uint *valp);
 
-#define regmap_write32(map, ptr, member, val) \
-	regmap_write(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), val)
+/**
+ * regmap_raw_write() - Write a value of specified length to a regmap
+ *
+ * @map:	Regmap to write to
+ * @offset:	Offset in the regmap to write to
+ * @val:	Value to write to the regmap at the specified offset
+ * @val_len:	Length of the data to be written to the regmap
+ *
+ * Note that this function will, as opposed to regmap_write, write data of
+ * arbitrary length to the regmap, and not just 32-bit values, and is thus a
+ * generalized version of regmap_write.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_raw_write(struct regmap *map, uint offset, const void *val,
+		     size_t val_len);
+
+/**
+ * regmap_raw_read() - Read a value of specified length from a regmap
+ *
+ * @map:	Regmap to read from
+ * @offset:	Offset in the regmap to read from
+ * @valp:	Pointer to the buffer to receive the data read from the regmap
+ *		at the specified offset
+ * @val_len:	Length of the data to be read from the regmap
+ *
+ * Note that this function will, as opposed to regmap_read, read data of
+ * arbitrary length from the regmap, and not just 32-bit values, and is thus a
+ * generalized version of regmap_read.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_raw_read(struct regmap *map, uint offset, void *valp,
+		    size_t val_len);
+
+/**
+ * regmap_raw_write_range() - Write a value of specified length to a range of a
+ *			      regmap
+ *
+ * @map:	Regmap to write to
+ * @range_num:	Number of the range in the regmap to write to
+ * @offset:	Offset in the regmap to write to
+ * @val:	Value to write to the regmap at the specified offset
+ * @val_len:	Length of the data to be written to the regmap
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
+			   const void *val, size_t val_len);
+
+/**
+ * regmap_raw_read_range() - Read a value of specified length from a range of a
+ *			     regmap
+ *
+ * @map:	Regmap to read from
+ * @range_num:	Number of the range in the regmap to write to
+ * @offset:	Offset in the regmap to read from
+ * @valp:	Pointer to the buffer to receive the data read from the regmap
+ *		at the specified offset
+ * @val_len:	Length of the data to be read from the regmap
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
+			  void *valp, size_t val_len);
+
+/**
+ * regmap_range_set() - Set a value in a regmap range described by a struct
+ * @map:    Regmap in which a value should be set
+ * @range:  Range of the regmap in which a value should be set
+ * @type:   Structure type that describes the memory layout of the regmap range
+ * @member: Member of the describing structure that should be set in the regmap
+ *          range
+ * @val:    Value which should be written to the regmap range
+ */
+#define regmap_range_set(map, range, type, member, val) \
+	do { \
+		typeof(((type *)0)->member) __tmp = val; \
+		regmap_raw_write_range(map, range, offsetof(type, member), \
+				       &__tmp, sizeof(((type *)0)->member)); \
+	} while (0)
+
+/**
+ * regmap_set() - Set a value in a regmap described by a struct
+ * @map:    Regmap in which a value should be set
+ * @type:   Structure type that describes the memory layout of the regmap
+ * @member: Member of the describing structure that should be set in the regmap
+ * @val:    Value which should be written to the regmap
+ */
+#define regmap_set(map, type, member, val) \
+	regmap_range_set(map, 0, type, member, val)
+
+/**
+ * regmap_range_get() - Get a value from a regmap range described by a struct
+ * @map:    Regmap from which a value should be read
+ * @range:  Range of the regmap from which a value should be read
+ * @type:   Structure type that describes the memory layout of the regmap
+ *          range
+ * @member: Member of the describing structure that should be read in the
+ *          regmap range
+ * @valp:   Variable that receives the value read from the regmap range
+ */
+#define regmap_range_get(map, range, type, member, valp) \
+	regmap_raw_read_range(map, range, offsetof(type, member), \
+			      (void *)valp, sizeof(((type *)0)->member))
 
-#define regmap_read32(map, ptr, member, valp) \
-	regmap_read(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), valp)
+/**
+ * regmap_get() - Get a value from a regmap described by a struct
+ * @map:    Regmap from which a value should be read
+ * @type:   Structure type that describes the memory layout of the regmap
+ *          range
+ * @member: Member of the describing structure that should be read in the
+ *          regmap
+ * @valp:   Variable that receives the value read from the regmap
+ */
+#define regmap_get(map, type, member, valp) \
+	regmap_range_get(map, 0, type, member, valp)
 
 /**
  * regmap_update_bits() - Perform a read/modify/write using a mask
@@ -49,31 +246,36 @@ int regmap_read(struct regmap *map, uint offset, uint *valp);
  * @offset:	Offset of the memory
  * @mask:	Mask to apply to the read value
  * @val:	Value to apply to the value to write
+ * Return: 0 if OK, -ve on error
  */
 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val);
 
 /**
  * regmap_init_mem() - Set up a new register map that uses memory access
  *
- * Use regmap_uninit() to free it.
- *
  * @node:	Device node that uses this map
  * @mapp:	Returns allocated map
+ * Return: 0 if OK, -ve on error
+ *
+ * Use regmap_uninit() to free it.
  */
 int regmap_init_mem(ofnode node, struct regmap **mapp);
 
 /**
- * regmap_init_mem_platdata() - Set up a new memory register map for of-platdata
+ * regmap_init_mem_platdata() - Set up a new memory register map for
+ *				of-platdata
+ *
+ * @dev:	Device that uses this map
+ * @reg:	List of address, size pairs
+ * @count:	Number of pairs (e.g. 1 if the regmap has a single entry)
+ * @mapp:	Returns allocated map
+ * Return: 0 if OK, -ve on error
  *
  * This creates a new regmap with a list of regions passed in, rather than
  * using the device tree. It only supports 32-bit machines.
  *
  * Use regmap_uninit() to free it.
  *
- * @dev:	Device that uses this map
- * @reg:	List of address, size pairs
- * @count:	Number of pairs (e.g. 1 if the regmap has a single entry)
- * @mapp:	Returns allocated map
  */
 int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
 			     struct regmap **mapp);
@@ -83,11 +285,15 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
  *
  * @map:	Regmap to query
  * @range_num:	Range to look up
+ * Return: Pointer to the range in question if OK, NULL on error
  */
 void *regmap_get_range(struct regmap *map, unsigned int range_num);
 
 /**
  * regmap_uninit() - free a previously inited regmap
+ *
+ * @map:	Regmap to free
+ * Return: 0 if OK, -ve on error
  */
 int regmap_uninit(struct regmap *map);
 
diff --git a/include/virtio.h b/include/virtio.h
new file mode 100644
index 0000000000000000000000000000000000000000..654fdf154b6c9731daa35e303e3dc24a32eeb42a
--- /dev/null
+++ b/include/virtio.h
@@ -0,0 +1,707 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO is a virtualization standard for network and disk device drivers
+ * where just the guest's device driver "knows" it is running in a virtual
+ * environment, and cooperates with the hypervisor. This enables guests to
+ * get high performance network and disk operations, and gives most of the
+ * performance benefits of paravirtualization. In the U-Boot case, the guest
+ * is U-Boot itself, while the virtual environment are normally QEMU targets
+ * like ARM, RISC-V and x86.
+ *
+ * See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
+ * the VirtIO specification v1.0.
+ *
+ * This file is largely based on Linux kernel virtio_*.h files
+ */
+
+#ifndef __VIRTIO_H__
+#define __VIRTIO_H__
+
+#define VIRTIO_ID_NET		1 /* virtio net */
+#define VIRTIO_ID_BLOCK		2 /* virtio block */
+#define VIRTIO_ID_MAX_NUM	3
+
+#define VIRTIO_NET_DRV_NAME	"virtio-net"
+#define VIRTIO_BLK_DRV_NAME	"virtio-blk"
+
+/* Status byte for guest to report progress, and synchronize features */
+
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE	1
+/* We have found a driver for the device */
+#define VIRTIO_CONFIG_S_DRIVER		2
+/* Driver has used its parts of the config, and is happy */
+#define VIRTIO_CONFIG_S_DRIVER_OK	4
+/* Driver has finished configuring features */
+#define VIRTIO_CONFIG_S_FEATURES_OK	8
+/* Device entered invalid state, driver must reset it */
+#define VIRTIO_CONFIG_S_NEEDS_RESET	0x40
+/* We've given up on this device */
+#define VIRTIO_CONFIG_S_FAILED		0x80
+
+/*
+ * Virtio feature bits VIRTIO_TRANSPORT_F_START through VIRTIO_TRANSPORT_F_END
+ * are reserved for the transport being used (eg: virtio_ring, virtio_pci etc.),
+ * the rest are per-device feature bits.
+ */
+#define VIRTIO_TRANSPORT_F_START	28
+#define VIRTIO_TRANSPORT_F_END		38
+
+#ifndef VIRTIO_CONFIG_NO_LEGACY
+/*
+ * Do we get callbacks when the ring is completely used,
+ * even if we've suppressed them?
+ */
+#define VIRTIO_F_NOTIFY_ON_EMPTY	24
+
+/* Can the device handle any descriptor layout? */
+#define VIRTIO_F_ANY_LAYOUT		27
+#endif /* VIRTIO_CONFIG_NO_LEGACY */
+
+/* v1.0 compliant */
+#define VIRTIO_F_VERSION_1		32
+
+/*
+ * If clear - device has the IOMMU bypass quirk feature.
+ * If set - use platform tools to detect the IOMMU.
+ *
+ * Note the reverse polarity (compared to most other features),
+ * this is for compatibility with legacy systems.
+ */
+#define VIRTIO_F_IOMMU_PLATFORM		33
+
+/* Does the device support Single Root I/O Virtualization? */
+#define VIRTIO_F_SR_IOV			37
+
+/**
+ * virtio scatter-gather struct
+ *
+ * @addr:		sg buffer address
+ * @lengh:		sg buffer length
+ */
+struct virtio_sg {
+	void *addr;
+	size_t length;
+};
+
+struct virtqueue;
+
+/* virtio bus operations */
+struct dm_virtio_ops {
+	/**
+	 * get_config() - read the value of a configuration field
+	 *
+	 * @vdev:	the real virtio device
+	 * @offset:	the offset of the configuration field
+	 * @buf:	the buffer to write the field value into
+	 * @len:	the length of the buffer
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*get_config)(struct udevice *vdev, unsigned int offset,
+			  void *buf, unsigned int len);
+	/**
+	 * set_config() - write the value of a configuration field
+	 *
+	 * @vdev:	the real virtio device
+	 * @offset:	the offset of the configuration field
+	 * @buf:	the buffer to read the field value from
+	 * @len:	the length of the buffer
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_config)(struct udevice *vdev, unsigned int offset,
+			  const void *buf, unsigned int len);
+	/**
+	 * generation() - config generation counter
+	 *
+	 * @vdev:	the real virtio device
+	 * @counter:	the returned config generation counter
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*generation)(struct udevice *vdev, u32 *counter);
+	/**
+	 * get_status() - read the status byte
+	 *
+	 * @vdev:	the real virtio device
+	 * @status:	the returned status byte
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*get_status)(struct udevice *vdev, u8 *status);
+	/**
+	 * set_status() - write the status byte
+	 *
+	 * @vdev:	the real virtio device
+	 * @status:	the new status byte
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_status)(struct udevice *vdev, u8 status);
+	/**
+	 * reset() - reset the device
+	 *
+	 * @vdev:	the real virtio device
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*reset)(struct udevice *vdev);
+	/**
+	 * get_features() - get the array of feature bits for this device
+	 *
+	 * @vdev:	the real virtio device
+	 * @features:	the first 32 feature bits (all we currently need)
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*get_features)(struct udevice *vdev, u64 *features);
+	/**
+	 * set_features() - confirm what device features we'll be using
+	 *
+	 * @vdev:	the real virtio device
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_features)(struct udevice *vdev);
+	/**
+	 * find_vqs() - find virtqueues and instantiate them
+	 *
+	 * @vdev:	the real virtio device
+	 * @nvqs:	the number of virtqueues to find
+	 * @vqs:	on success, includes new virtqueues
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*find_vqs)(struct udevice *vdev, unsigned int nvqs,
+			struct virtqueue *vqs[]);
+	/**
+	 * del_vqs() - free virtqueues found by find_vqs()
+	 *
+	 * @vdev:	the real virtio device
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*del_vqs)(struct udevice *vdev);
+	/**
+	 * notify() - notify the device to process the queue
+	 *
+	 * @vdev:	the real virtio device
+	 * @vq:		virtqueue to process
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*notify)(struct udevice *vdev, struct virtqueue *vq);
+};
+
+/* Get access to a virtio bus' operations */
+#define virtio_get_ops(dev)	((struct dm_virtio_ops *)(dev)->driver->ops)
+
+/**
+ * virtio uclass per device private data
+ *
+ * @vqs:			virtualqueue for the virtio device
+ * @vdev:			the real virtio device underneath
+ * @legacy:			is it a legacy device?
+ * @device:			virtio device ID
+ * @vendor:			virtio vendor ID
+ * @features:			negotiated supported features
+ * @feature_table:		an array of feature supported by the driver
+ * @feature_table_size:		number of entries in the feature table array
+ * @feature_table_legacy:	same as feature_table but working in legacy mode
+ * @feature_table_size_legacy:	number of entries in feature table legacy array
+ */
+struct virtio_dev_priv {
+	struct list_head vqs;
+	struct udevice *vdev;
+	bool legacy;
+	u32 device;
+	u32 vendor;
+	u64 features;
+	const u32 *feature_table;
+	u32 feature_table_size;
+	const u32 *feature_table_legacy;
+	u32 feature_table_size_legacy;
+};
+
+/**
+ * virtio_get_config() - read the value of a configuration field
+ *
+ * @vdev:	the real virtio device
+ * @offset:	the offset of the configuration field
+ * @buf:	the buffer to write the field value into
+ * @len:	the length of the buffer
+ * @return 0 if OK, -ve on error
+ */
+int virtio_get_config(struct udevice *vdev, unsigned int offset,
+		      void *buf, unsigned int len);
+
+/**
+ * virtio_set_config() - write the value of a configuration field
+ *
+ * @vdev:	the real virtio device
+ * @offset:	the offset of the configuration field
+ * @buf:	the buffer to read the field value from
+ * @len:	the length of the buffer
+ * @return 0 if OK, -ve on error
+ */
+int virtio_set_config(struct udevice *vdev, unsigned int offset,
+		      void *buf, unsigned int len);
+
+/**
+ * virtio_generation() - config generation counter
+ *
+ * @vdev:	the real virtio device
+ * @counter:	the returned config generation counter
+ * @return 0 if OK, -ve on error
+ */
+int virtio_generation(struct udevice *vdev, u32 *counter);
+
+/**
+ * virtio_get_status() - read the status byte
+ *
+ * @vdev:	the real virtio device
+ * @status:	the returned status byte
+ * @return 0 if OK, -ve on error
+ */
+int virtio_get_status(struct udevice *vdev, u8 *status);
+
+/**
+ * virtio_set_status() - write the status byte
+ *
+ * @vdev:	the real virtio device
+ * @status:	the new status byte
+ * @return 0 if OK, -ve on error
+ */
+int virtio_set_status(struct udevice *vdev, u8 status);
+
+/**
+ * virtio_reset() - reset the device
+ *
+ * @vdev:	the real virtio device
+ * @return 0 if OK, -ve on error
+ */
+int virtio_reset(struct udevice *vdev);
+
+/**
+ * virtio_get_features() - get the array of feature bits for this device
+ *
+ * @vdev:	the real virtio device
+ * @features:	the first 32 feature bits (all we currently need)
+ * @return 0 if OK, -ve on error
+ */
+int virtio_get_features(struct udevice *vdev, u64 *features);
+
+/**
+ * virtio_set_features() - confirm what device features we'll be using
+ *
+ * @vdev:	the real virtio device
+ * @return 0 if OK, -ve on error
+ */
+int virtio_set_features(struct udevice *vdev);
+
+/**
+ * virtio_find_vqs() - find virtqueues and instantiate them
+ *
+ * @vdev:	the real virtio device
+ * @nvqs:	the number of virtqueues to find
+ * @vqs:	on success, includes new virtqueues
+ * @return 0 if OK, -ve on error
+ */
+int virtio_find_vqs(struct udevice *vdev, unsigned int nvqs,
+		    struct virtqueue *vqs[]);
+
+/**
+ * virtio_del_vqs() - free virtqueues found by find_vqs()
+ *
+ * @vdev:	the real virtio device
+ * @return 0 if OK, -ve on error
+ */
+int virtio_del_vqs(struct udevice *vdev);
+
+/**
+ * virtio_notify() - notify the device to process the queue
+ *
+ * @vdev:	the real virtio device
+ * @vq:		virtqueue to process
+ * @return 0 if OK, -ve on error
+ */
+int virtio_notify(struct udevice *vdev, struct virtqueue *vq);
+
+/**
+ * virtio_add_status() - helper to set a new status code to the device
+ *
+ * @vdev:	the real virtio device
+ * @status:	new status code to be added
+ */
+void virtio_add_status(struct udevice *vdev, u8 status);
+
+/**
+ * virtio_finalize_features() - helper to finalize features
+ *
+ * @vdev:	the real virtio device
+ * @return 0 if OK, -ve on error
+ */
+int virtio_finalize_features(struct udevice *vdev);
+
+/**
+ * virtio_driver_features_init() - initialize driver supported features
+ *
+ * This fills in the virtio device parent per child private data with the given
+ * information, which contains driver supported features and legacy features.
+ *
+ * This API should be called in the virtio device driver's bind method, so that
+ * later virtio transport uclass driver can utilize the driver supplied features
+ * to negotiate with the device on the final supported features.
+ *
+ * @priv:		virtio uclass per device private data
+ * @feature:		an array of feature supported by the driver
+ * @feature_size:	number of entries in the feature table array
+ * @feature_legacy:	same as feature_table but working in legacy mode
+ * @feature_legacy_size:number of entries in feature table legacy array
+ */
+void virtio_driver_features_init(struct virtio_dev_priv *priv,
+				 const u32 *feature,
+				 u32 feature_size,
+				 const u32 *feature_legacy,
+				 u32 feature_legacy_size);
+
+/**
+ * virtio_init() - helper to enumerate all known virtio devices
+ *
+ * @return 0 if OK, -ve on error
+ */
+int virtio_init(void);
+
+static inline u16 __virtio16_to_cpu(bool little_endian, __virtio16 val)
+{
+	if (little_endian)
+		return le16_to_cpu((__force __le16)val);
+	else
+		return be16_to_cpu((__force __be16)val);
+}
+
+static inline __virtio16 __cpu_to_virtio16(bool little_endian, u16 val)
+{
+	if (little_endian)
+		return (__force __virtio16)cpu_to_le16(val);
+	else
+		return (__force __virtio16)cpu_to_be16(val);
+}
+
+static inline u32 __virtio32_to_cpu(bool little_endian, __virtio32 val)
+{
+	if (little_endian)
+		return le32_to_cpu((__force __le32)val);
+	else
+		return be32_to_cpu((__force __be32)val);
+}
+
+static inline __virtio32 __cpu_to_virtio32(bool little_endian, u32 val)
+{
+	if (little_endian)
+		return (__force __virtio32)cpu_to_le32(val);
+	else
+		return (__force __virtio32)cpu_to_be32(val);
+}
+
+static inline u64 __virtio64_to_cpu(bool little_endian, __virtio64 val)
+{
+	if (little_endian)
+		return le64_to_cpu((__force __le64)val);
+	else
+		return be64_to_cpu((__force __be64)val);
+}
+
+static inline __virtio64 __cpu_to_virtio64(bool little_endian, u64 val)
+{
+	if (little_endian)
+		return (__force __virtio64)cpu_to_le64(val);
+	else
+		return (__force __virtio64)cpu_to_be64(val);
+}
+
+/**
+ * __virtio_test_bit - helper to test feature bits
+ *
+ * For use by transports. Devices should normally use virtio_has_feature,
+ * which includes more checks.
+ *
+ * @udev: the transport device
+ * @fbit: the feature bit
+ */
+static inline bool __virtio_test_bit(struct udevice *udev, unsigned int fbit)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* Did you forget to fix assumptions on max features? */
+	if (__builtin_constant_p(fbit))
+		BUILD_BUG_ON(fbit >= 64);
+	else
+		WARN_ON(fbit >= 64);
+
+	return uc_priv->features & BIT_ULL(fbit);
+}
+
+/**
+ * __virtio_set_bit - helper to set feature bits
+ *
+ * For use by transports.
+ *
+ * @udev: the transport device
+ * @fbit: the feature bit
+ */
+static inline void __virtio_set_bit(struct udevice *udev, unsigned int fbit)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* Did you forget to fix assumptions on max features? */
+	if (__builtin_constant_p(fbit))
+		BUILD_BUG_ON(fbit >= 64);
+	else
+		WARN_ON(fbit >= 64);
+
+	uc_priv->features |= BIT_ULL(fbit);
+}
+
+/**
+ * __virtio_clear_bit - helper to clear feature bits
+ *
+ * For use by transports.
+ *
+ * @vdev: the transport device
+ * @fbit: the feature bit
+ */
+static inline void __virtio_clear_bit(struct udevice *udev, unsigned int fbit)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+	/* Did you forget to fix assumptions on max features? */
+	if (__builtin_constant_p(fbit))
+		BUILD_BUG_ON(fbit >= 64);
+	else
+		WARN_ON(fbit >= 64);
+
+	uc_priv->features &= ~BIT_ULL(fbit);
+}
+
+/**
+ * virtio_has_feature - helper to determine if this device has this feature
+ *
+ * Note this API is only usable after the virtio device driver's bind phase,
+ * as the feature has been negotiated between the device and the driver.
+ *
+ * @vdev: the virtio device
+ * @fbit: the feature bit
+ */
+static inline bool virtio_has_feature(struct udevice *vdev, unsigned int fbit)
+{
+	if (!(vdev->flags & DM_FLAG_BOUND))
+		WARN_ON(true);
+
+	return __virtio_test_bit(vdev->parent, fbit);
+}
+
+static inline bool virtio_legacy_is_little_endian(void)
+{
+#ifdef __LITTLE_ENDIAN
+	return true;
+#else
+	return false;
+#endif
+}
+
+static inline bool virtio_is_little_endian(struct udevice *vdev)
+{
+	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
+
+	return !uc_priv->legacy || virtio_legacy_is_little_endian();
+}
+
+/* Memory accessors */
+static inline u16 virtio16_to_cpu(struct udevice *vdev, __virtio16 val)
+{
+	return __virtio16_to_cpu(virtio_is_little_endian(vdev), val);
+}
+
+static inline __virtio16 cpu_to_virtio16(struct udevice *vdev, u16 val)
+{
+	return __cpu_to_virtio16(virtio_is_little_endian(vdev), val);
+}
+
+static inline u32 virtio32_to_cpu(struct udevice *vdev, __virtio32 val)
+{
+	return __virtio32_to_cpu(virtio_is_little_endian(vdev), val);
+}
+
+static inline __virtio32 cpu_to_virtio32(struct udevice *vdev, u32 val)
+{
+	return __cpu_to_virtio32(virtio_is_little_endian(vdev), val);
+}
+
+static inline u64 virtio64_to_cpu(struct udevice *vdev, __virtio64 val)
+{
+	return __virtio64_to_cpu(virtio_is_little_endian(vdev), val);
+}
+
+static inline __virtio64 cpu_to_virtio64(struct udevice *vdev, u64 val)
+{
+	return __cpu_to_virtio64(virtio_is_little_endian(vdev), val);
+}
+
+/* Read @count fields, @bytes each */
+static inline void __virtio_cread_many(struct udevice *vdev,
+				       unsigned int offset,
+				       void *buf, size_t count, size_t bytes)
+{
+	u32 old, gen;
+	int i;
+
+	/* no need to check return value as generation can be optional */
+	virtio_generation(vdev, &gen);
+	do {
+		old = gen;
+
+		for (i = 0; i < count; i++)
+			virtio_get_config(vdev, offset + bytes * i,
+					  buf + i * bytes, bytes);
+
+		virtio_generation(vdev, &gen);
+	} while (gen != old);
+}
+
+static inline void virtio_cread_bytes(struct udevice *vdev,
+				      unsigned int offset,
+				      void *buf, size_t len)
+{
+	__virtio_cread_many(vdev, offset, buf, len, 1);
+}
+
+static inline u8 virtio_cread8(struct udevice *vdev, unsigned int offset)
+{
+	u8 ret;
+
+	virtio_get_config(vdev, offset, &ret, sizeof(ret));
+	return ret;
+}
+
+static inline void virtio_cwrite8(struct udevice *vdev,
+				  unsigned int offset, u8 val)
+{
+	virtio_set_config(vdev, offset, &val, sizeof(val));
+}
+
+static inline u16 virtio_cread16(struct udevice *vdev,
+				 unsigned int offset)
+{
+	u16 ret;
+
+	virtio_get_config(vdev, offset, &ret, sizeof(ret));
+	return virtio16_to_cpu(vdev, (__force __virtio16)ret);
+}
+
+static inline void virtio_cwrite16(struct udevice *vdev,
+				   unsigned int offset, u16 val)
+{
+	val = (__force u16)cpu_to_virtio16(vdev, val);
+	virtio_set_config(vdev, offset, &val, sizeof(val));
+}
+
+static inline u32 virtio_cread32(struct udevice *vdev,
+				 unsigned int offset)
+{
+	u32 ret;
+
+	virtio_get_config(vdev, offset, &ret, sizeof(ret));
+	return virtio32_to_cpu(vdev, (__force __virtio32)ret);
+}
+
+static inline void virtio_cwrite32(struct udevice *vdev,
+				   unsigned int offset, u32 val)
+{
+	val = (__force u32)cpu_to_virtio32(vdev, val);
+	virtio_set_config(vdev, offset, &val, sizeof(val));
+}
+
+static inline u64 virtio_cread64(struct udevice *vdev,
+				 unsigned int offset)
+{
+	u64 ret;
+
+	__virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret));
+	return virtio64_to_cpu(vdev, (__force __virtio64)ret);
+}
+
+static inline void virtio_cwrite64(struct udevice *vdev,
+				   unsigned int offset, u64 val)
+{
+	val = (__force u64)cpu_to_virtio64(vdev, val);
+	virtio_set_config(vdev, offset, &val, sizeof(val));
+}
+
+/* Config space read accessor */
+#define virtio_cread(vdev, structname, member, ptr)			\
+	do {								\
+		/* Must match the member's type, and be integer */	\
+		if (!typecheck(typeof((((structname *)0)->member)), *(ptr))) \
+			(*ptr) = 1;					\
+									\
+		switch (sizeof(*ptr)) {					\
+		case 1:							\
+			*(ptr) = virtio_cread8(vdev,			\
+					       offsetof(structname, member)); \
+			break;						\
+		case 2:							\
+			*(ptr) = virtio_cread16(vdev,			\
+						offsetof(structname, member)); \
+			break;						\
+		case 4:							\
+			*(ptr) = virtio_cread32(vdev,			\
+						offsetof(structname, member)); \
+			break;						\
+		case 8:							\
+			*(ptr) = virtio_cread64(vdev,			\
+						offsetof(structname, member)); \
+			break;						\
+		default:						\
+			WARN_ON(true);					\
+		}							\
+	} while (0)
+
+/* Config space write accessor */
+#define virtio_cwrite(vdev, structname, member, ptr)			\
+	do {								\
+		/* Must match the member's type, and be integer */	\
+		if (!typecheck(typeof((((structname *)0)->member)), *(ptr))) \
+			WARN_ON((*ptr) == 1);				\
+									\
+		switch (sizeof(*ptr)) {					\
+		case 1:							\
+			virtio_cwrite8(vdev,				\
+				       offsetof(structname, member),	\
+				       *(ptr));				\
+			break;						\
+		case 2:							\
+			virtio_cwrite16(vdev,				\
+					offsetof(structname, member),	\
+					*(ptr));			\
+			break;						\
+		case 4:							\
+			virtio_cwrite32(vdev,				\
+					offsetof(structname, member),	\
+					*(ptr));			\
+			break;						\
+		case 8:							\
+			virtio_cwrite64(vdev,				\
+					offsetof(structname, member),	\
+					*(ptr));			\
+			break;						\
+		default:						\
+			WARN_ON(true);					\
+		}							\
+	} while (0)
+
+/* Conditional config space accessors */
+#define virtio_cread_feature(vdev, fbit, structname, member, ptr)	\
+	({								\
+		int _r = 0;						\
+		if (!virtio_has_feature(vdev, fbit))			\
+			_r = -ENOENT;					\
+		else							\
+			virtio_cread(vdev, structname, member, ptr);	\
+		_r;							\
+	})
+
+#endif /* __VIRTIO_H__ */
diff --git a/include/virtio_ring.h b/include/virtio_ring.h
new file mode 100644
index 0000000000000000000000000000000000000000..6fc0593b14b7b36f02d588270247097b1d288fd6
--- /dev/null
+++ b/include/virtio_ring.h
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_ring.h
+ */
+
+#ifndef _LINUX_VIRTIO_RING_H
+#define _LINUX_VIRTIO_RING_H
+
+#include <virtio_types.h>
+
+/* This marks a buffer as continuing via the next field */
+#define VRING_DESC_F_NEXT		1
+/* This marks a buffer as write-only (otherwise read-only) */
+#define VRING_DESC_F_WRITE		2
+/* This means the buffer contains a list of buffer descriptors */
+#define VRING_DESC_F_INDIRECT		4
+
+/*
+ * The Host uses this in used->flags to advise the Guest: don't kick me when
+ * you add a buffer. It's unreliable, so it's simply an optimization. Guest
+ * will still kick if it's out of buffers.
+ */
+#define VRING_USED_F_NO_NOTIFY		1
+
+/*
+ * The Guest uses this in avail->flags to advise the Host: don't interrupt me
+ * when you consume a buffer. It's unreliable, so it's simply an optimization.
+ */
+#define VRING_AVAIL_F_NO_INTERRUPT	1
+
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC	28
+
+/*
+ * The Guest publishes the used index for which it expects an interrupt
+ * at the end of the avail ring. Host should ignore the avail->flags field.
+ *
+ * The Host publishes the avail index for which it expects a kick
+ * at the end of the used ring. Guest should ignore the used->flags field.
+ */
+#define VIRTIO_RING_F_EVENT_IDX		29
+
+/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
+struct vring_desc {
+	/* Address (guest-physical) */
+	__virtio64 addr;
+	/* Length */
+	__virtio32 len;
+	/* The flags as indicated above */
+	__virtio16 flags;
+	/* We chain unused descriptors via this, too */
+	__virtio16 next;
+};
+
+struct vring_avail {
+	__virtio16 flags;
+	__virtio16 idx;
+	__virtio16 ring[];
+};
+
+struct vring_used_elem {
+	/* Index of start of used descriptor chain */
+	__virtio32 id;
+	/* Total length of the descriptor chain which was used (written to) */
+	__virtio32 len;
+};
+
+struct vring_used {
+	__virtio16 flags;
+	__virtio16 idx;
+	struct vring_used_elem ring[];
+};
+
+struct vring {
+	unsigned int num;
+	struct vring_desc *desc;
+	struct vring_avail *avail;
+	struct vring_used *used;
+};
+
+/**
+ * virtqueue - a queue to register buffers for sending or receiving.
+ *
+ * @list: the chain of virtqueues for this device
+ * @vdev: the virtio device this queue was created for
+ * @index: the zero-based ordinal number for this queue
+ * @num_free: number of elements we expect to be able to fit
+ * @vring: actual memory layout for this queue
+ * @event: host publishes avail event idx
+ * @free_head: head of free buffer list
+ * @num_added: number we've added since last sync
+ * @last_used_idx: last used index we've seen
+ * @avail_flags_shadow: last written value to avail->flags
+ * @avail_idx_shadow: last written value to avail->idx in guest byte order
+ */
+struct virtqueue {
+	struct list_head list;
+	struct udevice *vdev;
+	unsigned int index;
+	unsigned int num_free;
+	struct vring vring;
+	bool event;
+	unsigned int free_head;
+	unsigned int num_added;
+	u16 last_used_idx;
+	u16 avail_flags_shadow;
+	u16 avail_idx_shadow;
+};
+
+/*
+ * Alignment requirements for vring elements.
+ * When using pre-virtio 1.0 layout, these fall out naturally.
+ */
+#define VRING_AVAIL_ALIGN_SIZE		2
+#define VRING_USED_ALIGN_SIZE		4
+#define VRING_DESC_ALIGN_SIZE		16
+
+/*
+ * We publish the used event index at the end of the available ring,
+ * and vice versa. They are at the end for backwards compatibility.
+ */
+#define vring_used_event(vr)	((vr)->avail->ring[(vr)->num])
+#define vring_avail_event(vr)	(*(__virtio16 *)&(vr)->used->ring[(vr)->num])
+
+static inline void vring_init(struct vring *vr, unsigned int num, void *p,
+			      unsigned long align)
+{
+	vr->num = num;
+	vr->desc = p;
+	vr->avail = p + num * sizeof(struct vring_desc);
+	vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] +
+		   sizeof(__virtio16) + align - 1) & ~(align - 1));
+}
+
+static inline unsigned int vring_size(unsigned int num, unsigned long align)
+{
+	return ((sizeof(struct vring_desc) * num +
+		sizeof(__virtio16) * (3 + num)  + align - 1) & ~(align - 1)) +
+		sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+/*
+ * The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX.
+ * Assuming a given event_idx value from the other side, if we have just
+ * incremented index from old to new_idx, should we trigger an event?
+ */
+static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
+{
+	/*
+	 * Note: Xen has similar logic for notification hold-off
+	 * in include/xen/interface/io/ring.h with req_event and req_prod
+	 * corresponding to event_idx + 1 and new_idx respectively.
+	 * Note also that req_event and req_prod in Xen start at 1,
+	 * event indexes in virtio start at 0.
+	 */
+	return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
+}
+
+struct virtio_sg;
+
+/**
+ * virtqueue_add - expose buffers to other end
+ *
+ * @vq:		the struct virtqueue we're talking about
+ * @sgs:	array of terminated scatterlists
+ * @out_sgs:	the number of scatterlists readable by other side
+ * @in_sgs:	the number of scatterlists which are writable
+ *		(after readable ones)
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
+		  unsigned int out_sgs, unsigned int in_sgs);
+
+/**
+ * virtqueue_kick - update after add_buf
+ *
+ * @vq:		the struct virtqueue
+ *
+ * After one or more virtqueue_add() calls, invoke this to kick
+ * the other side.
+ *
+ * Caller must ensure we don't call this with other virtqueue
+ * operations at the same time (except where noted).
+ */
+void virtqueue_kick(struct virtqueue *vq);
+
+/**
+ * virtqueue_get_buf - get the next used buffer
+ *
+ * @vq:		the struct virtqueue we're talking about
+ * @len:	the length written into the buffer
+ *
+ * If the device wrote data into the buffer, @len will be set to the
+ * amount written. This means you don't need to clear the buffer
+ * beforehand to ensure there's no data leakage in the case of short
+ * writes.
+ *
+ * Caller must ensure we don't call this with other virtqueue
+ * operations at the same time (except where noted).
+ *
+ * Returns NULL if there are no used buffers, or the memory buffer
+ * handed to virtqueue_add_*().
+ */
+void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
+
+/**
+ * vring_create_virtqueue - create a virtqueue for a virtio device
+ *
+ * @index:	the index of the queue
+ * @num:	number of elements of the queue
+ * @vring_align:the alignment requirement of the descriptor ring
+ * @udev:	the virtio transport udevice
+ * @return: the virtqueue pointer or NULL if failed
+ *
+ * This creates a virtqueue and allocates the descriptor ring for a virtio
+ * device. The caller should query virtqueue_get_ring_size() to learn the
+ * actual size of the ring.
+ *
+ * This API is supposed to be called by the virtio transport driver in the
+ * virtio find_vqs() uclass method.
+ */
+struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
+					 unsigned int vring_align,
+					 struct udevice *udev);
+
+/**
+ * vring_del_virtqueue - destroy a virtqueue
+ *
+ * @vq:		the struct virtqueue we're talking about
+ *
+ * This destroys a virtqueue. If created with vring_create_virtqueue(),
+ * this also frees the descriptor ring.
+ *
+ * This API is supposed to be called by the virtio transport driver in the
+ * virtio del_vqs() uclass method.
+ */
+void vring_del_virtqueue(struct virtqueue *vq);
+
+/**
+ * virtqueue_get_vring_size - get the size of the virtqueue's vring
+ *
+ * @vq:		the struct virtqueue containing the vring of interest
+ * @return: the size of the vring in a virtqueue.
+ */
+unsigned int virtqueue_get_vring_size(struct virtqueue *vq);
+
+/**
+ * virtqueue_get_desc_addr - get the vring descriptor table address
+ *
+ * @vq:		the struct virtqueue containing the vring of interest
+ * @return: the descriptor table address of the vring in a virtqueue.
+ */
+ulong virtqueue_get_desc_addr(struct virtqueue *vq);
+
+/**
+ * virtqueue_get_avail_addr - get the vring available ring address
+ *
+ * @vq:		the struct virtqueue containing the vring of interest
+ * @return: the available ring address of the vring in a virtqueue.
+ */
+ulong virtqueue_get_avail_addr(struct virtqueue *vq);
+
+/**
+ * virtqueue_get_used_addr - get the vring used ring address
+ *
+ * @vq:		the struct virtqueue containing the vring of interest
+ * @return: the used ring address of the vring in a virtqueue.
+ */
+ulong virtqueue_get_used_addr(struct virtqueue *vq);
+
+/**
+ * virtqueue_poll - query pending used buffers
+ *
+ * @vq:			the struct virtqueue we're talking about
+ * @last_used_idx:	virtqueue last used index
+ *
+ * Returns "true" if there are pending used buffers in the queue.
+ */
+bool virtqueue_poll(struct virtqueue *vq, u16 last_used_idx);
+
+/**
+ * virtqueue_dump - dump the virtqueue for debugging
+ *
+ * @vq:		the struct virtqueue we're talking about
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ */
+void virtqueue_dump(struct virtqueue *vq);
+
+/*
+ * Barriers in virtio are tricky. Since we are not in a hyperviosr/guest
+ * scenario, having these as nops is enough to work as expected.
+ */
+
+static inline void virtio_mb(void)
+{
+}
+
+static inline void virtio_rmb(void)
+{
+}
+
+static inline void virtio_wmb(void)
+{
+}
+
+static inline void virtio_store_mb(__virtio16 *p, __virtio16 v)
+{
+	WRITE_ONCE(*p, v);
+}
+
+#endif /* _LINUX_VIRTIO_RING_H */
diff --git a/include/virtio_types.h b/include/virtio_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..d700d1936d725b67edbffc398f8528e47ad9a901
--- /dev/null
+++ b/include/virtio_types.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_types.h
+ */
+
+#ifndef _LINUX_VIRTIO_TYPES_H
+#define _LINUX_VIRTIO_TYPES_H
+
+#include <linux/types.h>
+
+/*
+ * __virtio{16,32,64} have the following meaning:
+ * - __u{16,32,64} for virtio devices in legacy mode, accessed in native endian
+ * - __le{16,32,64} for standard-compliant virtio devices
+ */
+
+typedef __u16 __bitwise __virtio16;
+typedef __u32 __bitwise __virtio32;
+typedef __u64 __bitwise __virtio64;
+
+#endif /* _LINUX_VIRTIO_TYPES_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index ccab426e12189299f4611d413b3ce7b0c4bf8a6e..847e797a3a4f7c1e0e7048829d38ccf1bd909997 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -21,6 +21,12 @@ config DYNAMIC_CRC_TABLE
 	  Enable this option to calculate entries for CRC tables at runtime.
 	  This can be helpful when reducing the size of the build image
 
+config HAVE_ARCH_IOMAP
+	bool
+	help
+	  Enable this option if architecture provides io{read,write}{8,16,32}
+	  I/O accessor functions.
+
 config HAVE_PRIVATE_LIBGCC
 	bool
 
diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c
index 5879d40386f60b8ea837f3fbe4ffd0d8cfa0b725..0047998ee0cfdfc7533ff5d40694bfcc9166c0a2 100644
--- a/lib/efi/efi_app.c
+++ b/lib/efi/efi_app.c
@@ -161,5 +161,4 @@ U_BOOT_DRIVER(efi_sysreset) = {
 	.id = UCLASS_SYSRESET,
 	.of_match = efi_sysreset_ids,
 	.ops = &efi_sysreset_ops,
-	.flags = DM_FLAG_PRE_RELOC,
 };
diff --git a/lib/efi_driver/efi_block_device.c b/lib/efi_driver/efi_block_device.c
index 5b9c139f3892ea4b9af29a58d5c7333a04d158ed..3f147cf6087948db2951e40ee8c9592ef5aa10f4 100644
--- a/lib/efi_driver/efi_block_device.c
+++ b/lib/efi_driver/efi_block_device.c
@@ -38,7 +38,7 @@
  * handle	handle of the controller on which this driver is installed
  * io		block io protocol proxied by this driver
  */
-struct efi_blk_priv {
+struct efi_blk_platdata {
 	efi_handle_t		handle;
 	struct efi_block_io	*io;
 };
@@ -55,8 +55,8 @@ struct efi_blk_priv {
 static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
 			 void *buffer)
 {
-	struct efi_blk_priv *priv = dev->priv;
-	struct efi_block_io *io = priv->io;
+	struct efi_blk_platdata *platdata = dev_get_platdata(dev);
+	struct efi_block_io *io = platdata->io;
 	efi_status_t ret;
 
 	EFI_PRINT("%s: read '%s', from block " LBAFU ", " LBAFU " blocks\n",
@@ -84,8 +84,8 @@ static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
 static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
 			  const void *buffer)
 {
-	struct efi_blk_priv *priv = dev->priv;
-	struct efi_block_io *io = priv->io;
+	struct efi_blk_platdata *platdata = dev_get_platdata(dev);
+	struct efi_block_io *io = platdata->io;
 	efi_status_t ret;
 
 	EFI_PRINT("%s: write '%s', from block " LBAFU ", " LBAFU " blocks\n",
@@ -135,7 +135,7 @@ static int efi_bl_bind(efi_handle_t handle, void *interface)
 	struct efi_object *obj = efi_search_obj(handle);
 	struct efi_block_io *io = interface;
 	int disks;
-	struct efi_blk_priv *priv;
+	struct efi_blk_platdata *platdata;
 
 	EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, io);
 
@@ -163,18 +163,16 @@ static int efi_bl_bind(efi_handle_t handle, void *interface)
 		return -ENOENT;
 	/* Set the DM_FLAG_NAME_ALLOCED flag to avoid a memory leak */
 	device_set_name_alloced(bdev);
-	/* Allocate priv */
+
+	platdata = dev_get_platdata(bdev);
+	platdata->handle = handle;
+	platdata->io = interface;
+
 	ret = device_probe(bdev);
 	if (ret)
 		return ret;
 	EFI_PRINT("%s: block device '%s' created\n", __func__, bdev->name);
 
-	priv = bdev->priv;
-	priv->handle = handle;
-	priv->io = interface;
-
-	ret = blk_prepare_device(bdev);
-
 	/* Create handles for the partions of the block device */
 	disks = efi_bl_bind_partitions(handle, bdev);
 	EFI_PRINT("Found %d partitions\n", disks);
@@ -193,7 +191,7 @@ U_BOOT_DRIVER(efi_blk) = {
 	.name			= "efi_blk",
 	.id			= UCLASS_BLK,
 	.ops			= &efi_blk_ops,
-	.priv_auto_alloc_size	= sizeof(struct efi_blk_priv),
+	.platdata_auto_alloc_size = sizeof(struct efi_blk_platdata),
 };
 
 /* EFI driver operators */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index b490cf28623ffe5c8992e434a09ed0da751e3f18..213e0fda946a0d521d9cdfed3ae1d6cabded6404 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -53,4 +53,5 @@ obj-$(CONFIG_MISC) += misc.o
 obj-$(CONFIG_DM_SERIAL) += serial.o
 obj-$(CONFIG_CPU) += cpu.o
 obj-$(CONFIG_TEE) += tee.o
+obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o
 endif
diff --git a/test/dm/blk.c b/test/dm/blk.c
index 4de477bafa3ad6378ecc31a93d0c938ca15ecd38..9c71adc69d914977a9cb0460980c949a4ae3af91 100644
--- a/test/dm/blk.c
+++ b/test/dm/blk.c
@@ -15,34 +15,29 @@ DECLARE_GLOBAL_DATA_PTR;
 /* Test that block devices can be created */
 static int dm_test_blk_base(struct unit_test_state *uts)
 {
-	struct udevice *blk, *usb_blk, *dev;
+	struct udevice *blk1, *blk3, *dev;
 
 	/* Make sure there are no block devices */
-	ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_BLK, 0, &blk));
+	ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_BLK, 0, &dev));
 
 	/* Create two, one the parent of the other */
 	ut_assertok(blk_create_device(gd->dm_root, "sandbox_host_blk", "test",
-				      IF_TYPE_HOST, 1, 512, 2, &blk));
-	ut_assertok(blk_create_device(blk, "usb_storage_blk", "test",
-				      IF_TYPE_USB, 3, 512, 2, &usb_blk));
+				      IF_TYPE_HOST, 1, 512, 2, &blk1));
+	ut_assertok(blk_create_device(blk1, "sandbox_host_blk", "test",
+				      IF_TYPE_HOST, 3, 512, 2, &blk3));
 
 	/* Check we can find them */
 	ut_asserteq(-ENODEV, blk_get_device(IF_TYPE_HOST, 0, &dev));
 	ut_assertok(blk_get_device(IF_TYPE_HOST, 1, &dev));
-	ut_asserteq_ptr(blk, dev);
-
-	ut_asserteq(-ENODEV, blk_get_device(IF_TYPE_USB, 0, &dev));
-	ut_assertok(blk_get_device(IF_TYPE_USB, 3, &dev));
-	ut_asserteq_ptr(usb_blk, dev);
+	ut_asserteq_ptr(blk1, dev);
+	ut_assertok(blk_get_device(IF_TYPE_HOST, 3, &dev));
+	ut_asserteq_ptr(blk3, dev);
 
 	/* Check we can iterate */
 	ut_assertok(blk_first_device(IF_TYPE_HOST, &dev));
-	ut_asserteq_ptr(blk, dev);
-	ut_asserteq(-ENODEV, blk_next_device(&dev));
-
-	ut_assertok(blk_first_device(IF_TYPE_USB, &dev));
-	ut_asserteq_ptr(usb_blk, dev);
-	ut_asserteq(-ENODEV, blk_next_device(&dev));
+	ut_asserteq_ptr(blk1, dev);
+	ut_assertok(blk_next_device(&dev));
+	ut_asserteq_ptr(blk3, dev);
 
 	return 0;
 }
diff --git a/test/dm/bus.c b/test/dm/bus.c
index 08137a2216a29babddd709a49b613d2e57df50c7..93f3acd4308e97691a45e1594db2c7d7ea43afb8 100644
--- a/test/dm/bus.c
+++ b/test/dm/bus.c
@@ -63,6 +63,15 @@ static int testbus_child_pre_probe_uclass(struct udevice *dev)
 	return 0;
 }
 
+static int testbus_child_post_probe_uclass(struct udevice *dev)
+{
+	struct dm_test_priv *priv = dev_get_priv(dev);
+
+	priv->uclass_postp++;
+
+	return 0;
+}
+
 static int testbus_child_post_remove(struct udevice *dev)
 {
 	struct dm_test_parent_data *parent_data = dev_get_parent_priv(dev);
@@ -102,12 +111,13 @@ UCLASS_DRIVER(testbus) = {
 	.id		= UCLASS_TEST_BUS,
 	.flags		= DM_UC_FLAG_SEQ_ALIAS,
 	.child_pre_probe = testbus_child_pre_probe_uclass,
+	.child_post_probe = testbus_child_post_probe_uclass,
 };
 
 /* Test that we can probe for children */
 static int dm_test_bus_children(struct unit_test_state *uts)
 {
-	int num_devices = 7;
+	int num_devices = 8;
 	struct udevice *bus;
 	struct uclass *uc;
 
@@ -547,3 +557,38 @@ static int dm_test_bus_child_pre_probe_uclass(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_bus_child_pre_probe_uclass,
 	DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/*
+ * Test that the bus' uclass' child_post_probe() is called after the
+ * device's probe() method
+ */
+static int dm_test_bus_child_post_probe_uclass(struct unit_test_state *uts)
+{
+	struct udevice *bus, *dev;
+	int child_count;
+
+	/*
+	 * See testfdt_drv_probe() which effectively initializes that
+	 * the uclass postp flag is set to a value
+	 */
+	ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+	for (device_find_first_child(bus, &dev), child_count = 0;
+	     dev;
+	     device_find_next_child(&dev)) {
+		struct dm_test_priv *priv = dev_get_priv(dev);
+
+		/* Check that things happened in the right order */
+		ut_asserteq_ptr(NULL, priv);
+		ut_assertok(device_probe(dev));
+
+		priv = dev_get_priv(dev);
+		ut_assert(priv != NULL);
+		ut_asserteq(0, priv->uclass_postp);
+		child_count++;
+	}
+	ut_asserteq(3, child_count);
+
+	return 0;
+}
+DM_TEST(dm_test_bus_child_post_probe_uclass,
+	DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/pci.c b/test/dm/pci.c
index a1dedd84a74b18922ba511b8a8803de2ca25aba1..a1febd54b7f01bebb982660132a601e1adcc8336 100644
--- a/test/dm/pci.c
+++ b/test/dm/pci.c
@@ -211,6 +211,16 @@ static int dm_test_pci_cap(struct unit_test_state *uts)
 	cap = dm_pci_find_capability(swap, PCI_CAP_ID_PCIX);
 	ut_asserteq(0, cap);
 
+	/* look up PCI_CAP_ID_MSIX starting from PCI_CAP_ID_PM_OFFSET */
+	cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_PM_OFFSET,
+					  PCI_CAP_ID_MSIX);
+	ut_asserteq(PCI_CAP_ID_MSIX_OFFSET, cap);
+
+	/* look up PCI_CAP_ID_VNDR starting from PCI_CAP_ID_EXP_OFFSET */
+	cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_EXP_OFFSET,
+					  PCI_CAP_ID_VNDR);
+	ut_asserteq(0, cap);
+
 	ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus));
 	ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap));
 
@@ -222,6 +232,16 @@ static int dm_test_pci_cap(struct unit_test_state *uts)
 	cap = dm_pci_find_ext_capability(swap, PCI_EXT_CAP_ID_SRIOV);
 	ut_asserteq(0, cap);
 
+	/* look up PCI_EXT_CAP_ID_DSN starting from PCI_EXT_CAP_ID_ERR_OFFSET */
+	cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_ERR_OFFSET,
+					      PCI_EXT_CAP_ID_DSN);
+	ut_asserteq(PCI_EXT_CAP_ID_DSN_OFFSET, cap);
+
+	/* look up PCI_EXT_CAP_ID_RCRB starting from PCI_EXT_CAP_ID_VC_OFFSET */
+	cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_VC_OFFSET,
+					      PCI_EXT_CAP_ID_RCRB);
+	ut_asserteq(0, cap);
+
 	return 0;
 }
 DM_TEST(dm_test_pci_cap, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/regmap.c b/test/dm/regmap.c
index d4b86b3b03c2a61bd5b55100cf58f66fe0961f74..a8d7e6829eca0c53adf580e1406bc07080548e96 100644
--- a/test/dm/regmap.c
+++ b/test/dm/regmap.c
@@ -25,7 +25,7 @@ static int dm_test_regmap_base(struct unit_test_state *uts)
 	ut_assertok_ptr(map);
 	ut_asserteq(1, map->range_count);
 	ut_asserteq(0x10, map->ranges[0].start);
-	ut_asserteq(4, map->ranges[0].size);
+	ut_asserteq(16, map->ranges[0].size);
 	ut_asserteq(0x10, map_to_sysmem(regmap_get_range(map, 0)));
 
 	ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev));
@@ -116,3 +116,31 @@ static int dm_test_regmap_rw(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_regmap_rw, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Get/Set test */
+static int dm_test_regmap_getset(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct regmap *map;
+	uint reg;
+	struct layout {
+		u32 val0;
+		u32 val1;
+		u32 val2;
+		u32 val3;
+	};
+
+	ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+	map = syscon_get_regmap(dev);
+	ut_assertok_ptr(map);
+
+	regmap_set(map, struct layout, val0, 0xcacafafa);
+	regmap_set(map, struct layout, val3, 0x55aa2211);
+
+	ut_assertok(regmap_get(map, struct layout, val0, &reg));
+	ut_assertok(regmap_get(map, struct layout, val3, &reg));
+
+	return 0;
+}
+
+DM_TEST(dm_test_regmap_getset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index 79b1f1de452dde1ac23f2e0a2779995eec884142..e43acb21d5e710043be167ace4f3382682ddd860 100644
--- a/test/dm/test-fdt.c
+++ b/test/dm/test-fdt.c
@@ -55,10 +55,13 @@ static int testfdt_drv_probe(struct udevice *dev)
 
 	/*
 	 * If this device is on a bus, the uclass_flag will be set before
-	 * calling this function. This is used by
-	 * dm_test_bus_child_pre_probe_uclass().
+	 * calling this function. In the meantime the uclass_postp is
+	 * initlized to a value -1. These are used respectively by
+	 * dm_test_bus_child_pre_probe_uclass() and
+	 * dm_test_bus_child_post_probe_uclass().
 	 */
 	priv->uclass_total += priv->uclass_flag;
+	priv->uclass_postp = -1;
 
 	return 0;
 }
@@ -84,6 +87,25 @@ U_BOOT_DRIVER(testfdt_drv) = {
 	.platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
 };
 
+static const struct udevice_id testfdt1_ids[] = {
+	{
+		.compatible = "denx,u-boot-fdt-test1",
+		.data = DM_TEST_TYPE_FIRST },
+	{ }
+};
+
+U_BOOT_DRIVER(testfdt1_drv) = {
+	.name	= "testfdt1_drv",
+	.of_match	= testfdt1_ids,
+	.id	= UCLASS_TEST_FDT,
+	.ofdata_to_platdata = testfdt_ofdata_to_platdata,
+	.probe	= testfdt_drv_probe,
+	.ops	= &test_ops,
+	.priv_auto_alloc_size = sizeof(struct dm_test_priv),
+	.platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
 /* From here is the testfdt uclass code */
 int testfdt_ping(struct udevice *dev, int pingval, int *pingret)
 {
@@ -168,7 +190,7 @@ int dm_check_devices(struct unit_test_state *uts, int num_devices)
 /* Test that FDT-based binding works correctly */
 static int dm_test_fdt(struct unit_test_state *uts)
 {
-	const int num_devices = 7;
+	const int num_devices = 8;
 	struct udevice *dev;
 	struct uclass *uc;
 	int ret;
@@ -208,8 +230,12 @@ static int dm_test_fdt_pre_reloc(struct unit_test_state *uts)
 	ret = uclass_get(UCLASS_TEST_FDT, &uc);
 	ut_assert(!ret);
 
-	/* These is only one pre-reloc device */
-	ut_asserteq(1, list_count_items(&uc->dev_head));
+	/*
+	 * These are 2 pre-reloc devices:
+	 * one with "u-boot,dm-pre-reloc" property (a-test node), and the other
+	 * one whose driver marked with DM_FLAG_PRE_RELOC flag (h-test node).
+	 */
+	ut_asserteq(2, list_count_items(&uc->dev_head));
 
 	return 0;
 }
diff --git a/test/dm/virtio.c b/test/dm/virtio.c
new file mode 100644
index 0000000000000000000000000000000000000000..4b317d2ec3b328ea580fc0530ab7a4ff9fbc8a70
--- /dev/null
+++ b/test/dm/virtio.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Basic test of the virtio uclass */
+static int dm_test_virtio_base(struct unit_test_state *uts)
+{
+	struct udevice *bus, *dev;
+	u8 status;
+
+	/* check probe success */
+	ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus));
+
+	/* check the child virtio-blk device is bound */
+	ut_assertok(device_find_first_child(bus, &dev));
+	ut_assertok(strcmp(dev->name, "virtio-blk#0"));
+
+	/* check driver status */
+	ut_assertok(virtio_get_status(dev, &status));
+	ut_asserteq(VIRTIO_CONFIG_S_ACKNOWLEDGE, status);
+
+	return 0;
+}
+DM_TEST(dm_test_virtio_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test all of the virtio uclass ops */
+static int dm_test_virtio_all_ops(struct unit_test_state *uts)
+{
+	struct udevice *bus, *dev;
+	struct virtio_dev_priv *uc_priv;
+	uint offset = 0, len = 0, nvqs = 1;
+	void *buffer = NULL;
+	u8 status;
+	u32 counter;
+	u64 features;
+	struct virtqueue *vqs[2];
+
+	/* check probe success */
+	ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus));
+
+	/* check the child virtio-blk device is bound */
+	ut_assertok(device_find_first_child(bus, &dev));
+
+	/*
+	 * fake the virtio device probe by filling in uc_priv->vdev
+	 * which is used by virtio_find_vqs/virtio_del_vqs.
+	 */
+	uc_priv = dev_get_uclass_priv(bus);
+	uc_priv->vdev = dev;
+
+	/* test virtio_xxx APIs */
+	ut_assertok(virtio_get_config(dev, offset, buffer, len));
+	ut_assertok(virtio_set_config(dev, offset, buffer, len));
+	ut_asserteq(-ENOSYS, virtio_generation(dev, &counter));
+	ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK));
+	ut_assertok(virtio_get_status(dev, &status));
+	ut_asserteq(VIRTIO_CONFIG_S_DRIVER_OK, status);
+	ut_assertok(virtio_reset(dev));
+	ut_assertok(virtio_get_status(dev, &status));
+	ut_asserteq(0, status);
+	ut_assertok(virtio_get_features(dev, &features));
+	ut_asserteq(VIRTIO_F_VERSION_1, features);
+	ut_assertok(virtio_set_features(dev));
+	ut_assertok(virtio_find_vqs(dev, nvqs, vqs));
+	ut_assertok(virtio_del_vqs(dev));
+	ut_assertok(virtio_notify(dev, vqs[0]));
+
+	return 0;
+}
+DM_TEST(dm_test_virtio_all_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of the virtio driver that does not have required driver ops */
+static int dm_test_virtio_missing_ops(struct unit_test_state *uts)
+{
+	struct udevice *bus;
+
+	/* find the virtio device */
+	ut_assertok(uclass_find_device(UCLASS_VIRTIO, 1, &bus));
+
+	/*
+	 * Probe the device should fail with error -ENOENT.
+	 * See ops check in virtio_uclass_pre_probe().
+	 */
+	ut_asserteq(-ENOENT, device_probe(bus));
+
+	return 0;
+}
+DM_TEST(dm_test_virtio_missing_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test removal of virtio device driver */
+static int dm_test_virtio_remove(struct unit_test_state *uts)
+{
+	struct udevice *bus, *dev;
+
+	/* check probe success */
+	ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus));
+
+	/* check the child virtio-blk device is bound */
+	ut_assertok(device_find_first_child(bus, &dev));
+
+	/* set driver status to VIRTIO_CONFIG_S_DRIVER_OK */
+	ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK));
+
+	/* check the device can be successfully removed */
+	dev->flags |= DM_FLAG_ACTIVATED;
+	ut_assertok(device_remove(bus, DM_REMOVE_ACTIVE_ALL));
+
+	return 0;
+}
+DM_TEST(dm_test_virtio_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/tools/buildman/README b/tools/buildman/README
index 76601902cb2efc87f3d3ec2717805e380410379e..5a709c6ff9e3b5ea270b1890dd3d02e0e1b87b20 100644
--- a/tools/buildman/README
+++ b/tools/buildman/README
@@ -114,6 +114,10 @@ a few commits or boards, it will be pretty slow. As a tip, if you don't
 plan to use your machine for anything else, you can use -T to increase the
 number of threads beyond the default.
 
+
+Selecting which boards to build
+===============================
+
 Buildman lets you build all boards, or a subset. Specify the subset by passing
 command-line arguments that list the desired board name, architecture name,
 SOC name, or anything else in the boards.cfg file. Multiple arguments are
@@ -138,11 +142,17 @@ You can also use -x to specifically exclude some boards. For example:
 means to build all arm boards except nvidia, freescale and anything ending
 with 'ball'.
 
+For building specific boards you can use the --boards option, which takes a
+comma-separated list of board target names and be used multiple times on
+the command line:
+
+   buidman --boards sandbox,snow --boards
+
 It is convenient to use the -n option to see what will be built based on
 the subset given. Use -v as well to get an actual list of boards.
 
 Buildman does not store intermediate object files. It optionally copies
-the binary output into a directory when a build is successful. Size
+the binary output into a directory when a build is successful (-k). Size
 information is always recorded. It needs a fair bit of disk space to work,
 typically 250MB per thread.
 
diff --git a/tools/buildman/board.py b/tools/buildman/board.py
index 272bac0c21c47a5678240cc980006ae118f22228..2a1d021574c1396098dafd41689e62311aa9f289 100644
--- a/tools/buildman/board.py
+++ b/tools/buildman/board.py
@@ -237,20 +237,30 @@ class Boards:
             terms.append(term)
         return terms
 
-    def SelectBoards(self, args, exclude=[]):
+    def SelectBoards(self, args, exclude=[], boards=None):
         """Mark boards selected based on args
 
+        Normally either boards (an explicit list of boards) or args (a list of
+        terms to match against) is used. It is possible to specify both, in
+        which case they are additive.
+
+        If boards and args are both empty, all boards are selected.
+
         Args:
             args: List of strings specifying boards to include, either named,
                   or by their target, architecture, cpu, vendor or soc. If
                   empty, all boards are selected.
             exclude: List of boards to exclude, regardless of 'args'
+            boards: List of boards to build
 
         Returns:
-            Dictionary which holds the list of boards which were selected
-            due to each argument, arranged by argument.
+            Tuple
+                Dictionary which holds the list of boards which were selected
+                    due to each argument, arranged by argument.
+                List of errors found
         """
         result = {}
+        warnings = []
         terms = self._BuildTerms(args)
 
         result['all'] = []
@@ -261,6 +271,7 @@ class Boards:
         for expr in exclude:
             exclude_list.append(Expr(expr))
 
+        found = []
         for board in self._boards:
             matching_term = None
             build_it = False
@@ -271,6 +282,10 @@ class Boards:
                         matching_term = str(term)
                         build_it = True
                         break
+            elif boards:
+                if board.target in boards:
+                    build_it = True
+                    found.append(board.target)
             else:
                 build_it = True
 
@@ -286,4 +301,9 @@ class Boards:
                     result[matching_term].append(board.target)
                 result['all'].append(board.target)
 
-        return result
+        if boards:
+            remaining = set(boards) - set(found)
+            if remaining:
+                warnings.append('Boards not found: %s\n' % ', '.join(remaining))
+
+        return result, warnings
diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py
index e493b1ac4a006fac107a3026e1d5b5db7ab1abda..49a8a131182bdf6a8d358255f45b897a61c9e415 100644
--- a/tools/buildman/cmdline.py
+++ b/tools/buildman/cmdline.py
@@ -18,6 +18,8 @@ def ParseArgs():
     parser.add_option('-B', '--bloat', dest='show_bloat',
           action='store_true', default=False,
           help='Show changes in function code size for each board')
+    parser.add_option('--boards', type='string', action='append',
+          help='List of board names to build separated by comma')
     parser.add_option('-c', '--count', dest='count', type='int',
           default=-1, help='Run build on the top n commits')
     parser.add_option('-C', '--force-reconfig', dest='force_reconfig',
@@ -102,7 +104,7 @@ def ParseArgs():
           type='string', action='append',
           help='Specify a list of boards to exclude, separated by comma')
 
-    parser.usage += """
+    parser.usage += """ [list of target/arch/cpu/board/vendor/soc to build]
 
     Build U-Boot for all commits in a branch. Use -n to do a dry run"""
 
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index bc0819784f86147e2cecb2f3be474ee1ad8d3400..96f8ccfe070d78e410178743b471ad067459722c 100644
--- a/tools/buildman/control.py
+++ b/tools/buildman/control.py
@@ -41,7 +41,8 @@ def GetActionSummary(is_summary, commits, selected, options):
             GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
     return str
 
-def ShowActions(series, why_selected, boards_selected, builder, options):
+def ShowActions(series, why_selected, boards_selected, builder, options,
+                board_warnings):
     """Display a list of actions that we would take, if not a dry run.
 
     Args:
@@ -55,6 +56,7 @@ def ShowActions(series, why_selected, boards_selected, builder, options):
                 value is Board object
         builder: The builder that will be used to build the commits
         options: Command line options object
+        board_warnings: List of warnings obtained from board selected
     """
     col = terminal.Color()
     print 'Dry run, so not doing much. But I would do this:'
@@ -79,6 +81,9 @@ def ShowActions(series, why_selected, boards_selected, builder, options):
                 print '   %s' % ' '.join(why_selected[arg])
     print ('Total boards to build for each commit: %d\n' %
             len(why_selected['all']))
+    if board_warnings:
+        for warning in board_warnings:
+            print col.Color(col.YELLOW, warning)
 
 def CheckOutputDir(output_dir):
     """Make sure that the output directory is not within the current directory
@@ -210,7 +215,15 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         for arg in options.exclude:
             exclude += arg.split(',')
 
-    why_selected = boards.SelectBoards(args, exclude)
+
+    if options.boards:
+        requested_boards = []
+        for b in options.boards:
+            requested_boards += b.split(',')
+    else:
+        requested_boards = None
+    why_selected, board_warnings = boards.SelectBoards(args, exclude,
+                                                       requested_boards)
     selected = boards.GetSelected()
     if not len(selected):
         sys.exit(col.Color(col.RED, 'No matching boards found'))
@@ -292,7 +305,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
 
     # For a dry run, just show our actions as a sanity check
     if options.dry_run:
-        ShowActions(series, why_selected, selected, builder, options)
+        ShowActions(series, why_selected, selected, builder, options,
+                    board_warnings)
     else:
         builder.force_build = options.force_build
         builder.force_build_failures = options.force_build_failures
diff --git a/tools/buildman/test.py b/tools/buildman/test.py
index e0c9d6da6a01ff4690b1234b069a7737644df7a9..61a462655f23ad8e1bd4964915ff93dd9007ed1a 100644
--- a/tools/buildman/test.py
+++ b/tools/buildman/test.py
@@ -313,60 +313,63 @@ class TestBuild(unittest.TestCase):
     def testBoardSingle(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['sandbox']),
-                         {'all': ['board4'], 'sandbox': ['board4']})
+                         ({'all': ['board4'], 'sandbox': ['board4']}, []))
 
     def testBoardArch(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['arm']),
-                         {'all': ['board0', 'board1'],
-                          'arm': ['board0', 'board1']})
+                         ({'all': ['board0', 'board1'],
+                          'arm': ['board0', 'board1']}, []))
 
     def testBoardArchSingle(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['arm sandbox']),
-                         {'sandbox': ['board4'],
+                         ({'sandbox': ['board4'],
                           'all': ['board0', 'board1', 'board4'],
-                          'arm': ['board0', 'board1']})
+                          'arm': ['board0', 'board1']}, []))
 
 
     def testBoardArchSingleMultiWord(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['arm', 'sandbox']),
-                         {'sandbox': ['board4'], 'all': ['board0', 'board1', 'board4'], 'arm': ['board0', 'board1']})
+                         ({'sandbox': ['board4'],
+                          'all': ['board0', 'board1', 'board4'],
+                          'arm': ['board0', 'board1']}, []))
 
     def testBoardSingleAnd(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['Tester & arm']),
-                         {'Tester&arm': ['board0', 'board1'], 'all': ['board0', 'board1']})
+                         ({'Tester&arm': ['board0', 'board1'],
+                           'all': ['board0', 'board1']}, []))
 
     def testBoardTwoAnd(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['Tester', '&', 'arm',
                                                    'Tester' '&', 'powerpc',
                                                    'sandbox']),
-                         {'sandbox': ['board4'],
+                         ({'sandbox': ['board4'],
                           'all': ['board0', 'board1', 'board2', 'board3',
                                   'board4'],
                           'Tester&powerpc': ['board2', 'board3'],
-                          'Tester&arm': ['board0', 'board1']})
+                          'Tester&arm': ['board0', 'board1']}, []))
 
     def testBoardAll(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards([]),
-                         {'all': ['board0', 'board1', 'board2', 'board3',
-                                  'board4']})
+                         ({'all': ['board0', 'board1', 'board2', 'board3',
+                                  'board4']}, []))
 
     def testBoardRegularExpression(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['T.*r&^Po']),
-                         {'all': ['board2', 'board3'],
-                          'T.*r&^Po': ['board2', 'board3']})
+                         ({'all': ['board2', 'board3'],
+                          'T.*r&^Po': ['board2', 'board3']}, []))
 
     def testBoardDuplicate(self):
         """Test single board selection"""
         self.assertEqual(self.boards.SelectBoards(['sandbox sandbox',
                                                    'sandbox']),
-                         {'all': ['board4'], 'sandbox': ['board4']})
+                         ({'all': ['board4'], 'sandbox': ['board4']}, []))
     def CheckDirs(self, build, dirname):
         self.assertEqual('base%s' % dirname, build._GetOutputDir(1))
         self.assertEqual('base%s/fred' % dirname,