summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/misc/gdsys,iocon_fpga.txt19
-rw-r--r--Documentation/devicetree/bindings/misc/gdsys,iocpu_fpga.txt19
-rw-r--r--Documentation/devicetree/bindings/misc/gdsys,soc.txt16
-rw-r--r--arch/Kconfig6
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/mach-stm32mp/bsec.c1
-rw-r--r--arch/mips/include/asm/io.h22
-rw-r--r--arch/riscv/lib/bootm.c11
-rw-r--r--arch/sandbox/cpu/os.c7
-rw-r--r--arch/sandbox/dts/test.dts17
-rw-r--r--arch/x86/cpu/baytrail/cpu.c1
-rw-r--r--arch/x86/cpu/broadwell/cpu.c1
-rw-r--r--arch/x86/cpu/cpu_x86.c1
-rw-r--r--arch/x86/cpu/ivybridge/model_206ax.c1
-rw-r--r--arch/x86/cpu/tangier/sysreset.c1
-rw-r--r--arch/x86/include/asm/io.h66
-rw-r--r--board/emulation/qemu-arm/Kconfig13
-rw-r--r--board/emulation/qemu-arm/qemu-arm.c10
-rw-r--r--board/emulation/qemu-riscv/Kconfig11
-rw-r--r--board/emulation/qemu-riscv/qemu-riscv.c9
-rw-r--r--board/emulation/qemu-x86/Kconfig3
-rw-r--r--cmd/Kconfig7
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/sata.c9
-rw-r--r--cmd/virtio.c38
-rw-r--r--common/board_f.c28
-rw-r--r--common/usb_storage.c4
-rw-r--r--configs/imx8qxp_mek_defconfig1
-rw-r--r--configs/qemu_arm64_defconfig1
-rw-r--r--configs/qemu_arm_defconfig1
-rw-r--r--configs/sandbox_noblk_defconfig1
-rw-r--r--disk/part.c6
-rw-r--r--doc/README.virtio253
-rw-r--r--doc/driver-model/README.txt16
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/block/blk-uclass.c25
-rw-r--r--drivers/block/ide.c2
-rw-r--r--drivers/block/sandbox.c17
-rw-r--r--drivers/clk/altera/clk-arria10.c1
-rw-r--r--drivers/clk/clk_pic32.c1
-rw-r--r--drivers/clk/clk_zynq.c1
-rw-r--r--drivers/clk/exynos/clk-exynos7420.c3
-rw-r--r--drivers/clk/owl/clk_s900.c1
-rw-r--r--drivers/core/device.c2
-rw-r--r--drivers/core/dump.c2
-rw-r--r--drivers/core/lists.c9
-rw-r--r--drivers/core/ofnode.c4
-rw-r--r--drivers/core/regmap.c316
-rw-r--r--drivers/core/root.c20
-rw-r--r--drivers/core/uclass.c31
-rw-r--r--drivers/core/util.c25
-rw-r--r--drivers/cpu/mpc83xx_cpu.c2
-rw-r--r--drivers/gpio/omap_gpio.c2
-rw-r--r--drivers/gpio/stm32f7_gpio.c2
-rw-r--r--drivers/gpio/tegra186_gpio.c1
-rw-r--r--drivers/gpio/tegra_gpio.c1
-rw-r--r--drivers/i2c/omap24xx_i2c.c2
-rw-r--r--drivers/misc/Kconfig17
-rw-r--r--drivers/misc/Makefile63
-rw-r--r--drivers/misc/gdsys_soc.c74
-rw-r--r--drivers/misc/gdsys_soc.h23
-rw-r--r--drivers/misc/ihs_fpga.c867
-rw-r--r--drivers/misc/ihs_fpga.h49
-rw-r--r--drivers/misc/imx8/scu.c4
-rw-r--r--drivers/misc/swap_case.c9
-rw-r--r--drivers/mmc/mmc.c3
-rw-r--r--drivers/mmc/omap_hsmmc.c2
-rw-r--r--drivers/nvme/nvme.c1
-rw-r--r--drivers/pci/pci-uclass.c51
-rw-r--r--drivers/pinctrl/broadcom/pinctrl-bcm283x.c2
-rw-r--r--drivers/pinctrl/exynos/pinctrl-exynos7420.c1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx5.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx6.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx7.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx7ulp.c2
-rw-r--r--drivers/pinctrl/pinctrl-single.c1
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c2
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c2
-rw-r--r--drivers/ram/bmips_ram.c1
-rw-r--r--drivers/scsi/scsi.c1
-rw-r--r--drivers/serial/altera_jtag_uart.c1
-rw-r--r--drivers/serial/altera_uart.c1
-rw-r--r--drivers/serial/arm_dcc.c1
-rw-r--r--drivers/serial/atmel_usart.c2
-rw-r--r--drivers/serial/ns16550.c20
-rw-r--r--drivers/serial/serial-uclass.c2
-rw-r--r--drivers/serial/serial_ar933x.c1
-rw-r--r--drivers/serial/serial_arc.c1
-rw-r--r--drivers/serial/serial_bcm283x_mu.c2
-rw-r--r--drivers/serial/serial_bcm283x_pl011.c2
-rw-r--r--drivers/serial/serial_bcm6345.c1
-rw-r--r--drivers/serial/serial_efi.c1
-rw-r--r--drivers/serial/serial_intel_mid.c1
-rw-r--r--drivers/serial/serial_lpuart.c1
-rw-r--r--drivers/serial/serial_meson.c1
-rw-r--r--drivers/serial/serial_mvebu_a3700.c1
-rw-r--r--drivers/serial/serial_mxc.c2
-rw-r--r--drivers/serial/serial_omap.c2
-rw-r--r--drivers/serial/serial_owl.c1
-rw-r--r--drivers/serial/serial_pic32.c1
-rw-r--r--drivers/serial/serial_pl01x.c2
-rw-r--r--drivers/serial/serial_s5p.c1
-rw-r--r--drivers/serial/serial_sh.c2
-rw-r--r--drivers/serial/serial_sti_asc.c1
-rw-r--r--drivers/serial/serial_stm32.c2
-rw-r--r--drivers/serial/serial_xuartlite.c1
-rw-r--r--drivers/serial/serial_zynq.c1
-rw-r--r--drivers/sysreset/sysreset_x86.c1
-rw-r--r--drivers/timer/Kconfig110
-rw-r--r--drivers/timer/ag101p_timer.c1
-rw-r--r--drivers/timer/altera_timer.c1
-rw-r--r--drivers/timer/arc_timer.c1
-rw-r--r--drivers/timer/ast_timer.c1
-rw-r--r--drivers/timer/atcpit100_timer.c1
-rw-r--r--drivers/timer/atmel_pit_timer.c1
-rw-r--r--drivers/timer/cadence-ttc.c1
-rw-r--r--drivers/timer/dw-apb-timer.c1
-rw-r--r--drivers/timer/mpc83xx_timer.c1
-rw-r--r--drivers/timer/omap-timer.c1
-rw-r--r--drivers/timer/rockchip_timer.c1
-rw-r--r--drivers/timer/sti-timer.c1
-rw-r--r--drivers/timer/stm32_timer.c1
-rw-r--r--drivers/timer/timer-uclass.c2
-rw-r--r--drivers/timer/tsc_timer.c1
-rw-r--r--drivers/video/simplefb.c1
-rw-r--r--drivers/virtio/Kconfig62
-rw-r--r--drivers/virtio/Makefile11
-rw-r--r--drivers/virtio/virtio-uclass.c369
-rw-r--r--drivers/virtio/virtio_blk.c137
-rw-r--r--drivers/virtio/virtio_blk.h129
-rw-r--r--drivers/virtio/virtio_mmio.c413
-rw-r--r--drivers/virtio/virtio_mmio.h129
-rw-r--r--drivers/virtio/virtio_net.c239
-rw-r--r--drivers/virtio/virtio_net.h268
-rw-r--r--drivers/virtio/virtio_pci.h173
-rw-r--r--drivers/virtio/virtio_pci_legacy.c421
-rw-r--r--drivers/virtio/virtio_pci_modern.c609
-rw-r--r--drivers/virtio/virtio_ring.c358
-rw-r--r--drivers/virtio/virtio_sandbox.c233
-rw-r--r--drivers/watchdog/ast_wdt.c1
-rw-r--r--include/blk.h22
-rw-r--r--include/dm/device-internal.h4
-rw-r--r--include/dm/lists.h9
-rw-r--r--include/dm/root.h17
-rw-r--r--include/dm/test.h1
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/dm/uclass.h4
-rw-r--r--include/dm/util.h27
-rw-r--r--include/init.h7
-rw-r--r--include/linux/io.h4
-rw-r--r--include/pci.h48
-rw-r--r--include/regmap.h232
-rw-r--r--include/virtio.h707
-rw-r--r--include/virtio_ring.h320
-rw-r--r--include/virtio_types.h24
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/efi/efi_app.c1
-rw-r--r--lib/efi_driver/efi_block_device.c26
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/blk.c27
-rw-r--r--test/dm/bus.c47
-rw-r--r--test/dm/pci.c20
-rw-r--r--test/dm/regmap.c30
-rw-r--r--test/dm/test-fdt.c36
-rw-r--r--test/dm/virtio.c122
-rw-r--r--tools/buildman/README12
-rw-r--r--tools/buildman/board.py28
-rw-r--r--tools/buildman/cmdline.py4
-rw-r--r--tools/buildman/control.py20
-rw-r--r--tools/buildman/test.py31
171 files changed, 7486 insertions, 343 deletions
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 0000000000..acd466fdc6
--- /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 0000000000..819db22bf7
--- /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 0000000000..278e935b16
--- /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 1f2f407d64..9fdd2f7e66 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 2899a60793..f0e7fde137 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 0e152efc04..d087a31389 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 957442effd..7c40e415c7 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 a7a9fb921b..2b5ccce933 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 07e46471fe..325ded51d8 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 57e0dd7663..024aa7c512 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 56e98131d7..2eb917283b 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 02b3169cf5..232fa40eb5 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 2b6cc9f22d..1aaf851bb4 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 33e5c6263d..6edc3e233c 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 e762ee1b81..b03bc28f93 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 c05c6bf8a2..81def0afd3 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 0000000000..02ae4d9884
--- /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 812c90636d..e1f4709c4c 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 af23363fcf..37a80db6a9 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 041e716c9b..2730a288fb 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 41a27dd933..6d19299d8b 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 ad14c9ce71..d609f9d1c9 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 ac4830a692..12a1330b06 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 4f0c6e0137..6d62ba8f74 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 0000000000..b7082bc63a
--- /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 afafec5e4d..96503ff8d3 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 d92ebb6eb1..560d60538b 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 518f7259f2..58b4ca0861 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 e9e2819272..f4502c9e9f 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 04c9afdb02..acebdc5a4e 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 93b2240185..e71e2d3860 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 9e457a6e72..f30f9e9187 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 0000000000..d3652f2e2f
--- /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 36541630a2..07b120d512 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 927a2b87f6..4ac823d962 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 fb38b67541..4453c62ad3 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 facf52711c..65a766e586 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 38adb6a241..4b8a4eac17 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 0392437309..d3b1aaaba3 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 78102c760d..612a1718dc 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 fdf95a12da..b3ac0d5a92 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 d647e0a01e..482f0937cb 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 763567b17c..aa86c7ca44 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 2b39bb99af..a7c15d2812 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 5176aa3f86..47a697f3e5 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 d7cdb1475d..9068084404 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 a1677269d8..a1f828463e 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 b7b7ad3a62..d9b5280b2d 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 8e5c3bcf61..5ef0f71c8b 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 b54bf5bcdc..4ce55f9cc8 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 3113d6a56b..6cfcde8918 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 451d4766d0..27a6848703 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 31717afaec..7bc86bf9b2 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 555eba2662..0031415d03 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 4c0786fff8..b903dc46b3 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 1f0e8d51da..6626b5415a 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 302efddc27..a730f5c4fe 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 54bf35e552..51f923752c 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 c5697011f2..48febc47d2 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 759d2c791b..302d441592 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 0000000000..94a21e08af
--- /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 0000000000..088d3b6523
--- /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 0000000000..f9e4b27a27
--- /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 0000000000..efb5dabb9c
--- /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 0647ddf103..b824ac79e6 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 bffb809f14..fa608cec1b 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 585951cd78..d6b9cdc992 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 ec853d063f..5cb97eb02a 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 eb6fdeda50..1ee0a0aefb 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 da49c96ed5..0c52337f33 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 891b4c25fd..3be080d29e 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 cb5975b252..ff6d6c4143 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 5d17380919..4e831b6f39 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 e63ecbdee6..d7c95bb738 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 769d428dda..8776fd9650 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 598bbfaf35..d778f82aac 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 d80c6eda70..9dec88c1aa 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 a1da90baa4..eb5978a166 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 0ba2052b29..685d8be80c 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 b5f19c983c..3e1dd9e241 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 bc6ac8cd32..df47e2fc78 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 61052a92d6..86c3de4e45 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 b7b0a13ca1..67d47199aa 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 43e8691a93..dfcb6fd698 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 9414f5f692..aa8cdff840 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 f9041aa626..1e6fc6c668 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 665cca85cb..3ded62732d 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 e91a5f7b24..5249c55398 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 925f0c2555..980b38d2a1 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 1f87f0cb28..bd1d89ec83 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 54fc9b5b39..2527bb8b1c 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 ee5d561bfd..a0e709a11e 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 1b54d1880f..dd3e511fc9 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 bdb5adb2a4..39bd40e68b 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 6106c1f9ec..a357b00d28 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 dbb853735f..b3dad77aa2 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 ce26d2bd15..7e4cd6c4b4 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 e586c18cf0..7e4e6d36b8 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 af3c755f96..ee6ad9c9e5 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 6fd97e2502..7ead73e6b7 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 ba73978e25..84600b1201 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 2a5f256184..12512f6578 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 faea6d4c99..e3160cf1bd 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 b1534981f8..c934d5f25a 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 5dfc6171eb..c972f1e9af 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 66e02d5689..31b43ee88d 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 cead3c62f5..1be777bd3b 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 f689015b4a..7e486a68ff 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 5943a63854..20b958cfd4 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 d012cf71a9..d0cfc35306 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 6e1ae68b32..6e20b4fc33 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 bc76819674..6f504f7cc4 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 cf9671ebbe..8c574ec5af 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 9973506f6b..21ffdbf575 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 f650c1bf66..c5d43b4a4a 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 603563d3de..009af2f929 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 4125a078b3..75263c5375 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 031f429acb..085bfb02c5 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 84a9ab072a..8e541109d4 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 f10df69092..a13fb71165 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 17bf6a44c3..69019740b0 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 f7f0e72fe1..9def7e02f4 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 9a856b1b5a..76315100e2 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 fe73f71819..12ee6eb804 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 da7c812908..ba940ebf1c 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 3b8da808bd..1679d20002 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 0000000000..a9d5fd07b7
--- /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 0000000000..4579044ae3
--- /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 0000000000..34397d7dbb
--- /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 0000000000..e793e34e83
--- /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 0000000000..8d8e02fa2e
--- /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 0000000000..7b738703b8
--- /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 0000000000..b3408828a5
--- /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 0000000000..0dbbd78023
--- /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 0000000000..c92bae5269
--- /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 0000000000..cc753ed7b3
--- /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 0000000000..08764ee6f2
--- /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 0000000000..da76aea8d1
--- /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 0000000000..0eeb3501c2
--- /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 0000000000..2addb1ebc5
--- /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 59afa21efa..523484b1ff 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 6af219681c..d0c033aece 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 */
};
@@ -357,16 +358,6 @@ int blk_create_devicef(struct udevice *parent, const char *drv_name,
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
*
* The devices are removed and then unbound.
@@ -389,6 +380,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
*
* Select a hardware partition if the device supports it (typically MMC does)
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 02ac4c7952..ee2b24a62a 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 13d1516a12..810e244d9e 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 2b9c6da416..c8d629ba9b 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 83418eb482..07385cd531 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 269a2c6e72..c91dca1f82 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 eebf2d5614..4ef0d0f0c0 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 898822e445..9ff6531d1b 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 a58d7a6917..afc953d51e 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 d1b3efed9d..9badab49b0 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 938a8390cb..785d7d28b7 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -1313,6 +1313,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
*
* Tell if a device supports a given PCI capability. Returns the
@@ -1335,6 +1358,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
*
* Tell if a device supports a given PCI express extended capability.
diff --git a/include/regmap.h b/include/regmap.h
index 6a574eaa41..b2b733fda6 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -8,6 +8,61 @@
#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
*
* @start: Start address
@@ -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 0000000000..654fdf154b
--- /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 0000000000..6fc0593b14
--- /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 0000000000..d700d1936d
--- /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 ccab426e12..847e797a3a 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 5879d40386..0047998ee0 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 5b9c139f38..3f147cf608 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 b490cf2862..213e0fda94 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 4de477bafa..9c71adc69d 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 08137a2216..93f3acd430 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 a1dedd84a7..a1febd54b7 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 d4b86b3b03..a8d7e6829e 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 79b1f1de45..e43acb21d5 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 0000000000..4b317d2ec3
--- /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 76601902cb..5a709c6ff9 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 272bac0c21..2a1d021574 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 e493b1ac4a..49a8a13118 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 bc0819784f..96f8ccfe07 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 e0c9d6da6a..61a462655f 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,