> Le 2 sept. 2026 à 12:30, Alex Bennée <[email protected]> a écrit :
> 
> Gilles Grimaud <[email protected]> writes:
> 
>> From: gilles grimaud <[email protected]>
>> 
>> Model the RP2040 system and USB PLL register blocks and their clock outputs. 
>> Add the clock mux, divider, selected-source and frequency-counter registers, 
>> then route clk-sys to the Cortex-M0+ and clk-peri to both UARTs. Include 
>> focused qtests for PLL configuration, clock switching and frequency 
>> measurement.
>> 
>> Signed-off-by: gilles grimaud <[email protected]>
>> ---
>> hw/arm/Kconfig                   |   2 +
> <snip>
>> +
>> +#define ATOMIC_ALIAS_MASK    0x3000
>> +#define ATOMIC_XOR           0x1000
>> +#define ATOMIC_SET           0x2000
>> +#define ATOMIC_CLR           0x3000
> 
> These seem to be common defines - why not have a common 2040 header that
> they all use.

Agreed. I will add a common RP2040 header for the APB atomic aliases and
use its inline helper throughout the peripheral models in v3.

> 
> <snip>
>> diff --git a/hw/misc/rp2040_pll.c b/hw/misc/rp2040_pll.c
>> new file mode 100644
>> index 0000000000..6fe2be6608
>> --- /dev/null
>> +++ b/hw/misc/rp2040_pll.c
>> @@ -0,0 +1,234 @@
> <snip>
>> +
>> +#define ATOMIC_ALIAS_MASK   0x3000
>> +#define ATOMIC_XOR          0x1000
>> +#define ATOMIC_SET          0x2000
>> +#define ATOMIC_CLR          0x3000
>> +
>> +static uint32_t rp2040_pll_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;
>> +    }
> 
> This is repeated throughout the code, sometimes inline sometimes not.
> This screams for a common inline helper in the headers.

Yes, I will use a common inline helper throughout the RP2040 peripheral
models in v3.

