summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-02-02 14:24:56 -0600
committerTom Rini <[email protected]>2026-02-02 14:25:48 -0600
commit1de103fc29761fa729dffaa15d0cfb2766be05e4 (patch)
treefc2253a01976672d68a4d984273e7f02c1fbd3cc
parent4b287e0a3ae4ab3159584cb5ae12c9b470716be8 (diff)
parent838e3be9e1d39e38661a788ae3bcfb9f989450a8 (diff)
Merge patch series "m68k: Add support for QEMU virt machine"
Kuan-Wei Chiu <[email protected]> says: Add support for the QEMU 'virt' machine on the m68k architecture. The QEMU virt machine models a generic system utilizing Goldfish virtual peripherals and is capable of emulating various classic 68k CPUs. Currently, U-Boot's m68k architecture support focuses on ColdFire variants. This series expands support to include the classic M680x0 architecture, implementing the necessary exception vectors, startup code, and a bootinfo parser compatible with the QEMU interface. Drivers for Goldfish peripherals (TTY, Timer, RTC) and the QEMU Virtual System Controller (sysreset) are also added to enable serial console, timekeeping, and system reset functionality. The implementation has been verified on QEMU targeting the M68040 CPU, confirming successful hardware initialization and boot to the U-Boot command shell. Additionally, the CI configuration was verified locally using gitlab-ci-local "qemu_m68k_virt test.py", resulting in PASS qemu_m68k_virt test.py. Link: https://lore.kernel.org/r/[email protected] [trini: Re-sort MAINTAINERS entries] Signed-off-by: Tom Rini <[email protected]>
-rw-r--r--.azure-pipelines.yml3
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--MAINTAINERS42
-rw-r--r--arch/m68k/Kconfig39
-rw-r--r--arch/m68k/Makefile1
-rw-r--r--arch/m68k/config.mk10
-rw-r--r--arch/m68k/cpu/m680x0/Makefile6
-rw-r--r--arch/m68k/cpu/m680x0/cpu.c73
-rw-r--r--arch/m68k/cpu/m680x0/start.S73
-rw-r--r--arch/m68k/cpu/m680x0/u-boot.lds47
-rw-r--r--arch/m68k/include/asm/bootinfo.h39
-rw-r--r--arch/m68k/lib/Makefile7
-rw-r--r--board/emulation/qemu-m68k/Kconfig12
-rw-r--r--board/emulation/qemu-m68k/MAINTAINERS8
-rw-r--r--board/emulation/qemu-m68k/Makefile5
-rw-r--r--board/emulation/qemu-m68k/qemu-m68k.c117
-rw-r--r--configs/qemu-m68k_defconfig20
-rw-r--r--doc/board/emulation/index.rst1
-rw-r--r--doc/board/emulation/qemu-m68k.rst39
-rw-r--r--drivers/rtc/goldfish_rtc.c23
-rw-r--r--drivers/serial/Kconfig8
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_goldfish.c117
-rw-r--r--drivers/sysreset/Kconfig8
-rw-r--r--drivers/sysreset/Makefile1
-rw-r--r--drivers/sysreset/sysreset_qemu_virt_ctrl.c55
-rw-r--r--drivers/timer/Kconfig8
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/goldfish_timer.c89
-rw-r--r--include/configs/qemu-m68k.h18
-rw-r--r--include/goldfish_rtc.h15
-rw-r--r--include/goldfish_timer.h13
-rw-r--r--include/goldfish_tty.h18
-rw-r--r--include/qemu_virt_ctrl.h13
34 files changed, 915 insertions, 21 deletions
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index 67880d22fff..4417ef4e5a5 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -524,6 +524,9 @@ stages:
TEST_PY_ID: "--id qemu"
TEST_PY_TEST_SPEC: "not sleep and not efi"
OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR"
+ qemu_m68k_virt:
+ TEST_PY_BD: "qemu-m68k"
+ TEST_PY_TEST_SPEC: "not sleep"
qemu_malta:
TEST_PY_BD: "malta"
TEST_PY_ID: "--id qemu"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f618c304bd8..08d01c41f35 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -421,6 +421,12 @@ qemu_m68k test.py:
OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR"
<<: *buildman_and_testpy_dfn
+qemu_m68k_virt test.py:
+ variables:
+ TEST_PY_BD: "qemu-m68k"
+ TEST_PY_TEST_SPEC: "not sleep"
+ <<: *buildman_and_testpy_dfn
+
qemu_malta test.py:
variables:
TEST_PY_BD: "malta"
diff --git a/MAINTAINERS b/MAINTAINERS
index a0f87da67e1..0734c07da1e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1048,15 +1048,6 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-clk.git
F: drivers/clk/
F: drivers/clk/imx/
-COLDFIRE
-M: Huan Wang <[email protected]>
-M: Angelo Dureghello <[email protected]>
-S: Maintained
-T: git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git
-F: arch/m68k/
-F: doc/arch/m68k.rst
-F: drivers/watchdog/mcf_wdt.c
-
CPU
M: Simon Glass <[email protected]>
M: Hou Zhiqiang <[email protected]>
@@ -1263,6 +1254,18 @@ S: Maintained
F: drivers/misc/gsc.c
F: include/gsc.h
+GOLDFISH SERIAL DRIVER
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+F: drivers/serial/serial_goldfish.c
+F: include/goldfish_tty.h
+
+GOLDFISH TIMER DRIVER
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+F: drivers/timer/goldfish_timer.c
+F: include/goldfish_timer.h
+
INTERCONNECT:
M: Neil Armstrong <[email protected]>
S: Maintained
@@ -1315,6 +1318,21 @@ F: lib/getopt.c
F: test/log/
F: test/py/tests/test_log.py
+M680X0 ARCHITECTURE
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+F: arch/m68k/cpu/m680x0/
+F: arch/m68k/include/asm/bootinfo.h
+
+M68K
+M: Angelo Dureghello <[email protected]>
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+T: git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git
+F: arch/m68k/
+F: doc/arch/m68k.rst
+F: drivers/watchdog/mcf_wdt.c
+
MALI DISPLAY PROCESSORS
M: Liviu Dudau <[email protected]>
S: Supported
@@ -1589,6 +1607,12 @@ S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-mpc85xx.git
F: arch/powerpc/cpu/mpc85xx/
+QEMU VIRTUAL SYSTEM CONTROLLER
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+F: drivers/sysreset/sysreset_qemu_virt_ctrl.c
+F: include/qemu_virt_ctrl.h
+
RAW NAND
M: Dario Binacchi <[email protected]>
M: Michael Trimarchi <[email protected]>
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index f5d495f9935..6ce8f577e3a 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -12,50 +12,64 @@ config MCF520x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF52x2
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF523x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF530x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF5301x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF532x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF537x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
config MCF5441x
select OF_CONTROL
select DM
select DM_SERIAL
+ select ARCH_COLDFIRE
bool
+config M680x0
+ bool
+ help
+ This enables support for the classic Motorola 68000 family of
+ processors.
+
# processor type
config M5208
bool
@@ -110,6 +124,10 @@ config M54418
bool
select MCF5441x
+config M68040
+ bool
+ select M680x0
+
# peripherals
config CF_DSPI
bool
@@ -176,8 +194,28 @@ config TARGET_STMARK2
select CF_DSPI
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.
+
endchoice
+config SYS_CPU
+ string
+ default "mcf52x2" if MCF52x2
+ default "mcf523x" if MCF523x
+ default "mcf530x" if MCF530x
+ default "mcf532x" if MCF532x
+ default "mcf5445x" if MCF5445x
+ default "m680x0" if M680x0
+
+config ARCH_COLDFIRE
+ bool
+
source "board/BuS/eb_cpu5282/Kconfig"
source "board/cobra5272/Kconfig"
source "board/nxp/m5208evbe/Kconfig"
@@ -192,6 +230,7 @@ source "board/nxp/m5329evb/Kconfig"
source "board/nxp/m5373evb/Kconfig"
source "board/sysam/amcore/Kconfig"
source "board/sysam/stmark2/Kconfig"
+source "board/emulation/qemu-m68k/Kconfig"
config M68K_QEMU
bool "Build with workarounds for incomplete QEMU emulation"
diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile
index 4a7960bbeb4..bb57cf9ac63 100644
--- a/arch/m68k/Makefile
+++ b/arch/m68k/Makefile
@@ -17,6 +17,7 @@ cpuflags-$(CONFIG_M5307) := -mcpu=5307
cpuflags-$(CONFIG_MCF5301x) := -mcpu=53015 -fPIC
cpuflags-$(CONFIG_MCF532x) := -mcpu=5329 -fPIC
cpuflags-$(CONFIG_MCF5441x) := -mcpu=54418 -fPIC
+cpuflags-$(CONFIG_M68040) := -mcpu=68040 -fno-pic
PLATFORM_CPPFLAGS += $(cpuflags-y)
diff --git a/arch/m68k/config.mk b/arch/m68k/config.mk
index 643b7d1d35d..458953f9712 100644
--- a/arch/m68k/config.mk
+++ b/arch/m68k/config.mk
@@ -3,8 +3,14 @@
# (C) Copyright 2000-2002
# Wolfgang Denk, DENX Software Engineering, [email protected].
-PLATFORM_CPPFLAGS += -D__M68K__ -fPIC
+PLATFORM_CPPFLAGS += -D__M68K__
+ifneq ($(CONFIG_M680x0),y)
+PLATFORM_CPPFLAGS += -fPIC
+endif
KBUILD_LDFLAGS += -n -pie
PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections
-PLATFORM_RELFLAGS += -ffixed-d7 -msep-data
+PLATFORM_RELFLAGS += -ffixed-d7
+ifneq ($(CONFIG_M680x0),y)
+PLATFORM_RELFLAGS += -msep-data
+endif
LDFLAGS_FINAL += --gc-sections -pie
diff --git a/arch/m68k/cpu/m680x0/Makefile b/arch/m68k/cpu/m680x0/Makefile
new file mode 100644
index 00000000000..f6e0c9a46c0
--- /dev/null
+++ b/arch/m68k/cpu/m680x0/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+
+extra-y += start.o
+obj-y += cpu.o
diff --git a/arch/m68k/cpu/m680x0/cpu.c b/arch/m68k/cpu/m680x0/cpu.c
new file mode 100644
index 00000000000..f60b932c7dd
--- /dev/null
+++ b/arch/m68k/cpu/m680x0/cpu.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CPU specific code for m68040
+ *
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#include <config.h>
+#include <cpu_func.h>
+#include <init.h>
+#include <stdio.h>
+#include <asm/global_data.h>
+#include <linux/types.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void m68k_virt_init_reserve(ulong base)
+{
+ struct global_data *gd_ptr = (struct global_data *)base;
+ char *p = (char *)gd_ptr;
+ unsigned int i;
+
+ /* FIXME: usage of memset() here caused a hang on QEMU m68k virt. */
+ for (i = 0; i < sizeof(*gd_ptr); i++)
+ p[i] = 0;
+
+ gd = gd_ptr;
+
+ gd->malloc_base = base + sizeof(*gd_ptr);
+}
+
+int print_cpuinfo(void)
+{
+ puts("CPU: M68040 (QEMU Virt)\n");
+
+ return 0;
+}
+
+int get_clocks(void)
+{
+ return 0;
+}
+
+int cpu_init_r(void)
+{
+ return 0;
+}
+
+/*
+ * Relocation Stub
+ * We skip actual relocation for this QEMU bring-up and jump directly
+ * to board_init_r.
+ */
+
+void relocate_code(ulong sp, struct global_data *new_gd, ulong relocaddr)
+{
+ board_init_r(new_gd, relocaddr);
+}
+
+/* Stubs for Standard Facilities (Cache, Interrupts) */
+
+int disable_interrupts(void) { return 0; }
+void enable_interrupts(void) { return; }
+int interrupt_init(void) { return 0; }
+
+void icache_enable(void) {}
+void icache_disable(void) {}
+int icache_status(void) { return 0; }
+void dcache_enable(void) {}
+void dcache_disable(void) {}
+int dcache_status(void) { return 0; }
+void flush_cache(unsigned long start, unsigned long size) {}
+void flush_dcache_range(unsigned long start, unsigned long stop) {}
diff --git a/arch/m68k/cpu/m680x0/start.S b/arch/m68k/cpu/m680x0/start.S
new file mode 100644
index 00000000000..0802ca1fca2
--- /dev/null
+++ b/arch/m68k/cpu/m680x0/start.S
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Startup code for m68040
+ *
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#include <asm-offsets.h>
+#include <config.h>
+#include <linux/linkage.h>
+
+.section .text
+
+/*
+ * Vector Table
+ * m68k uses the first 1KB for the exception vector table.
+ */
+.balign 4
+.global _vectors
+_vectors:
+ .long CFG_SYS_INIT_SP_ADDR /* 0x00: Initial SP */
+ .long _start /* 0x04: Initial PC (Reset) */
+ .long _fault /* 0x08: Bus Error */
+ .long _fault /* 0x0C: Address Error */
+ .long _fault /* 0x10: Illegal Instruction */
+ .long _fault /* 0x14: Zero Divide */
+ .long _fault /* 0x18: CHK */
+ .long _fault /* 0x1C: TRAPV */
+ .long _fault /* 0x20: Privilege */
+ .long _fault /* 0x24: Trace */
+ .long _fault /* 0x28: Line 1010 */
+ .long _fault /* 0x2C: Line 1111 */
+ .fill 0x400 - (.-_vectors), 1, 0
+
+/*
+ * Entry Point
+ */
+ENTRY(_start)
+ /* Disable Interrupts */
+ move.w #0x2700, %sr
+
+ /* Setup initial stack pointer */
+ move.l #CFG_SYS_INIT_SP_ADDR, %sp
+
+ /*
+ * Allocate Global Data (GD)
+ * board_init_f_alloc_reserve(top) returns the new top of stack in %d0
+ */
+ move.l %sp, -(%sp)
+ bsr.l board_init_f_alloc_reserve
+ addq.l #4, %sp
+
+ /* Update Stack Pointer and set GD register */
+ move.l %d0, %sp
+ move.l %d0, %d7 /* %d7 is the gd register */
+
+ /* Initialize Reserved Memory. */
+ move.l %d0, -(%sp)
+ bsr.l m68k_virt_init_reserve
+ addq.l #4, %sp
+
+ /* Enter board_init_f(0) */
+ clr.l -(%sp)
+ bsr.l board_init_f
+ addq.l #4, %sp
+
+ /* Should not return */
+hang:
+ bra.s hang
+ENDPROC(_start)
+
+_fault:
+ bra.s _fault
diff --git a/arch/m68k/cpu/m680x0/u-boot.lds b/arch/m68k/cpu/m680x0/u-boot.lds
new file mode 100644
index 00000000000..ae102c32833
--- /dev/null
+++ b/arch/m68k/cpu/m680x0/u-boot.lds
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linker Script for m68040
+ *
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+OUTPUT_ARCH(m68k)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = 0x00000000;
+ __text_start = .;
+
+ .text :
+ {
+ arch/m68k/cpu/m680x0/start.o (.text*)
+ *(.text*)
+ }
+
+ . = ALIGN(16);
+ .rodata : { *(.rodata*) }
+
+ . = ALIGN(16);
+ .data : { *(.data*) }
+
+ . = ALIGN(4);
+ .u_boot_list : {
+ KEEP(*(SORT(*u_boot_list*)));
+ }
+
+ . = ALIGN(4);
+ __image_copy_end = .;
+ __init_end = .;
+
+ . = ALIGN(16);
+ __bss_start = .;
+ .bss :
+ {
+ *(.bss*)
+ . = ALIGN(16);
+ }
+ __bss_end = .;
+
+ _end = .;
+}
diff --git a/arch/m68k/include/asm/bootinfo.h b/arch/m68k/include/asm/bootinfo.h
new file mode 100644
index 00000000000..6d7bde3b833
--- /dev/null
+++ b/arch/m68k/include/asm/bootinfo.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ *
+ * Definitions for the m68k bootinfo interface.
+ */
+
+#ifndef _ASM_M68K_BOOTINFO_H
+#define _ASM_M68K_BOOTINFO_H
+
+#ifndef __ASSEMBLY__
+
+struct bi_record {
+ unsigned short tag; /* tag ID */
+ unsigned short size; /* size of record (in bytes) */
+ unsigned long data[0]; /* data */
+};
+
+#endif /* __ASSEMBLY__ */
+
+/* Bootinfo Tag IDs */
+#define BI_LAST 0x0000
+#define BI_MACHTYPE 0x0001
+#define BI_CPUTYPE 0x0002
+#define BI_FPUTYPE 0x0003
+#define BI_MMUTYPE 0x0004
+#define BI_MEMCHUNK 0x0005
+#define BI_RAMDISK 0x0006
+#define BI_COMMAND_LINE 0x0007
+
+/* QEMU virt specific tags */
+#define BI_VIRT_QEMU_VERSION 0x8000
+#define BI_VIRT_GF_PIC_BASE 0x8001
+#define BI_VIRT_GF_RTC_BASE 0x8002
+#define BI_VIRT_GF_TTY_BASE 0x8003
+#define BI_VIRT_VIRTIO_BASE 0x8004
+#define BI_VIRT_CTRL_BASE 0x8005
+
+#endif /* _ASM_M68K_BOOTINFO_H */
diff --git a/arch/m68k/lib/Makefile b/arch/m68k/lib/Makefile
index 6e1fd938f52..cf93715637a 100644
--- a/arch/m68k/lib/Makefile
+++ b/arch/m68k/lib/Makefile
@@ -7,10 +7,5 @@
## if the user asked for it
lib-$(CONFIG_USE_PRIVATE_LIBGCC) += lshrdi3.o muldi3.o ashldi3.o ashrdi3.o
-obj-y += bdinfo.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
-obj-y += cache.o
-obj-y += interrupts.o
-obj-y += time.o
-obj-y += traps.o
-obj-y += fec.o
+obj-$(CONFIG_ARCH_COLDFIRE) += cache.o interrupts.o time.o traps.o bdinfo.o fec.o
diff --git a/board/emulation/qemu-m68k/Kconfig b/board/emulation/qemu-m68k/Kconfig
new file mode 100644
index 00000000000..aae6dfe400f
--- /dev/null
+++ b/board/emulation/qemu-m68k/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_QEMU_M68K
+
+config SYS_BOARD
+ default "qemu-m68k"
+
+config SYS_VENDOR
+ default "emulation"
+
+config SYS_CONFIG_NAME
+ default "qemu-m68k"
+
+endif
diff --git a/board/emulation/qemu-m68k/MAINTAINERS b/board/emulation/qemu-m68k/MAINTAINERS
new file mode 100644
index 00000000000..90414c58465
--- /dev/null
+++ b/board/emulation/qemu-m68k/MAINTAINERS
@@ -0,0 +1,8 @@
+QEMU M68K VIRT BOARD
+M: Kuan-Wei Chiu <[email protected]>
+S: Maintained
+F: board/emulation/qemu-m68k/
+F: board/emulation/common/
+F: include/configs/qemu-m68k.h
+F: configs/qemu-m68k_defconfig
+F: doc/board/emulation/qemu-m68k.rst
diff --git a/board/emulation/qemu-m68k/Makefile b/board/emulation/qemu-m68k/Makefile
new file mode 100644
index 00000000000..5cb2886ff5a
--- /dev/null
+++ b/board/emulation/qemu-m68k/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+
+obj-y += qemu-m68k.o
diff --git a/board/emulation/qemu-m68k/qemu-m68k.c b/board/emulation/qemu-m68k/qemu-m68k.c
new file mode 100644
index 00000000000..d3527aee112
--- /dev/null
+++ b/board/emulation/qemu-m68k/qemu-m68k.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#include <config.h>
+#include <goldfish_rtc.h>
+#include <goldfish_timer.h>
+#include <goldfish_tty.h>
+#include <init.h>
+#include <qemu_virt_ctrl.h>
+#include <serial.h>
+#include <asm-generic/sections.h>
+#include <asm/bootinfo.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <dm/platdata.h>
+#include <linux/errno.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct goldfish_tty_plat serial_plat;
+static struct goldfish_rtc_plat rtc_plat;
+static struct goldfish_timer_plat timer_plat;
+static struct qemu_virt_ctrl_plat reset_plat;
+
+/*
+ * Theoretical limit derivation:
+ * Max Bootinfo Size (Standard Page) = 4096 bytes
+ * Min Record Size (Tag + Size) = 4 bytes
+ * Max Records = 4096 / 4 = 1024
+ */
+#define MAX_BOOTINFO_RECORDS 1024
+
+static void parse_bootinfo(void)
+{
+ struct bi_record *record;
+ ulong addr;
+ int loops = 0;
+
+ /* QEMU places bootinfo after _end, aligned to 2 bytes */
+ addr = (ulong)&_end;
+ addr = ALIGN(addr, 2);
+
+ record = (struct bi_record *)addr;
+
+ if (record->tag != BI_MACHTYPE)
+ return;
+
+ while (record->tag != BI_LAST) {
+ phys_addr_t base = record->data[0];
+
+ if (++loops > MAX_BOOTINFO_RECORDS)
+ panic("Bootinfo loop exceeded");
+
+ switch (record->tag) {
+ case BI_VIRT_GF_TTY_BASE:
+ serial_plat.reg = base;
+ break;
+ case BI_VIRT_GF_RTC_BASE:
+ rtc_plat.reg = base;
+ timer_plat.reg = base;
+ break;
+ case BI_VIRT_CTRL_BASE:
+ reset_plat.reg = base;
+ break;
+ case BI_MEMCHUNK:
+ gd->ram_size = record->data[1];
+ break;
+ }
+ record = (struct bi_record *)((ulong)record + record->size);
+ }
+}
+
+int board_early_init_f(void)
+{
+ parse_bootinfo();
+
+ return 0;
+}
+
+int checkboard(void)
+{
+ puts("Board: QEMU m68k virt\n");
+
+ return 0;
+}
+
+int dram_init(void)
+{
+ /* Default: 16MB */
+ if (!gd->ram_size)
+ gd->ram_size = SZ_16M;
+
+ return 0;
+}
+
+U_BOOT_DRVINFO(goldfish_rtc) = {
+ .name = "rtc_goldfish",
+ .plat = &rtc_plat,
+};
+
+U_BOOT_DRVINFO(goldfish_timer) = {
+ .name = "goldfish_timer",
+ .plat = &timer_plat,
+};
+
+U_BOOT_DRVINFO(goldfish_serial) = {
+ .name = "serial_goldfish",
+ .plat = &serial_plat,
+};
+
+U_BOOT_DRVINFO(sysreset_qemu_virt_ctrl) = {
+ .name = "sysreset_qemu_virt_ctrl",
+ .plat = &reset_plat,
+};
diff --git a/configs/qemu-m68k_defconfig b/configs/qemu-m68k_defconfig
new file mode 100644
index 00000000000..d667791d93b
--- /dev/null
+++ b/configs/qemu-m68k_defconfig
@@ -0,0 +1,20 @@
+CONFIG_M68K=y
+CONFIG_TEXT_BASE=0x00000000
+CONFIG_SYS_MALLOC_LEN=0x20000
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_SYS_MONITOR_LEN=262144
+CONFIG_SYS_BOOTM_LEN=0x1000000
+CONFIG_SYS_LOAD_ADDR=0x00000000
+CONFIG_TARGET_QEMU_M68K=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_BOARD_EARLY_INIT_F=y
+CONFIG_CMD_POWEROFF=y
+CONFIG_DM_RTC=y
+CONFIG_RTC_GOLDFISH=y
+CONFIG_DM_SERIAL=y
+CONFIG_SERIAL_GOLDFISH=y
+CONFIG_SYSRESET=y
+CONFIG_SYSRESET_CMD_POWEROFF=y
+CONFIG_SYSRESET_QEMU_VIRT_CTRL=y
+CONFIG_TIMER=y
+CONFIG_GOLDFISH_TIMER=y
diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst
index b0139490e21..5d0dd83d8ac 100644
--- a/doc/board/emulation/index.rst
+++ b/doc/board/emulation/index.rst
@@ -17,6 +17,7 @@ Emulation
qemu-sbsa
qemu-x86
qemu-xtensa
+ qemu-m68k
Also see
diff --git a/doc/board/emulation/qemu-m68k.rst b/doc/board/emulation/qemu-m68k.rst
new file mode 100644
index 00000000000..6c4de54cf6a
--- /dev/null
+++ b/doc/board/emulation/qemu-m68k.rst
@@ -0,0 +1,39 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+.. Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+
+QEMU m68k
+=========
+
+QEMU for m68k supports a special 'virt' machine designed for emulation and
+virtualization purposes. This document describes how to run U-Boot under it.
+
+The QEMU virt machine models a generic m68k virtual machine with Goldfish
+interfaces. It supports the Motorola 68040 CPU architecture.
+
+Building U-Boot
+---------------
+Set the CROSS_COMPILE environment variable to your m68k toolchain, and run:
+
+.. code-block:: bash
+
+ export CROSS_COMPILE=m68k-linux-gnu-
+ make qemu-m68k_defconfig
+ make
+
+Running U-Boot
+--------------
+The minimal QEMU command line to get U-Boot up and running is:
+
+.. code-block:: bash
+
+ qemu-system-m68k -M virt -cpu m68040 -nographic -kernel u-boot
+
+Note that the `-nographic` option is used to redirect the console to stdio,
+which connects to the emulated Goldfish TTY device.
+
+Hardware Support
+----------------
+The following QEMU virt peripherals are supported in U-Boot:
+
+* Goldfish TTY (Serial Console)
+* Goldfish RTC (Real Time Clock)
diff --git a/drivers/rtc/goldfish_rtc.c b/drivers/rtc/goldfish_rtc.c
index e63a2766c76..d2991ca6719 100644
--- a/drivers/rtc/goldfish_rtc.c
+++ b/drivers/rtc/goldfish_rtc.c
@@ -9,6 +9,7 @@
#include <div64.h>
#include <dm.h>
+#include <goldfish_rtc.h>
#include <mapmem.h>
#include <rtc.h>
#include <linux/io.h>
@@ -74,15 +75,27 @@ static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time)
return 0;
}
-static int goldfish_rtc_probe(struct udevice *dev)
+static int goldfish_rtc_of_to_plat(struct udevice *dev)
{
- struct goldfish_rtc *priv = dev_get_priv(dev);
+ struct goldfish_rtc_plat *plat = dev_get_plat(dev);
fdt_addr_t addr;
addr = dev_read_addr(dev);
- if (addr == FDT_ADDR_T_NONE)
+ if (addr != FDT_ADDR_T_NONE)
+ plat->reg = addr;
+
+ return 0;
+}
+
+static int goldfish_rtc_probe(struct udevice *dev)
+{
+ struct goldfish_rtc_plat *plat = dev_get_plat(dev);
+ struct goldfish_rtc *priv = dev_get_priv(dev);
+
+ if (!plat->reg)
return -EINVAL;
- priv->base = map_sysmem(addr, 0x20);
+
+ priv->base = map_sysmem(plat->reg, 0x20);
return 0;
}
@@ -103,5 +116,7 @@ U_BOOT_DRIVER(rtc_goldfish) = {
.ops = &goldfish_rtc_ops,
.probe = goldfish_rtc_probe,
.of_match = goldfish_rtc_of_match,
+ .of_to_plat = goldfish_rtc_of_to_plat,
+ .plat_auto = sizeof(struct goldfish_rtc_plat),
.priv_auto = sizeof(struct goldfish_rtc),
};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 371d7aa5bba..b84cb9ec781 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1193,4 +1193,12 @@ config SYS_SDMR
depends on MPC8XX_CONS
default 0x0
+config SERIAL_GOLDFISH
+ bool "Goldfish TTY support"
+ depends on DM_SERIAL
+ help
+ Select this to enable support for the Goldfish TTY serial port.
+ This virtual device is commonly used by QEMU virtual machines
+ (e.g. m68k virt) for console output.
+
endif
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 8eaae62b0fc..fe8d23be512 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -63,3 +63,4 @@ obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o
obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o
+obj-$(CONFIG_SERIAL_GOLDFISH) += serial_goldfish.o
diff --git a/drivers/serial/serial_goldfish.c b/drivers/serial/serial_goldfish.c
new file mode 100644
index 00000000000..4ac2cfb6231
--- /dev/null
+++ b/drivers/serial/serial_goldfish.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ * Goldfish TTY driver for U-Boot
+ */
+
+#include <dm.h>
+#include <goldfish_tty.h>
+#include <mapmem.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+/* Goldfish TTY Register Offsets */
+#define GOLDFISH_TTY_PUT_CHAR 0x00
+#define GOLDFISH_TTY_BYTES_READY 0x04
+#define GOLDFISH_TTY_CMD 0x08
+#define GOLDFISH_TTY_DATA_PTR 0x10
+#define GOLDFISH_TTY_DATA_LEN 0x14
+#define GOLDFISH_TTY_DATA_PTR_HIGH 0x18
+#define GOLDFISH_TTY_VERSION 0x20
+
+/* Commands */
+#define CMD_WRITE_BUFFER 2
+#define CMD_READ_BUFFER 3
+
+struct goldfish_tty_priv {
+ void __iomem *base;
+ u8 rx_buf;
+};
+
+static int goldfish_serial_getc(struct udevice *dev)
+{
+ struct goldfish_tty_priv *priv = dev_get_priv(dev);
+ unsigned long paddr;
+ u32 count;
+
+ count = __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
+ if (count == 0)
+ return -EAGAIN;
+
+ paddr = virt_to_phys((void *)&priv->rx_buf);
+
+ __raw_writel(0, priv->base + GOLDFISH_TTY_DATA_PTR_HIGH);
+ __raw_writel(paddr, priv->base + GOLDFISH_TTY_DATA_PTR);
+ __raw_writel(1, priv->base + GOLDFISH_TTY_DATA_LEN);
+ __raw_writel(CMD_READ_BUFFER, priv->base + GOLDFISH_TTY_CMD);
+
+ return priv->rx_buf;
+}
+
+static int goldfish_serial_putc(struct udevice *dev, const char ch)
+{
+ struct goldfish_tty_priv *priv = dev_get_priv(dev);
+
+ __raw_writel(ch, priv->base + GOLDFISH_TTY_PUT_CHAR);
+
+ return 0;
+}
+
+static int goldfish_serial_pending(struct udevice *dev, bool input)
+{
+ struct goldfish_tty_priv *priv = dev_get_priv(dev);
+
+ if (input)
+ return __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
+
+ return 0;
+}
+
+static int goldfish_serial_of_to_plat(struct udevice *dev)
+{
+ struct goldfish_tty_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr != FDT_ADDR_T_NONE)
+ plat->reg = addr;
+
+ return 0;
+}
+
+static int goldfish_serial_probe(struct udevice *dev)
+{
+ struct goldfish_tty_plat *plat = dev_get_plat(dev);
+ struct goldfish_tty_priv *priv = dev_get_priv(dev);
+
+ if (!plat->reg)
+ return -EINVAL;
+
+ priv->base = map_sysmem(plat->reg, 0x1000);
+
+ return 0;
+}
+
+static const struct dm_serial_ops goldfish_serial_ops = {
+ .putc = goldfish_serial_putc,
+ .pending = goldfish_serial_pending,
+ .getc = goldfish_serial_getc,
+};
+
+static const struct udevice_id goldfish_serial_ids[] = {
+ { .compatible = "google,goldfish-tty" },
+ { }
+};
+
+U_BOOT_DRIVER(serial_goldfish) = {
+ .name = "serial_goldfish",
+ .id = UCLASS_SERIAL,
+ .of_match = goldfish_serial_ids,
+ .of_to_plat = goldfish_serial_of_to_plat,
+ .plat_auto = sizeof(struct goldfish_tty_plat),
+ .probe = goldfish_serial_probe,
+ .ops = &goldfish_serial_ops,
+ .priv_auto = sizeof(struct goldfish_tty_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 0181f6cd581..120e7510f15 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -298,6 +298,14 @@ config SYSRESET_QCOM_PSHOLD
help
Add support for the system reboot on Qualcomm SoCs via PSHOLD.
+config SYSRESET_QEMU_VIRT_CTRL
+ bool "QEMU Virtual System Controller support"
+ depends on SYSRESET
+ help
+ Enable support for the QEMU Virtual System Controller.
+ This device is used in QEMU machines (e.g. m68k virt) to trigger
+ system reset and poweroff events.
+
endif
endmenu
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index f5c78b25896..d18a5d52360 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_$(PHASE_)SYSRESET_X86) += sysreset_x86.o
obj-$(CONFIG_SYSRESET_RAA215300) += sysreset_raa215300.o
obj-$(CONFIG_SYSRESET_QCOM_PSHOLD) += sysreset_qcom-pshold.o
obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o
+obj-$(CONFIG_SYSRESET_QEMU_VIRT_CTRL) += sysreset_qemu_virt_ctrl.o
diff --git a/drivers/sysreset/sysreset_qemu_virt_ctrl.c b/drivers/sysreset/sysreset_qemu_virt_ctrl.c
new file mode 100644
index 00000000000..e7cacc9b6e9
--- /dev/null
+++ b/drivers/sysreset/sysreset_qemu_virt_ctrl.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ *
+ * QEMU Virtual System Controller Driver
+ */
+
+#include <dm.h>
+#include <qemu_virt_ctrl.h>
+#include <sysreset.h>
+#include <asm/io.h>
+#include <linux/err.h>
+
+/* Register offsets */
+#define VIRT_CTRL_REG_FEATURES 0x00
+#define VIRT_CTRL_REG_CMD 0x04
+
+/* Commands */
+#define VIRT_CTRL_CMD_NOOP 0x00
+#define VIRT_CTRL_CMD_RESET 0x01
+#define VIRT_CTRL_CMD_HALT 0x02
+#define VIRT_CTRL_CMD_PANIC 0x03
+
+static int qemu_virt_ctrl_request(struct udevice *dev, enum sysreset_t type)
+{
+ struct qemu_virt_ctrl_plat *plat = dev_get_plat(dev);
+ u32 val;
+
+ switch (type) {
+ case SYSRESET_WARM:
+ case SYSRESET_COLD:
+ val = VIRT_CTRL_CMD_RESET;
+ break;
+ case SYSRESET_POWER_OFF:
+ val = VIRT_CTRL_CMD_HALT;
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ writel(val, plat->reg + VIRT_CTRL_REG_CMD);
+
+ return -EINPROGRESS;
+}
+
+static struct sysreset_ops qemu_virt_ctrl_ops = {
+ .request = qemu_virt_ctrl_request,
+};
+
+U_BOOT_DRIVER(sysreset_qemu_virt_ctrl) = {
+ .name = "sysreset_qemu_virt_ctrl",
+ .id = UCLASS_SYSRESET,
+ .ops = &qemu_virt_ctrl_ops,
+ .plat_auto = sizeof(struct qemu_virt_ctrl_plat),
+};
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index f9511503b02..a84a0dc0539 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -340,4 +340,12 @@ config STARFIVE_TIMER
Select this to enable support for the timer found on
Starfive SoC.
+config GOLDFISH_TIMER
+ bool "Goldfish Timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the Goldfish Timer.
+ It uses the Goldfish RTC hardware to provide a nanosecond-resolution
+ timer, commonly found in QEMU virt machines.
+
endmenu
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index a72e411fb2f..d8b3f2b65d4 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_MCHP_PIT64B_TIMER) += mchp-pit64b-timer.o
obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o
obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o
obj-$(CONFIG_STARFIVE_TIMER) += starfive-timer.o
+obj-$(CONFIG_GOLDFISH_TIMER) += goldfish_timer.o
diff --git a/drivers/timer/goldfish_timer.c b/drivers/timer/goldfish_timer.c
new file mode 100644
index 00000000000..70673bbd93c
--- /dev/null
+++ b/drivers/timer/goldfish_timer.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ *
+ * Goldfish Timer driver
+ */
+
+#include <dm.h>
+#include <goldfish_timer.h>
+#include <mapmem.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+
+struct goldfish_timer_priv {
+ void __iomem *base;
+};
+
+/* Goldfish RTC registers used as Timer */
+#define TIMER_TIME_LOW 0x00
+#define TIMER_TIME_HIGH 0x04
+
+static u64 goldfish_timer_get_count(struct udevice *dev)
+{
+ struct goldfish_timer_priv *priv = dev_get_priv(dev);
+ u32 low, high;
+ u64 time;
+
+ /*
+ * TIMER_TIME_HIGH is only updated when TIMER_TIME_LOW is read.
+ * 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);
+
+ time = ((u64)high << 32) | low;
+
+ return time;
+}
+
+static int goldfish_timer_of_to_plat(struct udevice *dev)
+{
+ struct goldfish_timer_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr != FDT_ADDR_T_NONE)
+ plat->reg = addr;
+
+ return 0;
+}
+
+static int goldfish_timer_probe(struct udevice *dev)
+{
+ struct goldfish_timer_plat *plat = dev_get_plat(dev);
+ struct goldfish_timer_priv *priv = dev_get_priv(dev);
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (!plat->reg)
+ return -EINVAL;
+
+ priv->base = map_sysmem(plat->reg, 0x20);
+
+ /* Goldfish RTC counts in nanoseconds, so the rate is 1GHz */
+ uc_priv->clock_rate = 1000000000;
+
+ return 0;
+}
+
+static const struct timer_ops goldfish_timer_ops = {
+ .get_count = goldfish_timer_get_count,
+};
+
+static const struct udevice_id goldfish_timer_ids[] = {
+ { .compatible = "google,goldfish-rtc" },
+ { }
+};
+
+U_BOOT_DRIVER(goldfish_timer) = {
+ .name = "goldfish_timer",
+ .id = UCLASS_TIMER,
+ .of_match = goldfish_timer_ids,
+ .of_to_plat = goldfish_timer_of_to_plat,
+ .plat_auto = sizeof(struct goldfish_timer_plat),
+ .ops = &goldfish_timer_ops,
+ .probe = goldfish_timer_probe,
+ .priv_auto = sizeof(struct goldfish_timer_priv),
+};
diff --git a/include/configs/qemu-m68k.h b/include/configs/qemu-m68k.h
new file mode 100644
index 00000000000..1d8aa92fe80
--- /dev/null
+++ b/include/configs/qemu-m68k.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#ifndef __QEMU_M68K_H
+#define __QEMU_M68K_H
+
+/* Memory Configuration */
+#define CFG_SYS_SDRAM_BASE 0x00000000
+
+/*
+ * Initial Stack Pointer:
+ * Place the stack at 4MB offset to avoid overwriting U-Boot code/data.
+ */
+#define CFG_SYS_INIT_SP_ADDR (CFG_SYS_SDRAM_BASE + 0x400000)
+
+#endif /* __QEMU_M68K_H */
diff --git a/include/goldfish_rtc.h b/include/goldfish_rtc.h
new file mode 100644
index 00000000000..f0e6ba3d543
--- /dev/null
+++ b/include/goldfish_rtc.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#ifndef _GOLDFISH_RTC_H_
+#define _GOLDFISH_RTC_H_
+
+#include <linux/types.h>
+
+struct goldfish_rtc_plat {
+ phys_addr_t reg;
+};
+
+#endif /* _GOLDFISH_RTC_H_ */
diff --git a/include/goldfish_timer.h b/include/goldfish_timer.h
new file mode 100644
index 00000000000..acd3e460c5a
--- /dev/null
+++ b/include/goldfish_timer.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#ifndef _GOLDFISH_TIMER_H_
+#define _GOLDFISH_TIMER_H_
+
+struct goldfish_timer_plat {
+ phys_addr_t reg;
+};
+
+#endif /* _GOLDFISH_TIMER_H_ */
diff --git a/include/goldfish_tty.h b/include/goldfish_tty.h
new file mode 100644
index 00000000000..db49c8d6344
--- /dev/null
+++ b/include/goldfish_tty.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#ifndef _GOLDFISH_TTY_H_
+#define _GOLDFISH_TTY_H_
+
+#include <linux/types.h>
+
+/* Platform data for the Goldfish TTY driver
+ * Used to pass hardware base address from Board to Driver
+ */
+struct goldfish_tty_plat {
+ phys_addr_t reg;
+};
+
+#endif /* _GOLDFISH_TTY_H_ */
diff --git a/include/qemu_virt_ctrl.h b/include/qemu_virt_ctrl.h
new file mode 100644
index 00000000000..05aad82b767
--- /dev/null
+++ b/include/qemu_virt_ctrl.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
+ */
+
+#ifndef _QEMU_VIRT_CTRL_H_
+#define _QEMU_VIRT_CTRL_H_
+
+struct qemu_virt_ctrl_plat {
+ phys_addr_t reg;
+};
+
+#endif /* _QEMU_VIRT_CTRL_H_ */