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 -M: Angelo Dureghello -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 M: Hou Zhiqiang @@ -1263,6 +1254,18 @@ S: Maintained F: drivers/misc/gsc.c F: include/gsc.h +GOLDFISH SERIAL DRIVER +M: Kuan-Wei Chiu +S: Maintained +F: drivers/serial/serial_goldfish.c +F: include/goldfish_tty.h + +GOLDFISH TIMER DRIVER +M: Kuan-Wei Chiu +S: Maintained +F: drivers/timer/goldfish_timer.c +F: include/goldfish_timer.h + INTERCONNECT: M: Neil Armstrong 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 +S: Maintained +F: arch/m68k/cpu/m680x0/ +F: arch/m68k/include/asm/bootinfo.h + +M68K +M: Angelo Dureghello +M: Kuan-Wei Chiu +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 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 +S: Maintained +F: drivers/sysreset/sysreset_qemu_virt_ctrl.c +F: include/qemu_virt_ctrl.h + RAW NAND M: Dario Binacchi M: Michael Trimarchi 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, wd@denx.de. -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 + +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 + */ + +#include +#include +#include +#include +#include +#include + +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 + */ + +#include +#include +#include + +.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 + */ + +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 + * + * 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 +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 + +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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +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 #include +#include #include #include #include @@ -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 + * Goldfish TTY driver for U-Boot + */ + +#include +#include +#include +#include +#include +#include + +/* 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 + * + * QEMU Virtual System Controller Driver + */ + +#include +#include +#include +#include +#include + +/* 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 + * + * Goldfish Timer driver + */ + +#include +#include +#include +#include +#include +#include + +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 + */ + +#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 + */ + +#ifndef _GOLDFISH_RTC_H_ +#define _GOLDFISH_RTC_H_ + +#include + +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 + */ + +#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 + */ + +#ifndef _GOLDFISH_TTY_H_ +#define _GOLDFISH_TTY_H_ + +#include + +/* 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 + */ + +#ifndef _QEMU_VIRT_CTRL_H_ +#define _QEMU_VIRT_CTRL_H_ + +struct qemu_virt_ctrl_plat { + phys_addr_t reg; +}; + +#endif /* _QEMU_VIRT_CTRL_H_ */