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

Reply via email to