Hi Alex, Thanks for the review. I have looked at the impact of these comments, and for v3 I propose to:
* split the current patch into two focused patches: one for SYSINFO and one for SYSCFG; * remove the powered-off memory overlays. They were intended to make CPU and DMA accesses fail after setting MEMPOWERDOWN, but this goes beyond the documented register behavior; * retain the documented MEMPOWERDOWN register state, reset value, and write mask, without claiming to model physical ROM or SRAM power gating; * route PROC0_NMI_MASK and PROC1_NMI_MASK independently to the corresponding core; * remove the MemoryRegionOps.with_attrs callbacks, since they are only needed by the powered-off memory overlays; * remove the per-subsystem RP2040 Kconfig symbols and build the internal devices under the single CONFIG_RP2040 symbol. I will add focused qtests to the separate SYSINFO and SYSCFG patches. Does this sound in the right direction? Thanks, -- Gilles Grimaud - 2XS/SISE - CRIStAL/IRCICA - CNRS/Univ. Lille > Le 1 sept. 2026 à 20:44, Alex Bennée <[email protected]> a écrit : > > Gilles Grimaud <[email protected] > <mailto:[email protected]>> writes: > >> From: gilles grimaud <[email protected] >> <mailto:[email protected]>> >> >> Model the RP2040 identification and system configuration registers, >> including APB atomic aliases and documented reset and write masks. >> Wire SYSCFG into the SoC so NMI masks reroute interrupt inputs and >> MEMPOWERDOWN blocks access to powered-off ROM and SRAM banks. > > What exactly is going to be accessing ROM and SRAM while powered off? > > Why do all three things need to be done at the same time? > >> \n\nAdd focused qtests for register values, aliases, power-down behavior, >> and UART0 IRQ routing to NMI. >> >> Signed-off-by: gilles grimaud <[email protected]> >> --- >> MAINTAINERS | 1 + >> hw/arm/Kconfig | 2 + >> hw/arm/rp2040.c | 129 +++++++++++- >> hw/misc/Kconfig | 6 + >> hw/misc/meson.build | 2 + >> hw/misc/rp2040_syscfg.c | 244 +++++++++++++++++++++++ >> hw/misc/rp2040_sysinfo.c | 103 ++++++++++ >> include/hw/arm/rp2040.h | 12 ++ >> include/hw/misc/rp2040_syscfg.h | 42 ++++ >> include/hw/misc/rp2040_sysinfo.h | 25 +++ >> tests/qtest/meson.build | 1 + >> tests/qtest/rp2040-sysinfo-syscfg-test.c | 176 ++++++++++++++++ >> 12 files changed, 740 insertions(+), 3 deletions(-) >> create mode 100644 hw/misc/rp2040_syscfg.c >> create mode 100644 hw/misc/rp2040_sysinfo.c >> create mode 100644 include/hw/misc/rp2040_syscfg.h >> create mode 100644 include/hw/misc/rp2040_sysinfo.h >> create mode 100644 tests/qtest/rp2040-sysinfo-syscfg-test.c >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index b000b3287d..fd35521947 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -1042,6 +1042,7 @@ S: Maintained >> F: hw/*/rp2040* >> F: include/hw/*/rp2040* >> F: hw/arm/raspi_pico.c >> +F: tests/qtest/rp2040* >> F: tests/tcg/arm/system/rp2040* >> >> Real View >> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig >> index d93303ab80..5d55ca06fd 100644 >> --- a/hw/arm/Kconfig >> +++ b/hw/arm/Kconfig >> @@ -379,6 +379,8 @@ config RP2040 >> select ARM_V7M >> select PL011 >> select RP2040_NYI >> + select RP2040_SYSCFG >> + select RP2040_SYSINFO > > I'm not sure we are going to need Kconfig vars for every sub-system. If > they come with the RP2040 as a piece we should only need one symbol to > select the board, peripheral and CPU models. > >> select UNIMP >> >> config STM32F100_SOC >> diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c >> index 2196bad18d..a184d527e1 100644 >> --- a/hw/arm/rp2040.c >> +++ b/hw/arm/rp2040.c >> @@ -13,6 +13,7 @@ >> #include "hw/core/loader.h" >> #include "hw/core/qdev-clock.h" >> #include "hw/core/qdev-properties.h" >> +#include "hw/core/irq.h" >> #include "hw/misc/unimp.h" >> #include "qemu/datadir.h" >> #include "target/arm/cpu-qom.h" >> @@ -49,8 +50,6 @@ static const struct { >> hwaddr base; >> hwaddr size; >> } rp2040_unimplemented[] = { >> - { "rp2040.sysinfo", 0x40000000, 0x4000 }, >> - { "rp2040.syscfg", 0x40004000, 0x4000 }, >> { "rp2040.clocks", 0x40008000, 0x4000 }, >> { "rp2040.resets", 0x4000c000, 0x4000 }, >> { "rp2040.psm", 0x40010000, 0x4000 }, >> @@ -84,6 +83,80 @@ static const struct { >> { "rp2040.sio", 0xd0000000, 0x1000 }, >> }; >> >> +static MemTxResult rp2040_powered_off_read(void *opaque, hwaddr addr, >> + uint64_t *data, unsigned size, >> + MemTxAttrs attrs) >> +{ >> + *data = 0; >> + return MEMTX_ERROR; >> +} >> + >> +static MemTxResult rp2040_powered_off_write(void *opaque, hwaddr addr, >> + uint64_t data, unsigned size, >> + MemTxAttrs attrs) >> +{ >> + return MEMTX_ERROR; >> +} >> + >> +static const MemoryRegionOps rp2040_powered_off_ops = { >> + .read_with_attrs = rp2040_powered_off_read, >> + .write_with_attrs = rp2040_powered_off_write, > > You choose to have _with_attrs access functions here but not later. Why? > >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .valid = { >> + .min_access_size = 1, >> + .max_access_size = 4, >> + }, >> +}; >> + >> +static void rp2040_update_mempowerdown(RP2040State *s) >> +{ >> + uint32_t mempowerdown; >> + int i; >> + >> + if (!s->mempowerdown_ready) { >> + return; >> + } >> + >> + mempowerdown = rp2040_syscfg_get_mempowerdown(&s->syscfg); >> + for (i = 0; i < ARRAY_SIZE(s->sram_poweroff); i++) { >> + memory_region_set_enabled(&s->sram_poweroff[i], >> + mempowerdown & BIT(i)); >> + } >> + memory_region_set_enabled(&s->rom_poweroff, mempowerdown & BIT(7)); >> +} >> + >> +static void rp2040_update_nmi(RP2040State *s) >> +{ >> + uint32_t nmi_mask = rp2040_syscfg_get_proc0_nmi_mask(&s->syscfg); >> + bool nmi_level = false; >> + int i; >> + >> + for (i = 0; i < RP2040_NUM_IRQS; i++) { >> + bool route_to_nmi = nmi_mask & BIT(i); >> + >> + qemu_set_irq(s->cpu_irq[i], s->irq_level[i] && !route_to_nmi); >> + nmi_level |= s->irq_level[i] && route_to_nmi; >> + } >> + qemu_set_irq(s->nmi_irq, nmi_level); >> +} >> + >> +static void rp2040_syscfg_update(void *opaque) >> +{ >> + RP2040State *s = opaque; >> + >> + rp2040_update_mempowerdown(s); >> + rp2040_update_nmi(s); >> +} >> + >> +static void rp2040_set_irq(void *opaque, int irq, int level) >> +{ >> + RP2040State *s = opaque; >> + >> + assert(irq >= 0 && irq < RP2040_NUM_IRQS); >> + s->irq_level[irq] = level; >> + rp2040_update_nmi(s); >> +} >> + >> static void rp2040_soc_init(Object *obj) >> { >> RP2040State *s = RP2040(obj); >> @@ -104,6 +177,10 @@ static void rp2040_soc_init(Object *obj) >> "chardev"); >> } >> >> + object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_RP2040_SYSCFG); >> + object_initialize_child(obj, "sysinfo", &s->sysinfo, >> TYPE_RP2040_SYSINFO); >> + >> + s->irq = qemu_allocate_irqs(rp2040_set_irq, s, RP2040_NUM_IRQS); >> s->sysclk = clock_new(obj, "sysclk"); >> clock_set_hz(s->sysclk, RP2040_SYSCLK_FRQ); >> } >> @@ -117,9 +194,17 @@ static bool rp2040_init_memory(RP2040State *s, Error >> **errp) >> return false; >> } >> memory_region_add_subregion(s->board_memory, RP2040_ROM_BASE, &s->rom); >> + memory_region_init_io(&s->rom_poweroff, OBJECT(s), >> + &rp2040_powered_off_ops, s, >> + "rp2040.rom.poweroff", RP2040_ROM_SIZE); >> + memory_region_add_subregion_overlap(s->board_memory, RP2040_ROM_BASE, >> + &s->rom_poweroff, 1); >> + memory_region_set_enabled(&s->rom_poweroff, false); >> >> for (i = 0; i < 4; i++) { >> g_autofree char *name = g_strdup_printf("rp2040.sram%d", i); >> + g_autofree char *poweroff_name = >> + g_strdup_printf("rp2040.sram%d.poweroff", i); >> >> if (!memory_region_init_ram(&s->sram[i], OBJECT(s), name, >> RP2040_SRAM_BANK_SIZE, errp)) { >> @@ -129,6 +214,14 @@ static bool rp2040_init_memory(RP2040State *s, Error >> **errp) >> RP2040_SRAM_BASE + >> i * RP2040_SRAM_BANK_SIZE, >> &s->sram[i]); >> + memory_region_init_io(&s->sram_poweroff[i], OBJECT(s), >> + &rp2040_powered_off_ops, s, poweroff_name, >> + RP2040_SRAM_BANK_SIZE); >> + memory_region_add_subregion_overlap(s->board_memory, >> + RP2040_SRAM_BASE + >> + i * RP2040_SRAM_BANK_SIZE, >> + &s->sram_poweroff[i], 1); >> + memory_region_set_enabled(&s->sram_poweroff[i], false); >> } >> >> if (!memory_region_init_ram(&s->sram[4], OBJECT(s), "rp2040.sram4", >> @@ -137,6 +230,12 @@ static bool rp2040_init_memory(RP2040State *s, Error >> **errp) >> } >> memory_region_add_subregion(s->board_memory, RP2040_SRAM4_BASE, >> &s->sram[4]); >> + memory_region_init_io(&s->sram_poweroff[4], OBJECT(s), >> + &rp2040_powered_off_ops, s, >> + "rp2040.sram4.poweroff", RP2040_SRAM_HI_SIZE); >> + memory_region_add_subregion_overlap(s->board_memory, RP2040_SRAM4_BASE, >> + &s->sram_poweroff[4], 1); >> + memory_region_set_enabled(&s->sram_poweroff[4], false); >> >> if (!memory_region_init_ram(&s->sram[5], OBJECT(s), "rp2040.sram5", >> RP2040_SRAM_HI_SIZE, errp)) { >> @@ -144,6 +243,14 @@ static bool rp2040_init_memory(RP2040State *s, Error >> **errp) >> } >> memory_region_add_subregion(s->board_memory, RP2040_SRAM5_BASE, >> &s->sram[5]); >> + memory_region_init_io(&s->sram_poweroff[5], OBJECT(s), >> + &rp2040_powered_off_ops, s, >> + "rp2040.sram5.poweroff", RP2040_SRAM_HI_SIZE); >> + memory_region_add_subregion_overlap(s->board_memory, RP2040_SRAM5_BASE, >> + &s->sram_poweroff[5], 1); >> + memory_region_set_enabled(&s->sram_poweroff[5], false); >> + >> + s->mempowerdown_ready = true; >> >> return true; >> } >> @@ -201,6 +308,22 @@ static void rp2040_soc_realize(DeviceState *dev, Error >> **errp) >> if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { >> return; >> } >> + for (i = 0; i < RP2040_NUM_IRQS; i++) { >> + s->cpu_irq[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i); >> + } >> + s->nmi_irq = qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0); >> + >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) { >> + return; >> + } >> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscfg), 0, RP2040_SYSCFG_BASE); >> + rp2040_syscfg_set_update_callback(&s->syscfg, rp2040_syscfg_update, s); >> + >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sysinfo), errp)) { >> + return; >> + } >> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, RP2040_SYSINFO_BASE); >> + rp2040_syscfg_update(s); >> >> for (i = 0; i < ARRAY_SIZE(s->uart); i++) { >> qdev_connect_clock_in(DEVICE(&s->uart[i]), "clk", s->sysclk); >> @@ -209,7 +332,7 @@ static void rp2040_soc_realize(DeviceState *dev, Error >> **errp) >> } >> sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_base[i]); >> sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, >> - qdev_get_gpio_in(DEVICE(&s->armv7m), >> uart_irq[i])); >> + s->irq[uart_irq[i]]); >> } >> } >> >> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig >> index de7a0f56fa..f6f344ab51 100644 >> --- a/hw/misc/Kconfig >> +++ b/hw/misc/Kconfig >> @@ -104,6 +104,12 @@ config FSL_IMX8MP_CCM >> config RP2040_NYI >> bool >> >> +config RP2040_SYSCFG >> + bool >> + >> +config RP2040_SYSINFO >> + bool >> + >> config STM32_RCC >> bool >> >> diff --git a/hw/misc/meson.build b/hw/misc/meson.build >> index 8611d59437..7be9f45c78 100644 >> --- a/hw/misc/meson.build >> +++ b/hw/misc/meson.build >> @@ -99,6 +99,8 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files( >> 'bcm2835_powermgt.c', >> )) >> system_ss.add(when: 'CONFIG_RP2040_NYI', if_true: files('rp2040_nyi.c')) >> +system_ss.add(when: 'CONFIG_RP2040_SYSCFG', if_true: >> files('rp2040_syscfg.c')) >> +system_ss.add(when: 'CONFIG_RP2040_SYSINFO', if_true: >> files('rp2040_sysinfo.c')) >> system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) >> system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) >> system_ss.add(when: 'CONFIG_XLNX_ZYNQ_DDRC', if_true: >> files('xlnx-zynq-ddrc.c')) >> diff --git a/hw/misc/rp2040_syscfg.c b/hw/misc/rp2040_syscfg.c >> new file mode 100644 >> index 0000000000..69b4164ccd >> --- /dev/null >> +++ b/hw/misc/rp2040_syscfg.c >> @@ -0,0 +1,244 @@ >> +/* >> + * RP2040 syscfg emulation >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "hw/misc/rp2040_nyi.h" >> +#include "hw/misc/rp2040_syscfg.h" >> +#include "migration/vmstate.h" >> +#include "qemu/module.h" >> + >> +#define SYSCFG_PROC0_NMI_MASK 0x00 >> +#define SYSCFG_PROC1_NMI_MASK 0x04 >> +#define SYSCFG_PROC_CONFIG 0x08 >> +#define SYSCFG_PROC_IN_SYNC_BYPASS 0x0c >> +#define SYSCFG_PROC_IN_SYNC_BYPASS_HI 0x10 >> +#define SYSCFG_DBGFORCE 0x14 >> +#define SYSCFG_MEMPOWERDOWN 0x18 >> + >> +#define PROC_CONFIG_RW_MASK 0xff000000 >> +#define PROC_CONFIG_RESET 0x10000000 >> +#define PROC_IN_SYNC_BYPASS_MASK 0x3fffffff >> +#define PROC_IN_SYNC_BYPASS_HI_MASK 0x0000003f >> +#define DBGFORCE_RW_MASK 0x000000ee >> +#define DBGFORCE_RESET 0x00000066 >> +#define MEMPOWERDOWN_MASK 0x000000ff >> + >> +#define ATOMIC_ALIAS_MASK 0x3000 >> +#define ATOMIC_XOR 0x1000 >> +#define ATOMIC_SET 0x2000 >> +#define ATOMIC_CLR 0x3000 >> + >> +static uint32_t rp2040_syscfg_apply_alias(uint32_t old, uint32_t value, >> + hwaddr alias) >> +{ >> + switch (alias) { >> + case ATOMIC_XOR: >> + return old ^ value; >> + case ATOMIC_SET: >> + return old | value; >> + case ATOMIC_CLR: >> + return old & ~value; >> + default: >> + return value; >> + } >> +} >> + >> +static void rp2040_syscfg_update(RP2040SysCfgState *s) >> +{ >> + if (s->update) { >> + s->update(s->update_opaque); >> + } >> +} >> + >> +void rp2040_syscfg_set_update_callback(RP2040SysCfgState *s, >> + RP2040SysCfgUpdateFn update, >> + void *opaque) >> +{ >> + s->update = update; >> + s->update_opaque = opaque; >> +} >> + >> +uint32_t rp2040_syscfg_get_proc0_nmi_mask(RP2040SysCfgState *s) >> +{ >> + return s->proc0_nmi_mask; >> +} >> + >> +uint32_t rp2040_syscfg_get_mempowerdown(RP2040SysCfgState *s) >> +{ >> + return s->mempowerdown; >> +} >> + >> +static uint64_t rp2040_syscfg_read(void *opaque, hwaddr addr, unsigned size) >> +{ >> + RP2040SysCfgState *s = opaque; >> + hwaddr offset = addr & 0xfff; >> + uint64_t value; >> + >> + switch (offset) { >> + case SYSCFG_PROC0_NMI_MASK: >> + value = s->proc0_nmi_mask; >> + break; >> + case SYSCFG_PROC1_NMI_MASK: >> + value = s->proc1_nmi_mask; >> + break; >> + case SYSCFG_PROC_CONFIG: >> + value = s->proc_config; >> + break; >> + case SYSCFG_PROC_IN_SYNC_BYPASS: >> + value = s->proc_in_sync_bypass; >> + break; >> + case SYSCFG_PROC_IN_SYNC_BYPASS_HI: >> + value = s->proc_in_sync_bypass_hi; >> + break; >> + case SYSCFG_DBGFORCE: >> + value = s->dbgforce; >> + break; >> + case SYSCFG_MEMPOWERDOWN: >> + value = s->mempowerdown; >> + break; >> + default: >> + value = 0; >> + rp2040_log_unimplemented_read("syscfg", size, >> + RP2040_SYSCFG_BASE + addr, offset, >> + value); >> + break; >> + } >> + >> + return value; >> +} >> + >> +static void rp2040_syscfg_write(void *opaque, hwaddr addr, >> + uint64_t value64, unsigned size) >> +{ >> + RP2040SysCfgState *s = opaque; >> + hwaddr alias = addr & ATOMIC_ALIAS_MASK; >> + hwaddr offset = addr & 0xfff; >> + uint32_t value = value64; >> + >> + switch (offset) { >> + case SYSCFG_PROC0_NMI_MASK: >> + s->proc0_nmi_mask = >> + rp2040_syscfg_apply_alias(s->proc0_nmi_mask, value, alias); >> + rp2040_syscfg_update(s); >> + break; >> + case SYSCFG_PROC1_NMI_MASK: >> + s->proc1_nmi_mask = >> + rp2040_syscfg_apply_alias(s->proc1_nmi_mask, value, alias); >> + break; >> + case SYSCFG_PROC_CONFIG: >> + s->proc_config = >> + rp2040_syscfg_apply_alias(s->proc_config, value, alias) & >> + PROC_CONFIG_RW_MASK; >> + break; >> + case SYSCFG_PROC_IN_SYNC_BYPASS: >> + s->proc_in_sync_bypass = >> + rp2040_syscfg_apply_alias(s->proc_in_sync_bypass, value, alias) >> & >> + PROC_IN_SYNC_BYPASS_MASK; >> + break; >> + case SYSCFG_PROC_IN_SYNC_BYPASS_HI: >> + s->proc_in_sync_bypass_hi = >> + rp2040_syscfg_apply_alias(s->proc_in_sync_bypass_hi, value, >> + alias) & >> + PROC_IN_SYNC_BYPASS_HI_MASK; >> + break; >> + case SYSCFG_DBGFORCE: >> + s->dbgforce = >> + rp2040_syscfg_apply_alias(s->dbgforce, value, alias) & >> + DBGFORCE_RW_MASK; >> + break; >> + case SYSCFG_MEMPOWERDOWN: >> + s->mempowerdown = >> + rp2040_syscfg_apply_alias(s->mempowerdown, value, alias) & >> + MEMPOWERDOWN_MASK; >> + rp2040_syscfg_update(s); >> + break; >> + default: >> + rp2040_log_unimplemented_write("syscfg", size, >> + RP2040_SYSCFG_BASE + addr, offset, >> + value64); >> + break; >> + } >> +} >> + >> +static const MemoryRegionOps rp2040_syscfg_ops = { >> + .read = rp2040_syscfg_read, >> + .write = rp2040_syscfg_write, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .valid = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> +}; >> + >> +static void rp2040_syscfg_reset(DeviceState *dev) >> +{ >> + RP2040SysCfgState *s = RP2040_SYSCFG(dev); >> + >> + s->proc0_nmi_mask = 0; >> + s->proc1_nmi_mask = 0; >> + s->proc_config = PROC_CONFIG_RESET; >> + s->proc_in_sync_bypass = 0; >> + s->proc_in_sync_bypass_hi = 0; >> + s->dbgforce = DBGFORCE_RESET; >> + s->mempowerdown = 0; >> + rp2040_syscfg_update(s); >> +} >> + >> +static int rp2040_syscfg_post_load(void *opaque, int version_id) >> +{ >> + RP2040SysCfgState *s = opaque; >> + >> + rp2040_syscfg_update(s); >> + return 0; >> +} >> + >> +static void rp2040_syscfg_init(Object *obj) >> +{ >> + RP2040SysCfgState *s = RP2040_SYSCFG(obj); >> + >> + memory_region_init_io(&s->iomem, obj, &rp2040_syscfg_ops, s, >> + "rp2040.syscfg", RP2040_SYSCFG_SIZE); >> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); >> +} >> + >> +static const VMStateDescription rp2040_syscfg_vmstate = { >> + .name = TYPE_RP2040_SYSCFG, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .post_load = rp2040_syscfg_post_load, >> + .fields = (const VMStateField[]) { >> + VMSTATE_UINT32(proc0_nmi_mask, RP2040SysCfgState), >> + VMSTATE_UINT32(proc1_nmi_mask, RP2040SysCfgState), >> + VMSTATE_UINT32(proc_config, RP2040SysCfgState), >> + VMSTATE_UINT32(proc_in_sync_bypass, RP2040SysCfgState), >> + VMSTATE_UINT32(proc_in_sync_bypass_hi, RP2040SysCfgState), >> + VMSTATE_UINT32(dbgforce, RP2040SysCfgState), >> + VMSTATE_UINT32(mempowerdown, RP2040SysCfgState), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void rp2040_syscfg_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + device_class_set_legacy_reset(dc, rp2040_syscfg_reset); >> + dc->vmsd = &rp2040_syscfg_vmstate; >> +} >> + >> +static const TypeInfo rp2040_syscfg_info = { >> + .name = TYPE_RP2040_SYSCFG, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RP2040SysCfgState), >> + .instance_init = rp2040_syscfg_init, >> + .class_init = rp2040_syscfg_class_init, >> +}; >> + >> +static void rp2040_syscfg_register_types(void) >> +{ >> + type_register_static(&rp2040_syscfg_info); >> +} >> +type_init(rp2040_syscfg_register_types) >> diff --git a/hw/misc/rp2040_sysinfo.c b/hw/misc/rp2040_sysinfo.c >> new file mode 100644 >> index 0000000000..15c50df6af >> --- /dev/null >> +++ b/hw/misc/rp2040_sysinfo.c >> @@ -0,0 +1,103 @@ >> +/* >> + * RP2040 sysinfo emulation >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "hw/misc/rp2040_nyi.h" >> +#include "hw/misc/rp2040_sysinfo.h" >> +#include "migration/vmstate.h" >> +#include "qemu/log.h" >> +#include "qemu/module.h" >> + >> +#define SYSINFO_CHIP_ID 0x00 >> +#define SYSINFO_PLATFORM 0x04 >> +#define SYSINFO_GITREF_RP2040 0x40 >> + >> +#define SYSINFO_PLATFORM_ASIC BIT(1) >> + >> +static uint64_t rp2040_sysinfo_read(void *opaque, hwaddr addr, unsigned >> size) >> +{ >> + hwaddr offset = addr & 0xfff; >> + uint64_t value; >> + >> + switch (offset) { >> + case SYSINFO_CHIP_ID: >> + case SYSINFO_GITREF_RP2040: >> + value = 0; >> + break; >> + case SYSINFO_PLATFORM: >> + value = SYSINFO_PLATFORM_ASIC; >> + break; >> + default: >> + value = 0; >> + rp2040_log_unimplemented_read("sysinfo", size, >> + RP2040_SYSINFO_BASE + addr, offset, >> + value); >> + break; >> + } >> + >> + return value; >> +} >> + >> +static void rp2040_sysinfo_write(void *opaque, hwaddr addr, >> + uint64_t value, unsigned size) >> +{ >> + hwaddr offset = addr & 0xfff; >> + >> + qemu_log_mask(LOG_GUEST_ERROR, "rp2040.sysinfo: write to read-only " >> + "register (size %d, addr 0x%08" HWADDR_PRIx >> + ", offset 0x%04" HWADDR_PRIx >> + ", value 0x%0*" PRIx64 ")\n", >> + size, RP2040_SYSINFO_BASE + addr, offset, size << 1, >> value); >> +} >> + >> +static const MemoryRegionOps rp2040_sysinfo_ops = { >> + .read = rp2040_sysinfo_read, >> + .write = rp2040_sysinfo_write, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .valid = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> +}; >> + >> +static void rp2040_sysinfo_init(Object *obj) >> +{ >> + RP2040SysInfoState *s = RP2040_SYSINFO(obj); >> + >> + memory_region_init_io(&s->iomem, obj, &rp2040_sysinfo_ops, s, >> + "rp2040.sysinfo", RP2040_SYSINFO_SIZE); >> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); >> +} >> + >> +static const VMStateDescription rp2040_sysinfo_vmstate = { >> + .name = TYPE_RP2040_SYSINFO, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .fields = (const VMStateField[]) { >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void rp2040_sysinfo_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->vmsd = &rp2040_sysinfo_vmstate; >> +} >> + >> +static const TypeInfo rp2040_sysinfo_info = { >> + .name = TYPE_RP2040_SYSINFO, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RP2040SysInfoState), >> + .instance_init = rp2040_sysinfo_init, >> + .class_init = rp2040_sysinfo_class_init, >> +}; >> + >> +static void rp2040_sysinfo_register_types(void) >> +{ >> + type_register_static(&rp2040_sysinfo_info); >> +} >> +type_init(rp2040_sysinfo_register_types) >> diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h >> index 4e5cd06add..caa1f9127f 100644 >> --- a/include/hw/arm/rp2040.h >> +++ b/include/hw/arm/rp2040.h >> @@ -13,6 +13,8 @@ >> #include "hw/char/pl011.h" >> #include "hw/core/clock.h" >> #include "hw/core/sysbus.h" >> +#include "hw/misc/rp2040_syscfg.h" >> +#include "hw/misc/rp2040_sysinfo.h" >> #include "qom/object.h" >> >> #define TYPE_RP2040 "rp2040" >> @@ -34,12 +36,22 @@ struct RP2040State { >> >> ARMv7MState armv7m; >> PL011State uart[2]; >> + RP2040SysCfgState syscfg; >> + RP2040SysInfoState sysinfo; >> >> MemoryRegion *board_memory; >> MemoryRegion rom; >> + MemoryRegion rom_poweroff; >> MemoryRegion sram[6]; >> + MemoryRegion sram_poweroff[6]; >> char *bootrom_file; >> >> + qemu_irq *irq; >> + qemu_irq cpu_irq[RP2040_NUM_IRQS]; >> + qemu_irq nmi_irq; >> + bool irq_level[RP2040_NUM_IRQS]; >> + bool mempowerdown_ready; >> + >> Clock *sysclk; >> }; >> >> diff --git a/include/hw/misc/rp2040_syscfg.h >> b/include/hw/misc/rp2040_syscfg.h >> new file mode 100644 >> index 0000000000..c31799ae7a >> --- /dev/null >> +++ b/include/hw/misc/rp2040_syscfg.h >> @@ -0,0 +1,42 @@ >> +/* >> + * RP2040 syscfg emulation >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#ifndef HW_MISC_RP2040_SYSCFG_H >> +#define HW_MISC_RP2040_SYSCFG_H >> + >> +#include "hw/core/sysbus.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RP2040_SYSCFG "rp2040-syscfg" >> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040SysCfgState, RP2040_SYSCFG) >> + >> +#define RP2040_SYSCFG_BASE 0x40004000 >> +#define RP2040_SYSCFG_SIZE 0x4000 >> + >> +typedef void (*RP2040SysCfgUpdateFn)(void *opaque); >> + >> +struct RP2040SysCfgState { >> + SysBusDevice parent_obj; >> + >> + MemoryRegion iomem; >> + RP2040SysCfgUpdateFn update; >> + void *update_opaque; >> + uint32_t proc0_nmi_mask; >> + uint32_t proc1_nmi_mask; >> + uint32_t proc_config; >> + uint32_t proc_in_sync_bypass; >> + uint32_t proc_in_sync_bypass_hi; >> + uint32_t dbgforce; >> + uint32_t mempowerdown; >> +}; >> + >> +void rp2040_syscfg_set_update_callback(RP2040SysCfgState *s, >> + RP2040SysCfgUpdateFn update, >> + void *opaque); >> +uint32_t rp2040_syscfg_get_proc0_nmi_mask(RP2040SysCfgState *s); >> +uint32_t rp2040_syscfg_get_mempowerdown(RP2040SysCfgState *s); >> + >> +#endif >> diff --git a/include/hw/misc/rp2040_sysinfo.h >> b/include/hw/misc/rp2040_sysinfo.h >> new file mode 100644 >> index 0000000000..d521659488 >> --- /dev/null >> +++ b/include/hw/misc/rp2040_sysinfo.h >> @@ -0,0 +1,25 @@ >> +/* >> + * RP2040 sysinfo emulation >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#ifndef HW_MISC_RP2040_SYSINFO_H >> +#define HW_MISC_RP2040_SYSINFO_H >> + >> +#include "hw/core/sysbus.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RP2040_SYSINFO "rp2040-sysinfo" >> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040SysInfoState, RP2040_SYSINFO) >> + >> +#define RP2040_SYSINFO_BASE 0x40000000 >> +#define RP2040_SYSINFO_SIZE 0x4000 >> + >> +struct RP2040SysInfoState { >> + SysBusDevice parent_obj; >> + >> + MemoryRegion iomem; >> +}; >> + >> +#endif >> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build >> index 5555ce4eae..692d51a78a 100644 >> --- a/tests/qtest/meson.build >> +++ b/tests/qtest/meson.build >> @@ -255,6 +255,7 @@ qtests_arm = \ >> (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test', >> 'tpm-tis-i2c-swtpm-test'] : []) + \ >> (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : >> []) + \ >> (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + >> \ >> + (config_all_devices.has_key('CONFIG_RASPI_PICO') ? >> ['rp2040-sysinfo-syscfg-test'] : []) + \ >> (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : >> []) + \ >> (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? >> ['aspeed_fsi-test'] : []) + \ >> (config_all_devices.has_key('CONFIG_CAN_FLEXCAN') ? ['flexcan-test'] : []) >> + \ >> diff --git a/tests/qtest/rp2040-sysinfo-syscfg-test.c >> b/tests/qtest/rp2040-sysinfo-syscfg-test.c >> new file mode 100644 >> index 0000000000..18da6561bd >> --- /dev/null >> +++ b/tests/qtest/rp2040-sysinfo-syscfg-test.c >> @@ -0,0 +1,176 @@ >> +/* >> + * QTest testcase for the RP2040 sysinfo and syscfg blocks. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "libqtest.h" >> +#include "qemu/bitops.h" >> + >> +#define SYSINFO_BASE 0x40000000 >> +#define SYSINFO_CHIP_ID 0x00 >> +#define SYSINFO_PLATFORM 0x04 >> +#define SYSINFO_GITREF_RP2040 0x40 >> +#define SYSINFO_PLATFORM_ASIC BIT(1) >> + >> +#define SYSCFG_BASE 0x40004000 >> +#define SYSCFG_PROC0_NMI_MASK 0x00 >> +#define SYSCFG_PROC1_NMI_MASK 0x04 >> +#define SYSCFG_PROC_CONFIG 0x08 >> +#define SYSCFG_PROC_IN_SYNC_BYPASS 0x0c >> +#define SYSCFG_PROC_IN_SYNC_BYPASS_HI 0x10 >> +#define SYSCFG_DBGFORCE 0x14 >> +#define SYSCFG_MEMPOWERDOWN 0x18 >> + >> +#define SRAM0_BASE 0x20000000 >> +#define UART0_BASE 0x40034000 >> +#define UARTDR 0x00 >> +#define UARTCR 0x30 >> +#define UARTIMSC 0x38 >> +#define UARTICR 0x44 >> +#define UART_INT_TX BIT(5) >> +#define UART0_IRQ 20 >> +#define NVIC_ISPR 0xe000e200 >> +#define NVIC_ICPR 0xe000e280 >> +#define NVIC_ICSR 0xe000ed04 >> +#define NVIC_ICSR_NMIPENDSET BIT(31) >> + >> +#define PROC_CONFIG_RESET 0x10000000 >> +#define DBGFORCE_RESET 0x00000066 >> + >> +#define ATOMIC_SET_ALIAS 0x2000 >> +#define ATOMIC_CLR_ALIAS 0x3000 >> + >> +static QTestState *rp2040_start(void) >> +{ >> + return qtest_init("-machine raspi-pico"); >> +} >> + >> +static void test_sysinfo_read_values(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_CHIP_ID), ==, >> 0); >> + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_PLATFORM), ==, >> + SYSINFO_PLATFORM_ASIC); >> + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_GITREF_RP2040), >> + ==, 0); >> + >> + qtest_quit(qts); >> +} >> + >> +static void test_syscfg_reset_values(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), >> + ==, 0); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK), >> + ==, 0); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC_CONFIG), ==, >> + PROC_CONFIG_RESET); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_DBGFORCE), ==, >> + DBGFORCE_RESET); >> + >> + qtest_quit(qts); >> +} >> + >> +static void test_syscfg_rw_masks(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, 0xa5a5a5a5); >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK, 0x5a5a5a5a); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), >> + ==, 0xa5a5a5a5); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK), >> + ==, 0x5a5a5a5a); >> + >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS, 0xffffffff); >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS_HI, >> + 0xffffffff); >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, 0xffffffff); >> + g_assert_cmphex(qtest_readl(qts, >> + SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS), >> + ==, 0x3fffffff); >> + g_assert_cmphex(qtest_readl(qts, >> + SYSCFG_BASE + >> SYSCFG_PROC_IN_SYNC_BYPASS_HI), >> + ==, 0x0000003f); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN), ==, >> + 0x000000ff); >> + >> + qtest_quit(qts); >> +} >> + >> +static void test_syscfg_atomic_aliases(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, 0); >> + qtest_writel(qts, SYSCFG_BASE + ATOMIC_SET_ALIAS + >> SYSCFG_PROC0_NMI_MASK, >> + 0x0000000f); >> + qtest_writel(qts, SYSCFG_BASE + ATOMIC_CLR_ALIAS + >> SYSCFG_PROC0_NMI_MASK, >> + 0x00000003); >> + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), >> + ==, 0x0000000c); >> + >> + qtest_quit(qts); >> +} >> + >> +static void test_syscfg_mempowerdown_blocks_sram(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + qtest_writel(qts, SRAM0_BASE, 0x11223344); >> + g_assert_cmphex(qtest_readl(qts, SRAM0_BASE), ==, 0x11223344); >> + >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, BIT(0)); >> + qtest_writel(qts, SRAM0_BASE, 0xaabbccdd); >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, 0); >> + >> + g_assert_cmphex(qtest_readl(qts, SRAM0_BASE), ==, 0x11223344); >> + >> + qtest_quit(qts); >> +} >> + >> +static void test_syscfg_nmi_mask_routes_uart0_irq(void) >> +{ >> + QTestState *qts = rp2040_start(); >> + >> + qtest_writel(qts, UART0_BASE + UARTCR, BIT(0) | BIT(8)); >> + qtest_writel(qts, UART0_BASE + UARTIMSC, UART_INT_TX); >> + >> + qtest_writel(qts, UART0_BASE + UARTICR, UART_INT_TX); >> + qtest_writel(qts, NVIC_ICPR, BIT(UART0_IRQ)); >> + qtest_writel(qts, UART0_BASE + UARTDR, 'a'); >> + g_assert_cmphex(qtest_readl(qts, NVIC_ISPR) & BIT(UART0_IRQ), ==, >> + BIT(UART0_IRQ)); >> + >> + qtest_writel(qts, UART0_BASE + UARTICR, UART_INT_TX); >> + qtest_writel(qts, NVIC_ICPR, BIT(UART0_IRQ)); >> + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, BIT(UART0_IRQ)); >> + qtest_writel(qts, UART0_BASE + UARTDR, 'b'); >> + g_assert_cmphex(qtest_readl(qts, NVIC_ISPR) & BIT(UART0_IRQ), ==, 0); >> + g_assert_cmphex(qtest_readl(qts, NVIC_ICSR) & NVIC_ICSR_NMIPENDSET, ==, >> + NVIC_ICSR_NMIPENDSET); >> + >> + qtest_quit(qts); >> +} >> + >> +int main(int argc, char **argv) >> +{ >> + g_test_init(&argc, &argv, NULL); >> + >> + qtest_add_func("/rp2040-sysinfo/read-values", test_sysinfo_read_values); >> + qtest_add_func("/rp2040-syscfg/reset-values", test_syscfg_reset_values); >> + qtest_add_func("/rp2040-syscfg/rw-masks", test_syscfg_rw_masks); >> + qtest_add_func("/rp2040-syscfg/atomic-aliases", >> + test_syscfg_atomic_aliases); >> + qtest_add_func("/rp2040-syscfg/mempowerdown-blocks-sram", >> + test_syscfg_mempowerdown_blocks_sram); >> + qtest_add_func("/rp2040-syscfg/nmi-mask-routes-uart0-irq", >> + test_syscfg_nmi_mask_routes_uart0_irq); >> + >> + return g_test_run(); >> +} > > -- > Alex Bennée > Virtualisation Tech Lead @ Linaro
