[PATCH 6/6] powerpc: add QEMU ppce500 board support with CI test infrastructure

Ahmad Fatoum
2026-02-09T09:15:54Z

Add support for running barebox on the QEMU ppce500 virtual machine.
This enables automated testing of PowerPC e500 support in CI.

The ppce500 machine places CCSRBAR at effective address 0xE0000000
(physical 0xF_E0000000 with 36-bit addressing). The assembly entry
sets up TLB entries for RAM, CCSRBAR MMIO, and the boot page, then
jumps to C code which parses the timebase-frequency from the QEMU-
provided device tree to configure the clocksource correctly (QEMU
does not implement the GUTS PORPLLSR register).

Poweroff is handled by the existing gpio-poweroff driver, which uses
the fsl,qoriq-gpio controller exposed in QEMU's device tree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
.github/workflows/test-labgrid-pytest.yml | 4 +
arch/powerpc/Makefile | 1 +
arch/powerpc/boards/qemu-e500/Makefile | 5 +
arch/powerpc/boards/qemu-e500/law.c | 17 ++
arch/powerpc/boards/qemu-e500/qemu-e500.c | 92 ++++++++
arch/powerpc/boards/qemu-e500/tlb.c | 17 ++
arch/powerpc/configs/qemu-ppce500_defconfig | 69 ++++++
arch/powerpc/cpu-85xx/Makefile | 3 +
arch/powerpc/cpu-85xx/start-qemu.S | 211 ++++++++++++++++++
arch/powerpc/include/asm/board-qemu-e500.h | 39 ++++
arch/powerpc/include/asm/config.h | 2 +
arch/powerpc/include/asm/debug_ll.h | 52 +++++
arch/powerpc/lib/board.c | 6 +
arch/powerpc/mach-mpc85xx/Kconfig | 13 ++
arch/powerpc/mach-mpc85xx/barebox.lds.S | 12 +
.../include/mach/config_mpc85xx.h | 6 +
common/Kconfig.debug_ll | 7 +-
test/powerpc/qemu-ppce500_defconfig.yaml | 17 ++
18 files changed, 572 insertions(+), 1 deletion(-)
create mode 100644 arch/powerpc/boards/qemu-e500/Makefile
create mode 100644 arch/powerpc/boards/qemu-e500/law.c
create mode 100644 arch/powerpc/boards/qemu-e500/qemu-e500.c
create mode 100644 arch/powerpc/boards/qemu-e500/tlb.c
create mode 100644 arch/powerpc/configs/qemu-ppce500_defconfig
create mode 100644 arch/powerpc/cpu-85xx/start-qemu.S
create mode 100644 arch/powerpc/include/asm/board-qemu-e500.h
create mode 100644 arch/powerpc/include/asm/debug_ll.h
create mode 100644 test/powerpc/qemu-ppce500_defconfig.yaml

diff --git a/.github/workflows/test-labgrid-pytest.yml b/.github/workflows/test-labgrid-pytest.yml
index c06bc5f11faa..7e385af65245 100644
--- a/.github/workflows/test-labgrid-pytest.yml
+++ b/.github/workflows/test-labgrid-pytest.yml
@@ -49,6 +49,10 @@ jobs:
lgenv: test/openrisc/generic_defconfig.yaml
defconfig: generic_defconfig

+ - ARCH: powerpc
+ lgenv: test/powerpc/qemu-ppce500_defconfig.yaml
+ defconfig: qemu-ppce500_defconfig
+
- ARCH: x86
lgenv: test/x86/efi_defconfig.yaml
defconfig: efi_defconfig
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index ebd8fe60d3f1..fa511c6c1295 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -18,6 +18,7 @@ KBUILD_CPPFLAGS += -Wa,-me500x2 -msoft-float -mno-string
endif

