Introduce TYPE_TT_ATLANTIS_SOC and move the hart array, AIA, ACLINT, boot ROM, UART and I2C controllers into the new TTAtlantisSoCState. The machine keeps ownership of RAM, device tree generation, firmware loading, and the board-level I2C slaves.
This makes it easier to compose the SoC into a more complex machine in the future. Signed-off-by: Joel Stanley <[email protected]> --- include/hw/riscv/tt_atlantis.h | 21 ++++- hw/riscv/tt_atlantis.c | 144 +++++++++++++++++++++++---------- 2 files changed, 119 insertions(+), 46 deletions(-) diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h index 9ca98bfb8478..5d4e4d133bd9 100644 --- a/include/hw/riscv/tt_atlantis.h +++ b/include/hw/riscv/tt_atlantis.h @@ -19,14 +19,16 @@ #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE) +#define TYPE_TT_ATLANTIS_SOC "tt-atlantis-soc" +OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisSoCState, TT_ATLANTIS_SOC) + #define TT_ATL_NUM_I2C 5 -struct TTAtlantisState { +struct TTAtlantisSoCState { /*< private >*/ - MachineState parent; + DeviceState parent; /*< public >*/ - Notifier machine_done; const MemMapEntry *memmap; MemoryRegion *memory; @@ -39,6 +41,19 @@ struct TTAtlantisState { DesignWareI2CState i2c[TT_ATL_NUM_I2C]; UnimplementedDeviceState uart1; MemoryRegion bootrom; + + uint32_t num_harts; + char *cpu_type; +}; + +struct TTAtlantisState { + /*< private >*/ + MachineState parent; + + /*< public >*/ + Notifier machine_done; + + TTAtlantisSoCState soc; }; enum { diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index b80bdf54c40b..d2b29ff1a477 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/guest-random.h" @@ -14,6 +15,7 @@ #include "hw/core/boards.h" #include "hw/core/loader.h" +#include "hw/core/qdev-properties.h" #include "hw/core/sysbus.h" #include "target/riscv/cpu.h" @@ -67,7 +69,7 @@ static const MemMapEntry tt_atlantis_memmap[] = { [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, }; -static I2CBus *i2c_get_bus(TTAtlantisState *s, unsigned busnr) +static I2CBus *i2c_get_bus(TTAtlantisSoCState *s, unsigned busnr) { assert(busnr < TT_ATL_NUM_I2C); @@ -80,7 +82,7 @@ static uint32_t next_phandle(void) return fdt_phandle++; } -static void create_fdt_memory(void *fdt, TTAtlantisState *s) +static void create_fdt_memory(void *fdt, TTAtlantisSoCState *s) { hwaddr ram_size = memory_region_size(s->dram); hwaddr size_lo = ram_size; @@ -104,7 +106,7 @@ static void create_fdt_memory(void *fdt, TTAtlantisState *s) } } -static void create_fdt_aclint(void *fdt, TTAtlantisState *s, +static void create_fdt_aclint(void *fdt, TTAtlantisSoCState *s, uint32_t *intc_phandles) { g_autofree char *name = NULL; @@ -206,7 +208,7 @@ static void create_fdt_one_aplic(void *fdt, qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle); } -static void create_fdt_pmu(void *fdt, TTAtlantisState *s) +static void create_fdt_pmu(void *fdt, TTAtlantisSoCState *s) { char pmu_name[] = "/pmu"; RISCVCPU *hart = &s->cpus.harts[0]; @@ -216,7 +218,7 @@ static void create_fdt_pmu(void *fdt, TTAtlantisState *s) riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name); } -static void create_fdt_cpu(void *fdt, TTAtlantisState *s, +static void create_fdt_cpu(void *fdt, TTAtlantisSoCState *s, uint32_t aplic_s_phandle, uint32_t imsic_s_phandle) { @@ -313,7 +315,7 @@ static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq, qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0); } -static void create_fdt_i2c_device(void *fdt, TTAtlantisState *s, int bus, +static void create_fdt_i2c_device(void *fdt, TTAtlantisSoCState *s, int bus, const char *compat, int addr) { hwaddr base = s->memmap[TT_ATL_I2C0 + bus].base; @@ -325,7 +327,7 @@ static void create_fdt_i2c_device(void *fdt, TTAtlantisState *s, int bus, qemu_fdt_setprop_cell(fdt, name, "reg", addr); } -static void finalize_fdt(void *fdt, TTAtlantisState *s) +static void finalize_fdt(void *fdt, TTAtlantisSoCState *s) { uint32_t aplic_s_phandle = next_phandle(); uint32_t imsic_s_phandle = next_phandle(); @@ -359,9 +361,9 @@ static void finalize_fdt(void *fdt, TTAtlantisState *s) create_fdt_i2c_device(fdt, s, 4, "ti,tmp105", 0x48); } -static void create_fdt(TTAtlantisState *s) +static void create_fdt(TTAtlantisState *ams) { - MachineState *ms = MACHINE(s); + MachineState *ms = MACHINE(ams); int fdt_size = 0; ms->fdt = create_board_device_tree("Tenstorrent Atlantis RISC-V Machine", @@ -373,12 +375,12 @@ static void create_fdt(TTAtlantisState *s) qemu_fdt_add_subnode(ms->fdt, "/aliases"); - create_fdt_pmu(ms->fdt, s); + create_fdt_pmu(ms->fdt, &ams->soc); } -static void load_fdt(TTAtlantisState *s) +static void load_fdt(TTAtlantisState *ams) { - MachineState *ms = MACHINE(s); + MachineState *ms = MACHINE(ams); char **node_path; Error *err = NULL; int fdt_size = 0; @@ -404,7 +406,7 @@ static void load_fdt(TTAtlantisState *s) g_strfreev(node_path); } - create_fdt_memory(ms->fdt, s); + create_fdt_memory(ms->fdt, &ams->soc); } static void mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev, @@ -418,10 +420,11 @@ static void mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev, sysbus_mmio_get_region(dev, 0), -1000); } -static void tt_atlantis_machine_done(Notifier *notifier, void *data) +static void tt_atlantis_machine_done(Notifier *n, void *data) { - TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done); - MachineState *machine = MACHINE(s); + TTAtlantisState *ams = container_of(n, TTAtlantisState, machine_done); + TTAtlantisSoCState *s = &ams->soc; + MachineState *machine = MACHINE(ams); hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base; hwaddr mem_size; target_ulong firmware_end_addr, kernel_start_addr; @@ -479,23 +482,39 @@ static void tt_atlantis_machine_done(Notifier *notifier, void *data) fdt_load_addr); } -static void tt_atlantis_machine_init(MachineState *machine) +static void tt_atlantis_soc_init(Object *obj) { - TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine); + TTAtlantisSoCState *s = TT_ATLANTIS_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); + + object_initialize_child(obj, "uart1", &s->uart1, + TYPE_UNIMPLEMENTED_DEVICE); + for (int i = 0; i < TT_ATL_NUM_I2C; i++) { + object_initialize_child(obj, "i2c[*]", &s->i2c[i], + TYPE_DESIGNWARE_I2C); + } +} + +static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp) +{ + TTAtlantisSoCState *s = TT_ATLANTIS_SOC(dev); ram_addr_t lo_ram_size, ram_size; - int hart_count = machine->smp.cpus; + int hart_count = s->num_harts; s->memory = get_system_memory(); - s->dram = machine->ram; + if (!s->dram) { + error_setg(errp, "'dram' link is not set"); + return; + } ram_size = memory_region_size(s->dram); s->memmap = tt_atlantis_memmap; - object_initialize_child(OBJECT(machine), "soc", &s->cpus, - TYPE_RISCV_HART_ARRAY); - object_property_set_str(OBJECT(&s->cpus), "cpu-type", machine->cpu_type, + /* CPUs */ + object_property_set_str(OBJECT(&s->cpus), "cpu-type", s->cpu_type, &error_abort); object_property_set_int(OBJECT(&s->cpus), "hartid-base", 0, &error_abort); @@ -504,7 +523,9 @@ static void tt_atlantis_machine_init(MachineState *machine) object_property_set_int(OBJECT(&s->cpus), "resetvec", s->memmap[TT_ATL_BOOTROM].base, &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpus), errp)) { + return; + } s->irqchip = riscv_create_aia(s->memory, true, TT_IMSIC_GUESTS, @@ -534,26 +555,27 @@ static void tt_atlantis_machine_init(MachineState *machine) * up to 64GB. The low address is an alias of the first 2GB of that RAM. */ if (ram_size > s->memmap[TT_ATL_DDR_HI].size) { - char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size); - error_report("RAM size is too large, maximum is %s", sz); - g_free(sz); - exit(EXIT_FAILURE); + g_autofree char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size); + error_setg(errp, "RAM size is too large, maximum is %s", sz); + return; } - memory_region_init_alias(&s->ram_hi, OBJECT(machine), "ram.high", s->dram, + memory_region_init_alias(&s->ram_hi, OBJECT(s), "ram.high", s->dram, 0, ram_size); memory_region_add_subregion(s->memory, s->memmap[TT_ATL_DDR_HI].base, &s->ram_hi); lo_ram_size = MIN(ram_size, s->memmap[TT_ATL_DDR_LO].size); - memory_region_init_alias(&s->ram_lo, OBJECT(machine), "ram.low", s->dram, + memory_region_init_alias(&s->ram_lo, OBJECT(s), "ram.low", s->dram, 0, lo_ram_size); memory_region_add_subregion(s->memory, s->memmap[TT_ATL_DDR_LO].base, &s->ram_lo); /* Boot ROM */ - memory_region_init_rom(&s->bootrom, NULL, "tt-atlantis.bootrom", - s->memmap[TT_ATL_BOOTROM].size, &error_fatal); + if (!memory_region_init_rom(&s->bootrom, OBJECT(s), "tt-atlantis.bootrom", + s->memmap[TT_ATL_BOOTROM].size, errp)) { + return; + } memory_region_add_subregion(s->memory, s->memmap[TT_ATL_BOOTROM].base, &s->bootrom); @@ -570,26 +592,56 @@ static void tt_atlantis_machine_init(MachineState *machine) * Create an unimplemented device region so writes don't fault * and reads return zero, which keeps Linux happy. */ - object_initialize_child(OBJECT(s), "uart1", &s->uart1, - TYPE_UNIMPLEMENTED_DEVICE); mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->uart1), "tt-atlantis.uart1", s->memmap[TT_ATL_UART1].base, s->memmap[TT_ATL_UART1].size); /* I2C */ for (int i = 0; i < TT_ATL_NUM_I2C; i++) { - SysBusDevice *sbd; + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->i2c[i]); - object_initialize_child(OBJECT(s), "i2c[*]", &s->i2c[i], - TYPE_DESIGNWARE_I2C); - sbd = SYS_BUS_DEVICE(&s->i2c[i]); - sysbus_realize(sbd, &error_fatal); + if (!sysbus_realize(sbd, errp)) { + return; + } memory_region_add_subregion(s->memory, s->memmap[TT_ATL_I2C0 + i].base, sysbus_mmio_get_region(sbd, 0)); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i)); } +} + +static const Property tt_atlantis_soc_props[] = { + DEFINE_PROP_STRING("cpu-type", TTAtlantisSoCState, cpu_type), + DEFINE_PROP_UINT32("num-harts", TTAtlantisSoCState, num_harts, 8), + DEFINE_PROP_LINK("dram", TTAtlantisSoCState, dram, + TYPE_MEMORY_REGION, MemoryRegion *), +}; + +static void tt_atlantis_soc_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = tt_atlantis_soc_realize; + device_class_set_props(dc, tt_atlantis_soc_props); + /* The SoC can only be instantiated from the machine */ + dc->user_creatable = false; +} + +static void tt_atlantis_machine_init(MachineState *machine) +{ + TTAtlantisState *ams = TT_ATLANTIS_MACHINE(machine); + TTAtlantisSoCState *s = &ams->soc; + + object_initialize_child(OBJECT(machine), "soc", &ams->soc, + TYPE_TT_ATLANTIS_SOC); + object_property_set_str(OBJECT(&ams->soc), "cpu-type", machine->cpu_type, + &error_abort); + object_property_set_int(OBJECT(&ams->soc), "num-harts", machine->smp.cpus, + &error_abort); + object_property_set_link(OBJECT(&ams->soc), "dram", OBJECT(machine->ram), + &error_abort); + qdev_realize(DEVICE(&ams->soc), NULL, &error_fatal); /* I2C peripherals: qemu specific */ i2c_slave_create_simple(i2c_get_bus(s, 0), "ds1338", 0x6f); @@ -597,13 +649,13 @@ static void tt_atlantis_machine_init(MachineState *machine) /* Load or create device tree */ if (machine->dtb) { - load_fdt(s); + load_fdt(ams); } else { - create_fdt(s); + create_fdt(ams); } - s->machine_done.notify = tt_atlantis_machine_done; - qemu_add_machine_init_done_notifier(&s->machine_done); + ams->machine_done.notify = tt_atlantis_machine_done; + qemu_add_machine_init_done_notifier(&ams->machine_done); } static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data) @@ -623,6 +675,12 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data) static const TypeInfo tt_atlantis_types[] = { { + .name = TYPE_TT_ATLANTIS_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(TTAtlantisSoCState), + .instance_init = tt_atlantis_soc_init, + .class_init = tt_atlantis_soc_class_init, + }, { .name = MACHINE_TYPE_NAME("tt-atlantis"), .parent = TYPE_MACHINE, .class_init = tt_atlantis_machine_class_init, -- 2.47.3
