Gilles Grimaud <[email protected]> writes:

> From: gilles grimaud <[email protected]>
>
> Continue the Raspberry Pi Pico machine model introduced by Alex Bennée's 2022 
> RFC on current QEMU APIs. Add the RP2040 QOM skeleton, internal ROM and SRAM 
> map, the board XIP window, two PL011-backed UARTs, and explicit 
> unimplemented-device placeholders for the remaining MMIO blocks.\n\nKeep the 
> model single-core for now. Core 1 startup and the SIO launch protocol are 
> introduced later with the multicore support.
>
> Signed-off-by: gilles grimaud <[email protected]>
> ---
>  MAINTAINERS                             |   1 +
>  configs/devices/arm-softmmu/default.mak |   1 +
>  hw/arm/Kconfig                          |  13 ++
>  hw/arm/meson.build                      |   2 +
>  hw/arm/raspi_pico.c                     |  85 +++++++++
>  hw/arm/rp2040.c                         | 242 ++++++++++++++++++++++++
>  include/hw/arm/rp2040.h                 |  46 +++++
>  7 files changed, 390 insertions(+)
>  create mode 100644 hw/arm/raspi_pico.c
>  create mode 100644 hw/arm/rp2040.c
>  create mode 100644 include/hw/arm/rp2040.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index db641d9c4c..8fd7385d83 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1041,6 +1041,7 @@ L: [email protected]
>  S: Maintained
>  F: hw/*/rp2040*
>  F: include/hw/*/rp2040*
> +F: hw/arm/raspi_pico.c
>  
>  Real View
>  M: Peter Maydell <[email protected]>
> diff --git a/configs/devices/arm-softmmu/default.mak 
> b/configs/devices/arm-softmmu/default.mak
> index 71cf164511..e7f815dd0d 100644
> --- a/configs/devices/arm-softmmu/default.mak
> +++ b/configs/devices/arm-softmmu/default.mak
> @@ -33,6 +33,7 @@
>  # CONFIG_OLIMEX_STM32_H405=n
>  # CONFIG_MPS2=n
>  # CONFIG_RASPI=n
> +CONFIG_RASPI_PICO=y
>  # CONFIG_DIGIC=n
>  # CONFIG_SABRELITE=n
>  # CONFIG_EMCRAFT_SF2=n
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 260d2f0751..d93303ab80 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -368,6 +368,19 @@ config RASPI
>      select BCM2835_SPI
>      select BCM2835_I2C
>  
> +config RASPI_PICO
> +    bool
> +    default y
> +    depends on TCG && ARM
> +    select RP2040
> +
> +config RP2040
> +    bool
> +    select ARM_V7M
> +    select PL011
> +    select RP2040_NYI
> +    select UNIMP
> +
>  config STM32F100_SOC
>      bool
>      select ARM_V7M
> diff --git a/hw/arm/meson.build b/hw/arm/meson.build
> index 8ee5307a91..3240ef6637 100644
> --- a/hw/arm/meson.build
> +++ b/hw/arm/meson.build
> @@ -32,6 +32,8 @@ arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files(
>    'raspi.c',
>    'raspi4b.c'
>  ))
> +arm_common_ss.add(when: 'CONFIG_RP2040', if_true: files('rp2040.c'))
> +arm_common_ss.add(when: 'CONFIG_RASPI_PICO', if_true: files('raspi_pico.c'))
>  arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: 
> files('stm32f100_soc.c'))
>  arm_common_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: 
> files('stm32f205_soc.c'))
>  arm_common_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: 
> files('stm32f405_soc.c'))
> diff --git a/hw/arm/raspi_pico.c b/hw/arm/raspi_pico.c
> new file mode 100644
> index 0000000000..2bb7d4e511
> --- /dev/null
> +++ b/hw/arm/raspi_pico.c
> @@ -0,0 +1,85 @@
> +/*
> + * Raspberry Pi Pico machine
> + *
> + * Copyright (c) 2021 Linaro Ltd
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "hw/arm/boot.h"
> +#include "hw/arm/machines-qom.h"
> +#include "hw/arm/rp2040.h"
> +#include "hw/core/boards.h"
> +#include "hw/core/qdev-properties.h"
> +#include "system/address-spaces.h"
> +#include "system/system.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RASPI_PICO_MACHINE MACHINE_TYPE_NAME("raspi-pico")
> +OBJECT_DECLARE_SIMPLE_TYPE(RaspiPicoMachineState, RASPI_PICO_MACHINE)
> +
> +#define PICO_FLASH_SIZE (2 * MiB)
> +
> +struct RaspiPicoMachineState {
> +    MachineState parent_obj;
> +
> +    RP2040State soc;
> +    MemoryRegion flash;
> +};
> +
> +static void raspi_pico_init(MachineState *machine)
> +{
> +    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(machine);
> +    MemoryRegion *system_memory = get_system_memory();
> +
> +    object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RP2040);
> +    qdev_prop_set_chr(DEVICE(&s->soc), "serial0", serial_hd(0));
> +    qdev_prop_set_chr(DEVICE(&s->soc), "serial1", serial_hd(1));
> +    if (machine->firmware) {
> +        qdev_prop_set_string(DEVICE(&s->soc), "bootrom-file",
> +                             machine->firmware);
> +    }
> +    object_property_set_link(OBJECT(&s->soc), "memory",
> +                             OBJECT(system_memory), &error_fatal);
> +
> +    memory_region_init_rom(&s->flash, NULL, "raspi-pico.flash",
> +                           PICO_FLASH_SIZE, &error_fatal);
> +    memory_region_add_subregion(system_memory, RP2040_XIP_BASE, &s->flash);
> +
> +    sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
> +
> +    armv7m_load_kernel(s->soc.armv7m.cpu, machine->kernel_filename,
> +                       RP2040_XIP_BASE, PICO_FLASH_SIZE);
> +}
> +
> +static void raspi_pico_machine_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "Raspberry Pi Pico (Cortex-M0+)";
> +    mc->init = raspi_pico_init;
> +    mc->default_cpus = 1;
> +    mc->min_cpus = 1;
> +    mc->max_cpus = 1;

picos are duel core aren't they?

> +    mc->default_ram_size = 0;
> +    mc->no_parallel = 1;
> +    mc->no_floppy = 1;
> +    mc->no_cdrom = 1;
> +}
> +
> +static const TypeInfo raspi_pico_machine_info = {
> +    .name = TYPE_RASPI_PICO_MACHINE,
> +    .parent = TYPE_MACHINE,
> +    .instance_size = sizeof(RaspiPicoMachineState),
> +    .class_init = raspi_pico_machine_class_init,
> +    .interfaces = arm_machine_interfaces,
> +};
> +
> +static void raspi_pico_machine_init(void)
> +{
> +    type_register_static(&raspi_pico_machine_info);
> +}
> +type_init(raspi_pico_machine_init)
> diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c
> new file mode 100644
> index 0000000000..2196bad18d
> --- /dev/null
> +++ b/hw/arm/rp2040.c
> @@ -0,0 +1,242 @@
> +/*
> + * RP2040 SoC emulation
> + *
> + * Copyright (c) 2021 Linaro Ltd
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "hw/arm/rp2040.h"
> +#include "hw/core/loader.h"
> +#include "hw/core/qdev-clock.h"
> +#include "hw/core/qdev-properties.h"
> +#include "hw/misc/unimp.h"
> +#include "qemu/datadir.h"
> +#include "target/arm/cpu-qom.h"
> +
> +#define RP2040_SYSCLK_FRQ 125000000
> +
> +#define RP2040_UART0_BASE 0x40034000
> +#define RP2040_UART0_IRQ  20
> +#define RP2040_UART1_BASE 0x40038000
> +#define RP2040_UART1_IRQ  21
> +
> +/*
> + * Temporary boot ROM used until the synthetic ROM is introduced. It loads
> + * the reset handler from a vector table at the base of the XIP window.
> + */
> +static const uint8_t rp2040_bootrom[] = {
> +    0x00, 0x20, 0x04, 0x20, /* initial SP: 0x20042000 */
> +    0x09, 0x00, 0x00, 0x00, /* reset handler: 0x00000009 */
> +    0x03, 0x48,             /* ldr r0, vtor */
> +    0x04, 0x49,             /* ldr r1, xip_base */
> +    0x01, 0x60,             /* str r1, [r0] */
> +    0x04, 0x48,             /* ldr r0, xip_reset_vector */
> +    0x01, 0x68,             /* ldr r1, [r0] */
> +    0x08, 0x47,             /* bx r1 */
> +    0xfe, 0xe7,             /* b . */
> +    0xc0, 0x46,             /* nop */
> +    0x08, 0xed, 0x00, 0xe0, /* VTOR: 0xe000ed08 */
> +    0x00, 0x00, 0x00, 0x10, /* XIP base: 0x10000000 */
> +    0x04, 0x00, 0x00, 0x10, /* XIP reset vector: 0x10000004 */
> +};
> +
> +static const struct {
> +    const char *name;
> +    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 },
> +    { "rp2040.iobank0",  0x40014000, 0x4000 },
> +    { "rp2040.ioqspi",   0x40018000, 0x4000 },
> +    { "rp2040.padsbank0", 0x4001c000, 0x4000 },
> +    { "rp2040.padsqspi", 0x40020000, 0x4000 },
> +    { "rp2040.xosc",     0x40024000, 0x4000 },
> +    { "rp2040.pll_sys",  0x40028000, 0x4000 },
> +    { "rp2040.pll_usb",  0x4002c000, 0x4000 },
> +    { "rp2040.busctrl",  0x40030000, 0x4000 },
> +    { "rp2040.uart0_aliases", 0x40035000, 0x3000 },
> +    { "rp2040.uart1_aliases", 0x40039000, 0x3000 },
> +    { "rp2040.spi0",     0x4003c000, 0x4000 },
> +    { "rp2040.spi1",     0x40040000, 0x4000 },
> +    { "rp2040.i2c0",     0x40044000, 0x4000 },
> +    { "rp2040.i2c1",     0x40048000, 0x4000 },
> +    { "rp2040.adc",      0x4004c000, 0x4000 },
> +    { "rp2040.pwm",      0x40050000, 0x4000 },
> +    { "rp2040.timer",    0x40054000, 0x4000 },
> +    { "rp2040.watchdog", 0x40058000, 0x4000 },
> +    { "rp2040.rtc",      0x4005c000, 0x4000 },
> +    { "rp2040.rosc",     0x40060000, 0x4000 },
> +    { "rp2040.vreg_and_chip_reset", 0x40064000, 0x4000 },
> +    { "rp2040.tbman",    0x4006c000, 0x4000 },
> +    { "rp2040.dma",      0x50000000, 0x1000 },
> +    { "rp2040.usbctrl_dpram", 0x50100000, 0x10000 },
> +    { "rp2040.usbctrl_regs",  0x50110000, 0x10000 },
> +    { "rp2040.pio0",     0x50200000, 0x10000 },
> +    { "rp2040.pio1",     0x50300000, 0x10000 },
> +    { "rp2040.sio",      0xd0000000, 0x1000 },
> +};
> +
> +static void rp2040_soc_init(Object *obj)
> +{
> +    RP2040State *s = RP2040(obj);
> +    int i;
> +
> +    object_initialize_child(obj, "proc0", &s->armv7m, TYPE_ARMV7M);
> +    qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type",
> +                         ARM_CPU_TYPE_NAME("cortex-m0"));
> +    qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", RP2040_NUM_IRQS);
> +    qdev_prop_set_uint32(DEVICE(&s->armv7m), "mpu-ns-regions", 8);
> +
> +    for (i = 0; i < ARRAY_SIZE(s->uart); i++) {
> +        g_autofree char *name = g_strdup_printf("uart%d", i);
> +        g_autofree char *property = g_strdup_printf("serial%d", i);
> +
> +        object_initialize_child(obj, name, &s->uart[i], TYPE_PL011);
> +        object_property_add_alias(obj, property, OBJECT(&s->uart[i]),
> +                                  "chardev");
> +    }
> +
> +    s->sysclk = clock_new(obj, "sysclk");
> +    clock_set_hz(s->sysclk, RP2040_SYSCLK_FRQ);
> +}
> +
> +static bool rp2040_init_memory(RP2040State *s, Error **errp)
> +{
> +    int i;
> +
> +    if (!memory_region_init_rom(&s->rom, OBJECT(s), "rp2040.rom",
> +                                RP2040_ROM_SIZE, errp)) {
> +        return false;
> +    }
> +    memory_region_add_subregion(s->board_memory, RP2040_ROM_BASE, &s->rom);
> +
> +    for (i = 0; i < 4; i++) {
> +        g_autofree char *name = g_strdup_printf("rp2040.sram%d", i);
> +
> +        if (!memory_region_init_ram(&s->sram[i], OBJECT(s), name,
> +                                    RP2040_SRAM_BANK_SIZE, errp)) {
> +            return false;
> +        }
> +        memory_region_add_subregion(s->board_memory,
> +                                    RP2040_SRAM_BASE +
> +                                    i * RP2040_SRAM_BANK_SIZE,
> +                                    &s->sram[i]);
> +    }
> +
> +    if (!memory_region_init_ram(&s->sram[4], OBJECT(s), "rp2040.sram4",
> +                                RP2040_SRAM_HI_SIZE, errp)) {
> +        return false;
> +    }
> +    memory_region_add_subregion(s->board_memory, RP2040_SRAM4_BASE,
> +                                &s->sram[4]);
> +
> +    if (!memory_region_init_ram(&s->sram[5], OBJECT(s), "rp2040.sram5",
> +                                RP2040_SRAM_HI_SIZE, errp)) {
> +        return false;
> +    }
> +    memory_region_add_subregion(s->board_memory, RP2040_SRAM5_BASE,
> +                                &s->sram[5]);

I think you need multiple addresses spaces for the last two as they are
banked per-CPU right?

> +
> +    return true;
> +}
> +
> +static bool rp2040_load_bootrom(RP2040State *s, Error **errp)
> +{
> +    g_autofree char *filename = NULL;
> +
> +    if (!s->bootrom_file) {
> +        rom_add_blob_fixed("rp2040.bootrom", rp2040_bootrom,
> +                           sizeof(rp2040_bootrom), RP2040_ROM_BASE);
> +        return true;
> +    }
> +
> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bootrom_file);
> +    if (!filename) {
> +        error_setg(errp, "could not find RP2040 boot ROM image '%s'",
> +                   s->bootrom_file);
> +        return false;
> +    }
> +
> +    return load_image_targphys(filename, RP2040_ROM_BASE,
> +                               RP2040_ROM_SIZE, errp) >= 0;
> +}
> +
> +static void rp2040_soc_realize(DeviceState *dev, Error **errp)
> +{
> +    RP2040State *s = RP2040(dev);
> +    static const hwaddr uart_base[] = { RP2040_UART0_BASE, RP2040_UART1_BASE 
> };
> +    static const int uart_irq[] = { RP2040_UART0_IRQ, RP2040_UART1_IRQ };
> +    Error *err = NULL;
> +    int i;
> +
> +    if (!s->board_memory) {
> +        error_setg(errp, "memory property was not set");
> +        return;
> +    }
> +    if (!rp2040_init_memory(s, errp) || !rp2040_load_bootrom(s, errp)) {
> +        return;
> +    }
> +
> +    for (i = 0; i < ARRAY_SIZE(rp2040_unimplemented); i++) {
> +        create_unimplemented_device(rp2040_unimplemented[i].name,
> +                                    rp2040_unimplemented[i].base,
> +                                    rp2040_unimplemented[i].size);
> +    }
> +
> +    qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk);
> +    object_property_set_link(OBJECT(&s->armv7m), "memory",
> +                             OBJECT(s->board_memory), &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
> +        return;
> +    }
> +
> +    for (i = 0; i < ARRAY_SIZE(s->uart); i++) {
> +        qdev_connect_clock_in(DEVICE(&s->uart[i]), "clk", s->sysclk);
> +        if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> +            return;
> +        }
> +        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]));
> +    }
> +}
> +
> +static const Property rp2040_soc_properties[] = {
> +    DEFINE_PROP_LINK("memory", RP2040State, board_memory, TYPE_MEMORY_REGION,
> +                     MemoryRegion *),
> +    DEFINE_PROP_STRING("bootrom-file", RP2040State, bootrom_file),
> +};
> +
> +static void rp2040_soc_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = rp2040_soc_realize;
> +    device_class_set_props(dc, rp2040_soc_properties);
> +}
> +
> +static const TypeInfo rp2040_soc_info = {
> +    .name = TYPE_RP2040,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RP2040State),
> +    .instance_init = rp2040_soc_init,
> +    .class_init = rp2040_soc_class_init,
> +};
> +
> +static void rp2040_soc_types(void)
> +{
> +    type_register_static(&rp2040_soc_info);
> +}
> +type_init(rp2040_soc_types)
> diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h
> new file mode 100644
> index 0000000000..4e5cd06add
> --- /dev/null
> +++ b/include/hw/arm/rp2040.h
> @@ -0,0 +1,46 @@
> +/*
> + * RP2040 SoC emulation
> + *
> + * Copyright (c) 2021 Linaro Ltd
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_ARM_RP2040_H
> +#define HW_ARM_RP2040_H
> +
> +#include "hw/arm/armv7m.h"
> +#include "hw/char/pl011.h"
> +#include "hw/core/clock.h"
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RP2040 "rp2040"
> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040State, RP2040)
> +
> +#define RP2040_ROM_BASE       0x00000000
> +#define RP2040_ROM_SIZE       (16 * KiB)
> +#define RP2040_XIP_BASE       0x10000000
> +#define RP2040_SRAM_BASE      0x20000000
> +#define RP2040_SRAM_BANK_SIZE (64 * KiB)
> +#define RP2040_SRAM4_BASE     0x20040000
> +#define RP2040_SRAM5_BASE     0x20041000
> +#define RP2040_SRAM_HI_SIZE   (4 * KiB)
> +
> +#define RP2040_NUM_IRQS       32
> +
> +struct RP2040State {
> +    SysBusDevice parent_obj;
> +
> +    ARMv7MState armv7m;
> +    PL011State uart[2];
> +
> +    MemoryRegion *board_memory;
> +    MemoryRegion rom;
> +    MemoryRegion sram[6];
> +    char *bootrom_file;
> +
> +    Clock *sysclk;
> +};
> +
> +#endif

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro

Reply via email to