board-$(CONFIG_MACH_PHYCORE_MPC5200B_TINY) := pcm030
+board-$(CONFIG_QEMU_PPCE500) := qemu-e500
board-$(CONFIG_P1010RDB) := freescale-p1010rdb
board-$(CONFIG_P2020RDB) := freescale-p2020rdb
board-$(CONFIG_P1022DS) := freescale-p1022ds
diff --git a/arch/powerpc/boards/qemu-e500/Makefile b/arch/powerpc/boards/qemu-e500/Makefile
new file mode 100644
index 000000000000..15add493eea8
--- /dev/null
+++ b/arch/powerpc/boards/qemu-e500/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += qemu-e500.o
+obj-y += tlb.o
+obj-y += law.o
diff --git a/arch/powerpc/boards/qemu-e500/law.c b/arch/powerpc/boards/qemu-e500/law.c
new file mode 100644
index 000000000000..97683b01a573
--- /dev/null
+++ b/arch/powerpc/boards/qemu-e500/law.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LAW configuration for QEMU ppce500
+ *
+ * QEMU virtual hardware doesn't require LAW (Local Access Window)
+ * configuration, so we provide an empty table.
+ */
+
+#include <linux/types.h>
+#include <linux/array_size.h>
+#include <asm/fsl_law.h>
+
+struct law_entry law_table[] = {
+ /* No LAW entries needed for QEMU virtual hardware */
+};
+
+int num_law_entries = ARRAY_SIZE(law_table);
diff --git a/arch/powerpc/boards/qemu-e500/qemu-e500.c b/arch/powerpc/boards/qemu-e500/qemu-e500.c
new file mode 100644
index 000000000000..70f22aa9b118
--- /dev/null
+++ b/arch/powerpc/boards/qemu-e500/qemu-e500.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Board support for QEMU ppce500 virtual machine
+ */
+
+#include <barebox-info.h>
+#include <deep-probe.h>
+#include <init.h>
+#include <of.h>
+#include <linux/libfdt.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <mach/mpc85xx.h>
+#include <mach/clock.h>
+#include <debug_ll.h>
+
+/* Global pointer to device tree */
+static void *fdt;
+
+/* Called from start-qemu.S assembly */
+void qemu_e500_entry(void *fdt_blob);
+
+/* Called from board initialization */
+extern void board_init_r(ulong end_of_ram);
+
+void qemu_e500_entry(void *fdt_blob)
+{
+ int cpus_off, cpu_off, len;
+ const fdt32_t *prop;
+
+ /* Initialize debug UART first */
+ debug_ll_init();
+
+ putc_ll('Q');
+ putc_ll('E');
+ putc_ll('M');
+ putc_ll('U');
+ putc_ll('\r');
+ putc_ll('\n');
+
+ /* Save FDT to global for later use */
+ fdt = fdt_blob;
+
+ /*
+ * Parse timebase-frequency from QEMU's FDT. The MPC85xx GUTS
+ * PORPLLSR register is unimplemented in QEMU and reads as zero,
+ * so the normal register-based clock calculation fails.
+ */
+ cpus_off = fdt_path_offset(fdt_blob, "/cpus");
+ if (cpus_off >= 0) {
+ cpu_off = fdt_first_subnode(fdt_blob, cpus_off);
+ if (cpu_off >= 0) {
+ prop = fdt_getprop(fdt_blob, cpu_off,
+ "timebase-frequency", &len);
+ if (prop && len == sizeof(*prop))
+ fsl_set_timebase_clock(fdt32_to_cpu(*prop));
+ }
+ }
+
+ putc_ll('F');
+ putc_ll('D');
+ putc_ll('T');
+ putc_ll(':');
+ puthex_ll((unsigned long)fdt_blob);
+ putc_ll('\r');
+ putc_ll('\n');
+
+ putc_ll('B');
+ putc_ll('R');
+ putc_ll('\r');
+ putc_ll('\n');
+
+ board_init_r(256 * 1024 * 1024);
+}
+
+static int qemu_e500_of_init(void)
+{
+ if (!fdt)
+ return 0;
+
+ barebox_set_model("QEMU ppce500");
+ barebox_set_hostname("qemu-e500");
+
+ return barebox_register_fdt(fdt);
+}
+core_initcall(qemu_e500_of_init);
+
+static const struct of_device_id qemu_e500_of_match[] = {
+ { .compatible = "fsl,qemu-e500" },
+ { /* sentinel */ },
+};
+BAREBOX_DEEP_PROBE_ENABLE(qemu_e500_of_match);
diff --git a/arch/powerpc/boards/qemu-e500/tlb.c b/arch/powerpc/boards/qemu-e500/tlb.c
new file mode 100644
index 000000000000..a0f31a6fa85d
--- /dev/null
+++ b/arch/powerpc/boards/qemu-e500/tlb.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TLB configuration for QEMU ppce500
+ *
+ * QEMU sets up TLB entries before jumping to firmware, so we
+ * provide a minimal table here just for the API.
+ */
+
+#include <linux/types.h>
+#include <linux/array_size.h>
+#include <mach/mmu.h>
+
+struct fsl_e_tlb_entry tlb_table[] = {
+ /* QEMU already configured TLB - empty table */
+};
+
+int num_tlb_entries = ARRAY_SIZE(tlb_table);
diff --git a/arch/powerpc/configs/qemu-ppce500_defconfig b/arch/powerpc/configs/qemu-ppce500_defconfig
new file mode 100644
index 000000000000..6a0e070f4996
--- /dev/null
+++ b/arch/powerpc/configs/qemu-ppce500_defconfig
@@ -0,0 +1,69 @@
+CONFIG_ARCH_MPC85XX=y
+CONFIG_QEMU_PPCE500=y
+CONFIG_NAME="qemu-ppce500_defconfig"
+CONFIG_MALLOC_SIZE=0x100000
+CONFIG_RELOCATABLE=y
+CONFIG_CMDLINE_EDITING=y
+CONFIG_AUTO_COMPLETE=y
+CONFIG_BOOTM_VERBOSE=y
+CONFIG_DEBUG_LL=y
+CONFIG_CMD_DMESG=y
+CONFIG_LONGHELP=y
+CONFIG_CMD_IOMEM=y
+CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_NVMEM=y
+CONFIG_CMD_GO=y
+CONFIG_CMD_LOADY=y
+CONFIG_CMD_RESET=y
+CONFIG_CMD_PARTITION=y
+CONFIG_CMD_FINDMNT=y
+CONFIG_CMD_EXPORT=y
+CONFIG_CMD_DEFAULTENV=y
+CONFIG_CMD_LOADENV=y
+CONFIG_CMD_PRINTENV=y
+CONFIG_CMD_MAGICVAR=y
+CONFIG_CMD_MAGICVAR_HELP=y
+CONFIG_CMD_SAVEENV=y
+CONFIG_CMD_FILETYPE=y
+CONFIG_CMD_LN=y
+CONFIG_CMD_MD5SUM=y
+CONFIG_CMD_SHA1SUM=y
+CONFIG_CMD_SHA256SUM=y
+CONFIG_CMD_LET=y
+CONFIG_CMD_MSLEEP=y
+CONFIG_CMD_READF=y
+CONFIG_CMD_SLEEP=y
+CONFIG_CMD_ECHO_E=y
+CONFIG_CMD_EDIT=y
+CONFIG_CMD_READLINE=y
+CONFIG_CMD_TIMEOUT=y
+CONFIG_CMD_CRC=y
+CONFIG_CMD_CRC_CMP=y
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_DETECT=y
+CONFIG_CMD_FLASH=y
+CONFIG_CMD_POWEROFF=y
+CONFIG_CMD_OF_DIFF=y
+CONFIG_CMD_OF_NODE=y
+CONFIG_CMD_OF_PROPERTY=y
+CONFIG_CMD_OF_FIXUP_STATUS=y
+CONFIG_CMD_OFTREE=y
+CONFIG_CMD_TIME=y
+CONFIG_OFDEVICE=y
+CONFIG_OF_BAREBOX_DRIVERS=y
+CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_GPIO_MPC8XXX=y
+CONFIG_NVMEM=y
+CONFIG_NVMEM_RMEM=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_TEST=y
+CONFIG_SELFTEST=y
+CONFIG_SELFTEST_BASE64=y
+CONFIG_SELFTEST_RANGE=y
+CONFIG_SELFTEST_MALLOC=y
+CONFIG_SELFTEST_PRINTF=y
+CONFIG_SELFTEST_OF_MANIPULATION=y
+CONFIG_SELFTEST_PROGRESS_NOTIFIER=y
+CONFIG_SELFTEST_STRING=y
+CONFIG_SELFTEST_RESOURCE=y
+CONFIG_SELFTEST_IDR=y
diff --git a/arch/powerpc/cpu-85xx/Makefile b/arch/powerpc/cpu-85xx/Makefile
index cc85eb759472..a902ecb9c659 100644
--- a/arch/powerpc/cpu-85xx/Makefile
+++ b/arch/powerpc/cpu-85xx/Makefile
@@ -4,5 +4,8 @@ obj-y += traps.o
obj-y += tlb.o
obj-y += cache.o
obj-$(CONFIG_MMU) += mmu.o
+extra-$(CONFIG_QEMU_PPCE500) += start-qemu.o
+ifndef CONFIG_QEMU_PPCE500
extra-y += start.o
extra-y += resetvec.o
+endif
diff --git a/arch/powerpc/cpu-85xx/start-qemu.S b/arch/powerpc/cpu-85xx/start-qemu.S
new file mode 100644
index 000000000000..fd3a9aeec27e
--- /dev/null
+++ b/arch/powerpc/cpu-85xx/start-qemu.S
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ePAPR-compliant entry point for QEMU e500 platforms (mpc8544ds/ppce500)
+ *
+ * QEMU sets up the following CPU state before jumping to firmware:
+ * - r1 = 16MB - 8 (initial stack)
+ * - r3 = FDT physical address
+ * - r6 = EPAPR_MAGIC (0x45504150)
+ * - r7 = Initial TLB map size
+ * - TLB1[0] = Identity map covering RAM + FDT
+ */
+
+#include <asm/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.tmpl>
+#include <asm/ppc_defs.h>
+#include <asm/cache.h>
+#include <mach/mmu.h>
+
+#undef MSR_KERNEL
+#define MSR_KERNEL (MSR_ME) /* Machine Check */
+
+ .section .text_entry
+ .globl _start_qemu_e500
+ .globl _start
+
+_start:
+_start_qemu_e500:
+ /*
+ * Entry from QEMU with ePAPR boot convention:
+ * r3 = FDT pointer (must be preserved)
+ * r6 = EPAPR_MAGIC
+ * r1 = initial stack (16MB - 8)
+ * TLB already set up by QEMU for RAM
+ */
+
+ /* Save FDT pointer to r31 (callee-saved) */
+ mr r31, r3
+
+ /* Enable machine check exception */
+ li r0, MSR_KERNEL
+ mtmsr r0
+ isync
+
+ /* Invalidate L1 caches */
+ li r0, 2
+ mtspr L1CSR0, r0 /* invalidate d-cache */
+ mtspr L1CSR1, r0 /* invalidate i-cache */
+ isync
+
+ /* Clear debug status */
+ mfspr r0, DBSR
+ mtspr DBSR, r0
+ isync
+
+ /* Clear and set up timer/exception registers */
+ li r0, 0
+ lis r1, 0xffff
+ mtspr DEC, r0 /* prevent dec exceptions */
+ mttbl r0 /* prevent fit & wdt exceptions */
+ mttbu r0
+ mtspr TSR, r1 /* clear all timer exception status */
+ mtspr TCR, r0 /* disable all timers */
+ mtspr ESR, r0 /* clear exception syndrome register */
+ mtspr MCSR, r0 /* machine check syndrome register */
+ mtxer r0 /* clear integer exception register */
+
+ /* Enable machine check and timebase in HID0 */
+ lis r0, HID0_EMCP@h
+ ori r0, r0, HID0_TBEN@l
+ mtspr HID0, r0
+
+ /* Setup IVPR for exception vectors */
+ lis r0, TEXT_BASE@h
+ ori r0, r0, TEXT_BASE@l
+ mtspr IVPR, r0
+ isync
+
+ /*
+ * Set up TLB1[1]: Map CCSRBAR (16MB, cache-inhibited, guarded)
+ *
+ * mpc8544ds: CCSRBAR at 0x0_E0000000 (MAS7 = 0x0)
+ * ppce500: CCSRBAR at 0xF_E0000000 (MAS7 = 0xF)
+ */
+ lis r0, FSL_BOOKE_MAS0(1, 1, 0)@h
+ ori r0, r0, FSL_BOOKE_MAS0(1, 1, 0)@l
+ mtspr MAS0, r0
+ lis r0, FSL_BOOKE_MAS1(1, 1, 0, 0, BOOKE_PAGESZ_16M)@h
+ ori r0, r0, FSL_BOOKE_MAS1(1, 1, 0, 0, BOOKE_PAGESZ_16M)@l
+ mtspr MAS1, r0
+ lis r0, FSL_BOOKE_MAS2(CFG_CCSRBAR, (MAS2_I | MAS2_G))@h
+ ori r0, r0, FSL_BOOKE_MAS2(CFG_CCSRBAR, (MAS2_I | MAS2_G))@l
+ mtspr MAS2, r0
+ lis r0, FSL_BOOKE_MAS3(CFG_CCSRBAR, 0, (MAS3_SX | MAS3_SW | MAS3_SR))@h
+ ori r0, r0, FSL_BOOKE_MAS3(CFG_CCSRBAR, 0, (MAS3_SX | MAS3_SW | MAS3_SR))@l
+ mtspr MAS3, r0
+ li r0, CFG_CCSRBAR_PHYS_HIGH
+ mtspr MAS7, r0
+ isync
+ msync
+ tlbwe
+ isync
+
+ /*
+ * Clear BSS before calling C code.
+ */
+ lis r3, __bss_start@h
+ ori r3, r3, __bss_start@l
+ lis r4, __bss_stop@h
+ ori r4, r4, __bss_stop@l
+ li r0, 0
+ cmplw 0, r3, r4
+ beq 2f
+1: stw r0, 0(r3)
+ addi r3, r3, 4
+ cmplw 0, r3, r4
+ blt 1b
+2:
+
+ /* Restore stack pointer (was clobbered by timer setup above) */
+ lis r1, (16 * 1024 * 1024 - 8)@h
+ ori r1, r1, (16 * 1024 * 1024 - 8)@l
+
+ /* Clear LR for clean stack traces */
+ li r0, 0
+ mtlr r0
+
+ /* Jump to C entry with FDT pointer in r3 */
+ mr r3, r31
+ lis r4, qemu_e500_entry@h
+ ori r4, r4, qemu_e500_entry@l
+ mtctr r4
+ bctr
+
+ /* Should never return */
+ b .
+
+/*
+ * Low-level helper functions required by barebox
+ */
+
+/* get_svr: Return System Version Register */
+ .globl get_svr
+get_svr:
+ mfspr r3, SVR
+ blr
+
+/* invalidate_icache: Invalidate instruction cache */
+ .globl invalidate_icache
+invalidate_icache:
+ mfspr r0, L1CSR1
+ ori r0, r0, L1CSR1_ICFI
+ msync
+ isync
+ mtspr L1CSR1, r0
+ isync
+ blr
+
+/* flush_dcache: Flush data cache */
+ .globl flush_dcache
+flush_dcache:
+ mfspr r3, SPRN_L1CFG0
+ rlwinm r5, r3, 9, 3
+ li r4, 32
+ subfic r6, r5, 2
+ slw r5, r4, r5
+ rlwinm r7, r3, 0, 0xff
+ mulli r7, r7, 13
+ slw r7, r7, r6
+
+ mfspr r8, SPRN_HID0
+ ori r9, r8, HID0_DCFA@l
+ mtspr SPRN_HID0, r9
+ isync
+
+ lis r4, 0
+ mtctr r7
+
+1: lwz r3, 0(r4)
+ add r4, r4, r5
+ bdnz 1b
+
+ mtspr SPRN_HID0, r8
+ isync
+ blr
+
+/* trap_init: Initialize exception vectors - simplified for QEMU */
+ .globl trap_init
+trap_init:
+ blr
+
+/*
+ * void e500_write_tlb(mas0, mas1, mas2, mas3, mas7)
+ */
+ .globl e500_write_tlb
+e500_write_tlb:
+ mtspr MAS0, r3
+ mtspr MAS1, r4
+ mtspr MAS2, r5
+ mtspr MAS3, r6
+ mtspr MAS7, r7
+ isync
+ tlbwe
+ msync
+ isync
+ blr
+
+/* _text_base: Text base address variable */
+ .globl _text_base
+_text_base:
+ .long TEXT_BASE
diff --git a/arch/powerpc/include/asm/board-qemu-e500.h b/arch/powerpc/include/asm/board-qemu-e500.h
new file mode 100644
index 000000000000..625d27b5185d
--- /dev/null
+++ b/arch/powerpc/include/asm/board-qemu-e500.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QEMU ppce500 virtual machine configuration
+ *
+ * CCSRBAR effective address is 0xE0000000, physical 0xF_E0000000
+ * (36-bit, MAS7 = 0xF).
+ */
+
+#ifndef __ASM_BOARD_QEMU_E500_H
+#define __ASM_BOARD_QEMU_E500_H
+
+/*
+ * Serial clock - QEMU's ns16550 baudbase is 399193, but we use
+ * 1843200 (standard 16550 crystal) so the divisor calculation
+ * produces a reasonable value. QEMU ignores the actual baud rate.
+ */
+#define CFG_SYS_CLK_FREQ 1843200
+
+/*
+ * DDR configuration - QEMU doesn't have a real DDR controller,
+ * but the define is needed for compilation.
+ */
+#define CFG_CHIP_SELECTS_PER_CTRL 1
+
+#define CFG_SDRAM_BASE 0x00000000
+
+/*
+ * CCSR (CCSRBAR) - SoC register space
+ *
+ * QEMU ppce500 maps CCSRBAR at effective address 0xE0000000,
+ * physical address 0xF_E0000000 (36-bit, MAS7 = 0xF).
+ */
+#define CFG_CCSRBAR_DEFAULT 0xE0000000
+#define CFG_CCSRBAR 0xE0000000
+#define CFG_CCSRBAR_PHYS CFG_CCSRBAR
+#define CFG_CCSRBAR_PHYS_HIGH 0xF
+#define CFG_IMMR CFG_CCSRBAR
+
+#endif /* __ASM_BOARD_QEMU_E500_H */
diff --git a/arch/powerpc/include/asm/config.h b/arch/powerpc/include/asm/config.h
index 727daa872f20..59965095b92e 100644
--- a/arch/powerpc/include/asm/config.h
+++ b/arch/powerpc/include/asm/config.h
@@ -39,6 +39,8 @@

