From ccec4ce2ee8ae7c95a00b16da0b5dbd88615a8e2 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:39:53 +0900 Subject: [PATCH 1/9] sysreset: qemu virt: Use map_sysmem() In the platform data there is a phys_addr_t (an integer) for the address of the register and we pass that as-is into writel() which is fine in most places because we don't need to do any mapping and the macro for writel() does a cast to a pointer. If writel() is a static inline function the address argument is a pointer so passing it in as an integer without casting it first causes warnings or build failure. map_sysmem() handles the casting part and if phys_addr_t is 32bits when on a 64bit machine. Signed-off-by: Daniel Palmer Acked-by: Kuan-Wei Chiu --- drivers/sysreset/sysreset_qemu_virt_ctrl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/sysreset/sysreset_qemu_virt_ctrl.c b/drivers/sysreset/sysreset_qemu_virt_ctrl.c index e7cacc9b6e9..61b38d507fc 100644 --- a/drivers/sysreset/sysreset_qemu_virt_ctrl.c +++ b/drivers/sysreset/sysreset_qemu_virt_ctrl.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,7 @@ static int qemu_virt_ctrl_request(struct udevice *dev, enum sysreset_t type) { struct qemu_virt_ctrl_plat *plat = dev_get_plat(dev); + void __iomem *reg = map_sysmem(plat->reg + VIRT_CTRL_REG_CMD, 0x4); u32 val; switch (type) { @@ -38,7 +40,7 @@ static int qemu_virt_ctrl_request(struct udevice *dev, enum sysreset_t type) return -EPROTONOSUPPORT; } - writel(val, plat->reg + VIRT_CTRL_REG_CMD); + writel(val, reg); return -EINPROGRESS; } From 0bcd158db30aa14aa6add56060626388079c50cf Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:39:54 +0900 Subject: [PATCH 2/9] sysreset: qemu virt: Use __raw_writel() The virt ctrl register seems to be native endian, currently this driver uses writel(), which works by luck because its currently broken on m68k. Use __raw_writel() instead to avoid breaking this driver when the endianness of writel() is fixed. Acked-by: Kuan-Wei Chiu Reviewed-by: Angelo Dureghello Reviewed-by: Simon Glass Signed-off-by: Daniel Palmer --- drivers/sysreset/sysreset_qemu_virt_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sysreset/sysreset_qemu_virt_ctrl.c b/drivers/sysreset/sysreset_qemu_virt_ctrl.c index 61b38d507fc..ce15e776f8f 100644 --- a/drivers/sysreset/sysreset_qemu_virt_ctrl.c +++ b/drivers/sysreset/sysreset_qemu_virt_ctrl.c @@ -40,7 +40,7 @@ static int qemu_virt_ctrl_request(struct udevice *dev, enum sysreset_t type) return -EPROTONOSUPPORT; } - writel(val, reg); + __raw_writel(val, reg); return -EINPROGRESS; } From 5116fed77dee99a513f17f18be0dbf2e63363eef Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Sat, 16 May 2026 16:39:55 +0900 Subject: [PATCH 3/9] rtc: goldfish: Use __raw_readl() and __raw_writel() In QEMU, the Goldfish RTC is explicitly instantiated as a big-endian device on the m68k virt machine (via the 'big-endian=true' property). Currently, this driver uses ioread32() and iowrite32(), which works by luck because the underlying readl() and writel() are currently broken on m68k. Use __raw_readl() and __raw_writel() instead to avoid breaking this driver when the endianness of readl() and writel() is fixed. Signed-off-by: Kuan-Wei Chiu Tested-by: Daniel Palmer Reviewed-by: Simon Glass Signed-off-by: Daniel Palmer --- drivers/rtc/goldfish_rtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/goldfish_rtc.c b/drivers/rtc/goldfish_rtc.c index d2991ca6719..4892a63f8d8 100644 --- a/drivers/rtc/goldfish_rtc.c +++ b/drivers/rtc/goldfish_rtc.c @@ -40,8 +40,8 @@ static int goldfish_rtc_get(struct udevice *dev, struct rtc_time *time) u64 time_low; u64 now; - time_low = ioread32(base + GOLDFISH_TIME_LOW); - time_high = ioread32(base + GOLDFISH_TIME_HIGH); + time_low = __raw_readl(base + GOLDFISH_TIME_LOW); + time_high = __raw_readl(base + GOLDFISH_TIME_HIGH); now = (time_high << 32) | time_low; do_div(now, 1000000000U); @@ -62,8 +62,8 @@ static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time) return -EINVAL; now = rtc_mktime(time) * 1000000000ULL; - iowrite32(now >> 32, base + GOLDFISH_TIME_HIGH); - iowrite32(now, base + GOLDFISH_TIME_LOW); + __raw_writel(now >> 32, base + GOLDFISH_TIME_HIGH); + __raw_writel(now, base + GOLDFISH_TIME_LOW); if (time->tm_isdst > 0) priv->isdst = 1; From 75a1d7280a72d587d54b6af653234a96210f7177 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Sat, 16 May 2026 16:39:56 +0900 Subject: [PATCH 4/9] timer: goldfish: Use __raw_readl() The Goldfish timer registers are native endian, so they act as big-endian on the m68k virt machine. Currently, this driver uses readl(), which works by luck because it's currently broken on m68k. Use __raw_readl() instead to avoid breaking this driver when the endianness of readl() is fixed. Signed-off-by: Kuan-Wei Chiu Tested-by: Daniel Palmer Reviewed-by: Simon Glass Signed-off-by: Daniel Palmer --- drivers/timer/goldfish_timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/timer/goldfish_timer.c b/drivers/timer/goldfish_timer.c index 70673bbd93c..91277d7932a 100644 --- a/drivers/timer/goldfish_timer.c +++ b/drivers/timer/goldfish_timer.c @@ -31,8 +31,8 @@ static u64 goldfish_timer_get_count(struct udevice *dev) * We must read LOW before HIGH to latch the high 32-bit value * and ensure a consistent 64-bit timestamp. */ - low = readl(priv->base + TIMER_TIME_LOW); - high = readl(priv->base + TIMER_TIME_HIGH); + low = __raw_readl(priv->base + TIMER_TIME_LOW); + high = __raw_readl(priv->base + TIMER_TIME_HIGH); time = ((u64)high << 32) | low; From 3e2b261647a78929f494a932fad4e80e607a2fef Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:39:57 +0900 Subject: [PATCH 5/9] m68k: Fix writew(), writel(), readw(), readl() endianness for classic m68k In Linux these are meant to read a little-endian value and swap to the CPU endian. In u-boot for m68k this is currently broken and prevents virtio-mmio from functioning. This change is only for classic m68k. Coldfire has read big-endian, no swap for these in u-boot and Linux and existing drivers probably depend on this. Tested-by: Angelo Dureghello Reviewed-by: Simon Glass Acked-by: Kuan-Wei Chiu Acked-by: Angelo Dureghello Signed-off-by: Daniel Palmer --- arch/m68k/include/asm/io.h | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/arch/m68k/include/asm/io.h b/arch/m68k/include/asm/io.h index 35ad4a1c044..2577081d836 100644 --- a/arch/m68k/include/asm/io.h +++ b/arch/m68k/include/asm/io.h @@ -23,18 +23,27 @@ #define __raw_writew(w,addr) ((*(volatile u16 *) (addr)) = (w)) #define __raw_writel(l,addr) ((*(volatile u32 *) (addr)) = (l)) -#define readb(addr) in_8((volatile u8 *)(addr)) -#define writeb(b,addr) out_8((volatile u8 *)(addr), (b)) -#if !defined(__BIG_ENDIAN) -#define readw(addr) (*(volatile u16 *) (addr)) -#define readl(addr) (*(volatile u32 *) (addr)) -#define writew(b,addr) ((*(volatile u16 *) (addr)) = (b)) -#define writel(b,addr) ((*(volatile u32 *) (addr)) = (b)) +#define readb(addr) in_8((volatile u8 *)(addr)) +#define writeb(b, addr) out_8((volatile u8 *)(addr), (b)) +#ifdef CONFIG_M680x0 +/* + * For classic m68k these work the same way as Linux: + * Read a little endian value, swap to the CPU endian. + */ +#define readw(addr) in_le16((volatile u16 *)(addr)) +#define readl(addr) in_le32((volatile u32 *)(addr)) +#define writew(b, addr) out_le16((volatile u16 *)(addr), (b)) +#define writel(b, addr) out_le32((volatile u32 *)(addr), (b)) #else -#define readw(addr) in_be16((volatile u16 *)(addr)) -#define readl(addr) in_be32((volatile u32 *)(addr)) -#define writew(b,addr) out_be16((volatile u16 *)(addr),(b)) -#define writel(b,addr) out_be32((volatile u32 *)(addr),(b)) +/* + * For coldfire these read a big endian value and use it + * as-is. This means that for little endian devices on the + * bus like PCI device these won't work as expected currently. + */ +#define readw(addr) in_be16((volatile u16 *)(addr)) +#define readl(addr) in_be32((volatile u32 *)(addr)) +#define writew(b, addr) out_be16((volatile u16 *)(addr), (b)) +#define writel(b, addr) out_be32((volatile u32 *)(addr), (b)) #endif /* From 009cd5b56dbc2d0f7675e4262347a1a6b6a55cb2 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:39:58 +0900 Subject: [PATCH 6/9] virtio: mmio: Allow instantiation via platform data The m68k QEMU virt machine doesn't use devicetree, yet, so allow it to create virtio-mmio instances via platform data. Reviewed-by: Simon Glass Reviewed-by: Kuan-Wei Chiu Reviewed-by: Angelo Dureghello Signed-off-by: Daniel Palmer --- drivers/virtio/virtio_mmio.c | 27 ++++++++++++++++++--------- include/virtio_mmio.h | 12 ++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 include/virtio_mmio.h diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index d90d8309f99..975f98cd9e5 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -335,21 +336,28 @@ static int virtio_mmio_notify(struct udevice *udev, struct virtqueue *vq) static int virtio_mmio_of_to_plat(struct udevice *udev) { - struct virtio_mmio_priv *priv = dev_get_priv(udev); + struct virtio_mmio_plat *plat = dev_get_plat(udev); + fdt_addr_t addr; - priv->base = (void __iomem *)(ulong)dev_read_addr(udev); - if (priv->base == (void __iomem *)FDT_ADDR_T_NONE) + addr = dev_read_addr(udev); + + if (addr == FDT_ADDR_T_NONE) return -EINVAL; + plat->base = addr; + return 0; } static int virtio_mmio_probe(struct udevice *udev) { + struct virtio_mmio_plat *plat = dev_get_plat(udev); struct virtio_mmio_priv *priv = dev_get_priv(udev); struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); u32 magic; + priv->base = (void __iomem *)(uintptr_t)plat->base; + /* Check magic value */ magic = readl(priv->base + VIRTIO_MMIO_MAGIC_VALUE); if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) { @@ -405,11 +413,12 @@ static const struct udevice_id virtio_mmio_ids[] = { }; U_BOOT_DRIVER(virtio_mmio) = { - .name = "virtio-mmio", - .id = UCLASS_VIRTIO, - .of_match = virtio_mmio_ids, - .ops = &virtio_mmio_ops, - .probe = virtio_mmio_probe, + .name = "virtio-mmio", + .id = UCLASS_VIRTIO, + .of_match = virtio_mmio_ids, + .ops = &virtio_mmio_ops, + .probe = virtio_mmio_probe, .of_to_plat = virtio_mmio_of_to_plat, - .priv_auto = sizeof(struct virtio_mmio_priv), + .priv_auto = sizeof(struct virtio_mmio_priv), + .plat_auto = sizeof(struct virtio_mmio_plat), }; diff --git a/include/virtio_mmio.h b/include/virtio_mmio.h new file mode 100644 index 00000000000..8c072826db5 --- /dev/null +++ b/include/virtio_mmio.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __VIRTIO_MMIO_H__ +#define __VIRTIO_MMIO_H__ + +#include + +struct virtio_mmio_plat { + phys_addr_t base; +}; + +#endif /* __VIRTIO_MMIO_H__ */ From b781017fb63831abac4bb6956e83595f5cb8c428 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:39:59 +0900 Subject: [PATCH 7/9] virtio: cmd: Depend on VIRTIO_BLK The virtio command is calling virtio blk functions but currently depends on CONFIG_VIRTIO only. This means disabling CONFIG_VIRTIO_BLK causes the final link to fail. Since CONFIG_VIRTIO_BLK depends on CONFIG_VIRTIO switch to depending on just CONFIG_VIRTIO_BLK Reviewed-by: Kuan-Wei Chiu Reviewed-by: Angelo Dureghello Reviewed-by: Tom Rini Reviewed-by: Simon Glass Signed-off-by: Daniel Palmer --- cmd/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index c71c6824a19..032e55e8127 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1871,8 +1871,8 @@ config CMD_PVBLOCK config CMD_VIRTIO bool "virtio" - depends on VIRTIO - default y if VIRTIO + depends on VIRTIO_BLK + default y if VIRTIO_BLK help VirtIO block device support From ddba15ab72df5a01fd483a0f4fc40f9ae4d2475c Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:40:00 +0900 Subject: [PATCH 8/9] virtio: blk: Fix converting the vendor id to a string Currently we are trying to work out if the vendor id is from a virtio-mmio device and then casting a u32 to a char* and using it as a C-string. By chance there is usually a zero after the u32 and it works. Since the vendor id we are trying to convert to a string is QEMU's just define a value for the QEMU vendor id, check if the vendor id matches and then use a predefined string for "QEMU". I don't think we should have been assumming all virtio-mmio vendor ids are printable ASCII chars in the first place so do this special casing just for QEMU. If the vendor id isn't QEMU print the hex value of it. Reviewed-by: Simon Glass Reviewed-by: Kuan-Wei Chiu Signed-off-by: Daniel Palmer --- drivers/virtio/virtio_blk.c | 11 ++++------- include/virtio.h | 3 +++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/virtio/virtio_blk.c b/drivers/virtio/virtio_blk.c index 45fb596a330..94968ef1c75 100644 --- a/drivers/virtio/virtio_blk.c +++ b/drivers/virtio/virtio_blk.c @@ -231,14 +231,11 @@ static int virtio_blk_bind(struct udevice *dev) 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); + + if (uc_priv->vendor == VIRTIO_VENDOR_QEMU) + strcpy(desc->vendor, "QEMU"); else - sprintf(desc->vendor, "%04x", uc_priv->vendor); + sprintf(desc->vendor, "%08x", uc_priv->vendor); desc->bdev = dev; /* Indicate what driver features we support */ diff --git a/include/virtio.h b/include/virtio.h index 17f894a79e3..3edf023463d 100644 --- a/include/virtio.h +++ b/include/virtio.h @@ -25,6 +25,9 @@ #include #include #include + +#define VIRTIO_VENDOR_QEMU 0x554d4551 + #define VIRTIO_ID_NET 1 /* virtio net */ #define VIRTIO_ID_BLOCK 2 /* virtio block */ #define VIRTIO_ID_RNG 4 /* virtio rng */ From 3dc2761d6347c35fe15fef64592d20946396872d Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sat, 16 May 2026 16:40:01 +0900 Subject: [PATCH 9/9] board: qemu: m68k: Create virtio mmio instances So that you can use virtio network, block etc create the virtio mmio instances. There are 128 of these even if they are not all used, a single mmio base value is passed via bootinfo. Reviewed-by: Angelo Dureghello Reviewed-by: Simon Glass Reviewed-by: Kuan-Wei Chiu Tested-by: Kuan-Wei Chiu Signed-off-by: Daniel Palmer --- arch/m68k/Kconfig | 14 +++++---- board/emulation/qemu-m68k/qemu-m68k.c | 45 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 00e89bd0a62..8bebf0ea3e1 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -196,12 +196,14 @@ config TARGET_STMARK2 select M54418 config TARGET_QEMU_M68K - bool "Support QEMU m68k virt" - select M68040 - imply CMD_DM - help - This target supports the QEMU m68k virtual machine (-M virt). - It simulates a Motorola 68040 CPU with Goldfish peripherals. + bool "Support QEMU m68k virt" + select M68040 + select BOARD_EARLY_INIT_R + select VIRTIO_MMIO + imply CMD_DM + help + This target supports the QEMU m68k virtual machine (-M virt). + It simulates a Motorola 68040 CPU with Goldfish peripherals. endchoice diff --git a/board/emulation/qemu-m68k/qemu-m68k.c b/board/emulation/qemu-m68k/qemu-m68k.c index d3527aee112..a19b23a28ce 100644 --- a/board/emulation/qemu-m68k/qemu-m68k.c +++ b/board/emulation/qemu-m68k/qemu-m68k.c @@ -14,9 +14,14 @@ #include #include #include +#include +#include +#include #include +#include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -25,6 +30,38 @@ static struct goldfish_rtc_plat rtc_plat; static struct goldfish_timer_plat timer_plat; static struct qemu_virt_ctrl_plat reset_plat; +#define VIRTIO_MMIO_NUM 128 +#define VIRTIO_MMIO_SZ 0x200 + +static struct virtio_mmio_plat virtio_mmio_plat[VIRTIO_MMIO_NUM]; +static char virtio_mmio_names[VIRTIO_MMIO_NUM][11]; +static phys_addr_t virtio_mmio_base; + +static int create_virtio_mmios(void) +{ + struct driver *drv; + int i, ret; + + if (!virtio_mmio_base) + return -ENODEV; + + drv = lists_driver_lookup_name("virtio-mmio"); + if (!drv) + return -ENOENT; + + for (i = 0; i < VIRTIO_MMIO_NUM; i++) { + virtio_mmio_plat[i].base = virtio_mmio_base + (VIRTIO_MMIO_SZ * i); + sprintf(virtio_mmio_names[i], "virtio-%d", i); + + ret = device_bind(dm_root(), drv, virtio_mmio_names[i], + &virtio_mmio_plat[i], ofnode_null(), NULL); + if (ret) + return ret; + } + + return 0; +} + /* * Theoretical limit derivation: * Max Bootinfo Size (Standard Page) = 4096 bytes @@ -65,6 +102,9 @@ static void parse_bootinfo(void) case BI_VIRT_CTRL_BASE: reset_plat.reg = base; break; + case BI_VIRT_VIRTIO_BASE: + virtio_mmio_base = base; + break; case BI_MEMCHUNK: gd->ram_size = record->data[1]; break; @@ -80,6 +120,11 @@ int board_early_init_f(void) return 0; } +int board_early_init_r(void) +{ + return create_virtio_mmios(); +} + int checkboard(void) { puts("Board: QEMU m68k virt\n");