summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-11-16 08:37:50 -0500
committerTom Rini <trini@konsulko.com>2018-11-16 08:37:50 -0500
commit1d6edcbfed2af33c748f2beb399810a0441888da (patch)
treefe88d63e5ef1dbe1915f90e02429e8b6934859da /drivers
parentf6206f8587fc7ec82a57dbbeb5de0f94b3c2ef49 (diff)
parent4c6e27f63c88d065a98f438085dfc36af47d3a23 (diff)
Merge tag 'pull-14nov18' of git://git.denx.de/u-boot-dm
- virtio implementation and supporting patches - DM_FLAG_PRE_RELOC fixes - regmap improvements - minor buildman and sandbox things
Diffstat (limited to 'drivers')
-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
107 files changed, 5169 insertions, 220 deletions
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,
};