> 
>> +}
>> +
>> +static bool rp2040_pll_locked(RP2040PllState *s)
>> +{
>> +    return !(s->pwr & (PLL_PWR_PD | PLL_PWR_VCOPD));
>> +}
>> +
>> +static unsigned rp2040_pll_output_hz(RP2040PllState *s)
>> +{
>> +    uint32_t refdiv = s->cs & PLL_CS_REFDIV_MASK;
>> +    uint32_t fbdiv = s->fbdiv_int & PLL_FBDIV_MASK;
>> +    uint32_t postdiv1 = extract32(s->prim, 16, 3);
>> +    uint32_t postdiv2 = extract32(s->prim, 12, 3);
>> +    uint64_t hz;
>> +
>> +    if (!rp2040_pll_locked(s) || (s->pwr & PLL_PWR_POSTDIVPD)) {
>> +        return 0;
>> +    }
>> +
>> +    refdiv = refdiv ? refdiv : 1;
>> +    if (s->cs & PLL_CS_BYPASS) {
>> +        return XOSC_HZ / refdiv;
>> +    }
>> +
>> +    if (!fbdiv || !postdiv1 || !postdiv2) {
>> +        return s->fallback_hz;
>> +    }
>> +
>> +    hz = XOSC_HZ;
>> +    hz = hz * fbdiv / refdiv / postdiv1 / postdiv2;
>> +    return hz;
>> +}
>> +
>> +static void rp2040_pll_update_clock(RP2040PllState *s)
>> +{
>> +    clock_update_hz(s->clk, rp2040_pll_output_hz(s));
>> +}
>> +
>> +static uint64_t rp2040_pll_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RP2040PllState *s = opaque;
>> +    hwaddr offset = addr & 0xfff;
>> +    const char *name = s->trace_name ? s->trace_name : "rp2040.pll";
>> +    uint64_t value;
>> +
>> +    switch (offset) {
>> +    case PLL_CS:
>> +        value = s->cs & ~PLL_CS_LOCK;
>> +        if (rp2040_pll_locked(s)) {
>> +            value |= PLL_CS_LOCK;
>> +        }
>> +        break;
>> +    case PLL_PWR:
>> +        value = s->pwr;
>> +        break;
>> +    case PLL_FBDIV_INT:
>> +        value = s->fbdiv_int;
>> +        break;
>> +    case PLL_PRIM:
>> +        value = s->prim;
>> +        break;
>> +    default:
>> +        value = 0;
>> +        rp2040_log_unimplemented_read(name, size, s->base + addr, offset,
>> +                                      value);
>> +        break;
>> +    }
>> +
>> +    return value;
>> +}
>> +
>> +static void rp2040_pll_write(void *opaque, hwaddr addr,
>> +                             uint64_t value64, unsigned size)
>> +{
>> +    RP2040PllState *s = opaque;
>> +    hwaddr alias = addr & ATOMIC_ALIAS_MASK;
>> +    hwaddr offset = addr & 0xfff;
>> +    const char *name = s->trace_name ? s->trace_name : "rp2040.pll";
>> +    uint32_t value = value64;
>> +
>> +    switch (offset) {
>> +    case PLL_CS:
>> +        s->cs = rp2040_pll_apply_alias(s->cs, value, alias) &
>> +                (PLL_CS_BYPASS | PLL_CS_REFDIV_MASK);
>> +        break;
>> +    case PLL_PWR:
>> +        s->pwr = rp2040_pll_apply_alias(s->pwr, value, alias) & 
>> PLL_PWR_MASK;
>> +        break;
>> +    case PLL_FBDIV_INT:
>> +        s->fbdiv_int = rp2040_pll_apply_alias(s->fbdiv_int, value, alias) &
>> +                       PLL_FBDIV_MASK;
>> +        break;
>> +    case PLL_PRIM:
>> +        s->prim = rp2040_pll_apply_alias(s->prim, value, alias) &
>> +                  PLL_PRIM_MASK;
>> +        break;
>> +    default:
>> +        rp2040_log_unimplemented_write(name, size, s->base + addr, offset,
>> +                                       value64);
>> +        break;
>> +    }
>> +
>> +    rp2040_pll_update_clock(s);
>> +}
>> +
>> +static const MemoryRegionOps rp2040_pll_ops = {
>> +    .read = rp2040_pll_read,
>> +    .write = rp2040_pll_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    },
>> +};
>> +
>> +static void rp2040_pll_reset(DeviceState *dev)
>> +{
>> +    RP2040PllState *s = RP2040_PLL(dev);
>> +
>> +    s->cs = 1;
>> +    s->pwr = PLL_PWR_MASK;
>> +    s->fbdiv_int = 0;
>> +    s->prim = (7 << 16) | (7 << 12);
> 
> #defines for magic numbers please.

Agreed. I will replace these with named PLL post-divider fields and a
named reset value.

> 
>> +
>> +    rp2040_pll_update_clock(s);
>> +}
>> +
>> +static void rp2040_pll_init(Object *obj)
>> +{
>> +    RP2040PllState *s = RP2040_PLL(obj);
>> +    DeviceState *dev = DEVICE(obj);
>> +
>> +    s->clk = qdev_init_clock_out(dev, "clk");
>> +    memory_region_init_io(&s->iomem, obj, &rp2040_pll_ops, s,
>> +                          "rp2040.pll", RP2040_PLL_SIZE);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
>> +}
>> +
>> +static const VMStateDescription rp2040_pll_vmstate = {
>> +    .name = TYPE_RP2040_PLL,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_UINT32(cs, RP2040PllState),
>> +        VMSTATE_UINT32(pwr, RP2040PllState),
>> +        VMSTATE_UINT32(fbdiv_int, RP2040PllState),
>> +        VMSTATE_UINT32(prim, RP2040PllState),
>> +        VMSTATE_CLOCK(clk, RP2040PllState),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static const Property rp2040_pll_properties[] = {
>> +    DEFINE_PROP_STRING("trace-name", RP2040PllState, trace_name),
>> +    DEFINE_PROP_UINT32("base", RP2040PllState, base, 0),
>> +    DEFINE_PROP_UINT32("fallback-hz", RP2040PllState, fallback_hz, 0),
>> +};
>> +
>> +static void rp2040_pll_class_init(ObjectClass *klass, const void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    device_class_set_legacy_reset(dc, rp2040_pll_reset);
>> +    device_class_set_props(dc, rp2040_pll_properties);
>> +    dc->vmsd = &rp2040_pll_vmstate;
>> +}
>> +
>> +static const TypeInfo rp2040_pll_info = {
>> +    .name          = TYPE_RP2040_PLL,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(RP2040PllState),
>> +    .instance_init = rp2040_pll_init,
>> +    .class_init    = rp2040_pll_class_init,
>> +};
>> +
>> +static void rp2040_pll_register_types(void)
>> +{
>> +    type_register_static(&rp2040_pll_info);
>> +}
>> +type_init(rp2040_pll_register_types)
>> diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h
>> index 47a8cb2236..abdd2f006c 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_clocks.h"
>> +#include "hw/misc/rp2040_pll.h"
>> #include "hw/misc/rp2040_rosc.h"
>> #include "hw/misc/rp2040_syscfg.h"
>> #include "hw/misc/rp2040_sysinfo.h"
>> @@ -40,6 +42,9 @@ struct RP2040State {
>> 
>>     ARMv7MState armv7m;
>>     PL011State uart[2];
>> +    RP2040ClocksState clocks;
>> +    RP2040PllState pll_sys;
>> +    RP2040PllState pll_usb;
>>     RP2040SysCfgState syscfg;
>>     RP2040SysInfoState sysinfo;
>>     RP2040RoscState rosc;
>> diff --git a/include/hw/misc/rp2040_clocks.h 
>> b/include/hw/misc/rp2040_clocks.h
>> new file mode 100644
>> index 0000000000..ebbb14e3fa
>> --- /dev/null
>> +++ b/include/hw/misc/rp2040_clocks.h
>> @@ -0,0 +1,36 @@
>> +/*
>> + * RP2040 clocks emulation
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef HW_MISC_RP2040_CLOCKS_H
>> +#define HW_MISC_RP2040_CLOCKS_H
>> +
>> +#include "hw/core/clock.h"
>> +#include "hw/core/sysbus.h"
>> +#include "qom/object.h"
>> +
>> +#define TYPE_RP2040_CLOCKS "rp2040-clocks"
>> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040ClocksState, RP2040_CLOCKS)
>> +
>> +#define RP2040_CLOCKS_BASE 0x40008000
>> +#define RP2040_CLOCKS_SIZE 0x4000
>> +
>> +struct RP2040ClocksState {
>> +    SysBusDevice parent_obj;
>> +
>> +    MemoryRegion iomem;
>> +    Clock *clk_ref;
>> +    Clock *clk_sys;
>> +    Clock *clk_peri;
>> +    Clock *clk_usb;
>> +    Clock *clk_adc;
>> +    Clock *clk_rtc;
>> +    Clock *pll_sys;
>> +    Clock *pll_usb;
>> +
>> +    uint32_t regs[0x100 / 4];
>> +};
>> +
>> +#endif
>> diff --git a/include/hw/misc/rp2040_pll.h b/include/hw/misc/rp2040_pll.h
>> new file mode 100644
>> index 0000000000..ad188aa46e
>> --- /dev/null
>> +++ b/include/hw/misc/rp2040_pll.h
>> @@ -0,0 +1,37 @@
>> +/*
>> + * RP2040 PLL emulation
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef HW_MISC_RP2040_PLL_H
>> +#define HW_MISC_RP2040_PLL_H
>> +
>> +#include "hw/core/clock.h"
>> +#include "hw/core/sysbus.h"
>> +#include "qom/object.h"
>> +
>> +#define TYPE_RP2040_PLL "rp2040-pll"
>> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040PllState, RP2040_PLL)
>> +
>> +#define RP2040_PLL_SYS_BASE 0x40028000
>> +#define RP2040_PLL_USB_BASE 0x4002c000
>> +#define RP2040_PLL_SIZE     0x4000
>> +
>> +struct RP2040PllState {
>> +    SysBusDevice parent_obj;
>> +
>> +    MemoryRegion iomem;
>> +    Clock *clk;
>> +
>> +    char *trace_name;
>> +    uint32_t base;
>> +    uint32_t fallback_hz;
>> +
>> +    uint32_t cs;
>> +    uint32_t pwr;
>> +    uint32_t fbdiv_int;
>> +    uint32_t prim;
>> +};
>> +
>> +#endif
>> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>> index 441f6f294a..416d90ae94 100644
>> --- a/tests/qtest/meson.build
>> +++ b/tests/qtest/meson.build
>> @@ -256,6 +256,7 @@ qtests_arm = \
>>   (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',
>> +                                                       'rp2040-clocks-test',
>>                                                        'rp2040-rosc-test',
>>                                                        'rp2040-tbman-test',
>>                                                        'rp2040-vreg-test'] : 
>> []) + \
>> diff --git a/tests/qtest/rp2040-clocks-test.c 
>> b/tests/qtest/rp2040-clocks-test.c
>> new file mode 100644
>> index 0000000000..39e49eb57f
>> --- /dev/null
>> +++ b/tests/qtest/rp2040-clocks-test.c
>> @@ -0,0 +1,107 @@
>> +/*
>> + * QTest testcase for the RP2040 clocks block.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "libqtest.h"
>> +#include "qemu/bitops.h"
>> +
>> +#define CLOCKS_BASE             0x40008000
>> +#define CLK_SYS_CTRL            0x3c
>> +#define CLK_SYS_DIV             0x40
>> +#define CLK_PERI_CTRL           0x48
>> +#define CLK_PERI_DIV            0x4c
>> +#define CLK_USB_CTRL            0x54
>> +#define CLK_USB_DIV             0x58
>> +#define CLK_ADC_CTRL            0x60
>> +#define CLK_ADC_DIV             0x64
>> +#define CLK_RTC_CTRL            0x6c
>> +#define CLK_RTC_DIV             0x70
>> +#define FC0_SRC                 0x94
>> +#define FC0_STATUS              0x98
>> +#define FC0_RESULT              0x9c
>> +
>> +#define PLL_SYS_BASE            0x40028000
>> +#define PLL_USB_BASE            0x4002c000
>> +#define PLL_CS                  0x00
>> +#define PLL_PWR                 0x04
>> +#define PLL_FBDIV_INT           0x08
>> +#define PLL_PRIM                0x0c
>> +
>> +#define PLL_PWR_BITS            (BIT(5) | BIT(3) | BIT(2) | BIT(0))
>> +#define CLK_CTRL_ENABLE         BIT(11)
>> +#define FC0_STATUS_DONE         BIT(4)
> 
> Again you can re-use include definitions.

Yes. 

> 
> -- 
> Alex Bennée
> Virtualisation Tech Lead @ Linaro


Reply via email to