#ifdef CONFIG_MACH_PHYCORE_MPC5200B_TINY
#include <asm/board-pcm030.h>
+#elif defined(CONFIG_QEMU_PPCE500)
+#include <asm/board-qemu-e500.h>
#elif defined(CONFIG_P1010RDB)
#include <asm/board-p1010rdb.h>
#elif defined(CONFIG_P2020RDB)
diff --git a/arch/powerpc/include/asm/debug_ll.h b/arch/powerpc/include/asm/debug_ll.h
new file mode 100644
index 000000000000..910db2808d81
--- /dev/null
+++ b/arch/powerpc/include/asm/debug_ll.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __ASM_DEBUG_LL_H__
+#define __ASM_DEBUG_LL_H__
+
+#include <io.h>
+
+#ifdef CONFIG_DEBUG_QEMU_PPCE500
+
+#include <asm/board-qemu-e500.h>
+
+/*
+ * QEMU ppce500 NS16550 UART at CCSRBAR + 0x4500
+ * Clock is platform clock (400 MHz default)
+ */
+#define DEBUG_LL_UART_BASE IOMEM(CFG_CCSRBAR + 0x4500)
+#define DEBUG_LL_UART_CLK CFG_SYS_CLK_FREQ
+
+static inline uint8_t debug_ll_read_reg(void __iomem *base, int reg)
+{
+ return readb(base + reg);
+}
+
+static inline void debug_ll_write_reg(void __iomem *base, int reg, uint8_t val)
+{
+ writeb(val, base + reg);
+}
+
+#include <debug_ll/ns16550.h>
+
+static inline void debug_ll_init(void)
+{
+ uint16_t divisor;
+
+ divisor = debug_ll_ns16550_calc_divisor(DEBUG_LL_UART_CLK);
+ debug_ll_ns16550_init(DEBUG_LL_UART_BASE, divisor);
+}
+
+static inline void PUTC_LL(int c)
+{
+ debug_ll_ns16550_putc(DEBUG_LL_UART_BASE, c);
+}
+
+#else
+
+static inline void debug_ll_init(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_QEMU_PPCE500 */
+
+#endif /* __ASM_DEBUG_LL_H__ */
diff --git a/arch/powerpc/lib/board.c b/arch/powerpc/lib/board.c
index a6111606b68c..3781345926a4 100644
--- a/arch/powerpc/lib/board.c
+++ b/arch/powerpc/lib/board.c
@@ -43,7 +43,13 @@ void board_init_r (ulong end_of_ram)
asm ("sync ; isync");

#ifdef CONFIG_MPC85xx
+#ifndef CONFIG_QEMU_PPCE500
+ /* Traditional boards relocate to end of RAM */
_text_base = end_of_ram;
+#else
+ /* QEMU: no relocation, stay at link address */
+ _text_base = TEXT_BASE;
+#endif
#endif

malloc_end = (_text_base - STACK_SIZE) & ~(4095);
diff --git a/arch/powerpc/mach-mpc85xx/Kconfig b/arch/powerpc/mach-mpc85xx/Kconfig
index 550c554286d7..7f184babbee1 100644
--- a/arch/powerpc/mach-mpc85xx/Kconfig
+++ b/arch/powerpc/mach-mpc85xx/Kconfig
@@ -11,11 +11,13 @@ config BTB

config TEXT_BASE
hex
+ default 0x00f00000 if QEMU_PPCE500
default 0xeff80000 if P1010RDB || P2020RDB || P1022DS
default 0xfff80000 if DA923RC

config RESET_VECTOR_ADDRESS
hex
+ default 0x00000000 if QEMU_PPCE500
default 0xfffffffc if DA923RC
default 0xeffffffc if P1010RDB || P2020RDB || P1022DS

@@ -33,6 +35,7 @@ config E500

choice
prompt "Select your board"
+
config P1010RDB
bool "P1010RDB"
select P1010
@@ -64,6 +67,16 @@ config DA923RC
select FSL_DDR2
help
Say Y here if you are using the GE Intelligent Platforms DA923RC
+
+config QEMU_PPCE500
+ bool "QEMU ppce500"
+ select HAS_DEBUG_LL
+ select LIBFDT
+ select GPIOLIB
+ help
+ Say Y here for QEMU ppce500 generic paravirt e500 support.
+ CCSRBAR is at 36-bit physical address 0xFE0000000.
+
endchoice
endif

diff --git a/arch/powerpc/mach-mpc85xx/barebox.lds.S b/arch/powerpc/mach-mpc85xx/barebox.lds.S
index 5407ecc7e295..989fec7f1def 100644
--- a/arch/powerpc/mach-mpc85xx/barebox.lds.S
+++ b/arch/powerpc/mach-mpc85xx/barebox.lds.S
@@ -22,7 +22,11 @@
#endif

OUTPUT_ARCH(BAREBOX_OUTPUT_ARCH)
+#ifdef CONFIG_QEMU_PPCE500
+ENTRY(_start_qemu_e500)
+#else
ENTRY(_start_e500)
+#endif

PHDRS
{
@@ -122,6 +126,8 @@ SECTIONS

__init_size = __init_end - _start;

+#ifndef CONFIG_QEMU_PPCE500
+ /* Boot page and reset vector for flash boot */
.bootpg RESET_VECTOR_ADDRESS - 0xffc :
{
_text = .;
@@ -140,6 +146,12 @@ SECTIONS
/* This avoids wrapping around to offset 0 */
. |= 0x10;
#endif
+#else
+ /* QEMU entry point: simpler, no boot page or reset vector needed */
+ .text_entry : {
+ arch/powerpc/cpu-85xx/start-qemu.o (.text_entry)
+ } :text
+#endif

__bss_start = .;
.bss :
diff --git a/arch/powerpc/mach-mpc85xx/include/mach/config_mpc85xx.h b/arch/powerpc/mach-mpc85xx/include/mach/config_mpc85xx.h
index fad2c47d110f..0a53282c623c 100644
--- a/arch/powerpc/mach-mpc85xx/include/mach/config_mpc85xx.h
+++ b/arch/powerpc/mach-mpc85xx/include/mach/config_mpc85xx.h
@@ -57,6 +57,12 @@
#define PPC_E500_DEBUG_TLB 2
#define FSL_TSECV2

+#elif defined(CONFIG_QEMU_PPCE500)
+#define MAX_CPUS 1
+#define FSL_NUM_LAWS 12
+#define PPC_E500_DEBUG_TLB 0
+/* QEMU e500: generic e500v2 platform, minimal configuration */
+
#else
#error Processor type not defined for this platform
#endif
diff --git a/common/Kconfig.debug_ll b/common/Kconfig.debug_ll
index a08d29859d27..b11df2b79925 100644
--- a/common/Kconfig.debug_ll
+++ b/common/Kconfig.debug_ll
@@ -311,6 +311,11 @@ config DEBUG_ZYNQMP_UART
Say Y here if you want kernel low-level debugging support
on Zynqmp.

+config DEBUG_QEMU_PPCE500
+ bool "Qemu ppce500 PowerPC ns16550 port"
+ depends on QEMU_PPCE500
+ select DEBUG_LL_NS16550
+
config DEBUG_ERIZO
bool "Erizo ns16550 port"
depends on SOC_ERIZO
@@ -382,7 +387,7 @@ endchoice
config DEBUG_LL_NS16550
bool
help
- Selected by RISC-V platforms that use ns16550 for debug_ll
+ Selected by RISC-V and PowerPC platforms that use ns16550 for debug_ll

config DEBUG_IMX_UART_PORT
int "i.MX Debug UART Port Selection" if DEBUG_IMX1_UART || \
diff --git a/test/powerpc/qemu-ppce500_defconfig.yaml b/test/powerpc/qemu-ppce500_defconfig.yaml
new file mode 100644
index 000000000000..d64f42fc6eca
--- /dev/null
+++ b/test/powerpc/qemu-ppce500_defconfig.yaml
@@ -0,0 +1,17 @@
+targets:
+ main:
+ drivers:
+ QEMUDriver:
+ qemu_bin: qemu-system-ppc
+ machine: ppce500
+ cpu: e500
+ memory: 256M
+ kernel: barebox
+ display: qemu-default
+ BareboxDriver:
+ prompt: 'barebox:[^ ]+ '
+ BareboxTestStrategy: {}
+images:
+ barebox: !template "$LG_BUILDDIR/barebox"
+imports:
+ - ../strategy.py
--
2.47.3