@samitouri / QOSamiQemu / commits / 33a71a68c6

hw/arm: Add Axiado SoC AX3000

This patch adds new model for Axiado SoC AX3000 which supports 4 Cortex-A53 ARM64 CPUs Arm Generic Interrupt Controller v3 4 Cadence UARTs Signed-off-by: Kuan-Jui Chiu <kchiu@axiado.com> Message-id: 20260713073033.3883619-2-kchiu@axiado.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: Kconfig for the SoC shouldn't depend on ARM] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Kuan-Jui Chiu committed Aug 11, 2026 at 20:14 UTC 33a71a68c6e137c80c9f6a7370d546404ed38513
5 files changed +266
MAINTAINERS
+7
@@ -1300,6 +1300,13 @@ M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
1300 S: Maintained
1301 F: rust/hw/char/pl011/
1302
1303 +Axiado SoCs and EVKs
1304 +M: Kuan-Jui Chiu <kchiu@axiado.com>
1305 +L: qemu-arm@nongnu.org
1306 +S: Maintained
1307 +F: hw/arm/ax3000*.c
1308 +F: include/hw/arm/ax3000*.h
1309 +
1310 AVR Machines
1311 -------------
1312
hw/arm/Kconfig
+6
@@ -725,3 +725,9 @@ config ARMSSE
725 select UNIMP
726 select SSE_COUNTER
727 select SSE_TIMER
728 +
729 +config AXIADO_SOC
730 + bool
731 + select ARM_GIC
732 + select CADENCE # UART
733 + select UNIMP
hw/arm/ax3000-soc.c new
+182
@@ -0,0 +1,182 @@
1 +/*
2 + * Axiado SoC AX3000
3 + *
4 + * Author: Kuan-Jui Chiu <kchiu@axiado.com>
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#include "qemu/osdep.h"
10 +#include "system/address-spaces.h"
11 +#include "hw/arm/bsa.h"
12 +#include "hw/arm/ax3000-soc.h"
13 +#include "hw/misc/unimp.h"
14 +#include "system/system.h"
15 +#include "qobject/qlist.h"
16 +#include "qom/object.h"
17 +#include "hw/core/boards.h"
18 +
19 +static void ax3000_init(Object *obj)
20 +{
21 + Ax3000SoCState *s = AX3000_SOC(obj);
22 + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s);
23 +
24 + for (int i = 0; i < sc->num_cpus; i++) {
25 + g_autofree char *name = g_strdup_printf("cpu%d", i);
26 + object_initialize_child(obj, name, &s->cpu[i],
27 + ARM_CPU_TYPE_NAME("cortex-a53"));
28 + }
29 +
30 + object_initialize_child(obj, "gic", &s->gic, gicv3_class_name());
31 +
32 + for (int i = 0; i < AX3000_NUM_UARTS; i++) {
33 + g_autofree char *name = g_strdup_printf("uart%d", i);
34 + object_initialize_child(obj, name, &s->uart[i], TYPE_CADENCE_UART);
35 + }
36 +}
37 +
38 +static void ax3000_realize(DeviceState *dev, Error **errp)
39 +{
40 + Ax3000SoCState *s = AX3000_SOC(dev);
41 + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s);
42 + SysBusDevice *gic_sbd = SYS_BUS_DEVICE(&s->gic);
43 + DeviceState *gic_dev = DEVICE(&s->gic);
44 + QList *redist_region_count;
45 + SysBusDevice *sdhci0_sbd;
46 + DeviceState *card;
47 +
48 + /* CPUs */
49 + for (int i = 0; i < sc->num_cpus; i++) {
50 + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000,
51 + &error_abort);
52 +
53 + if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) {
54 + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3",
55 + false, &error_abort);
56 + }
57 +
58 + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
59 + return;
60 + }
61 + }
62 +
63 + /* GIC */
64 + qdev_prop_set_uint32(gic_dev, "num-cpu", sc->num_cpus);
65 + qdev_prop_set_uint32(gic_dev, "num-irq",
66 + AX3000_NUM_IRQS + GIC_INTERNAL);
67 +
68 + redist_region_count = qlist_new();
69 + qlist_append_int(redist_region_count, sc->num_cpus);
70 + qdev_prop_set_array(gic_dev, "redist-region-count", redist_region_count);
71 +
72 + if (!sysbus_realize(gic_sbd, errp)) {
73 + return;
74 + }
75 +
76 + sysbus_mmio_map(gic_sbd, 0, AX3000_GIC_DIST_BASE);
77 + sysbus_mmio_map(gic_sbd, 1, AX3000_GIC_REDIST_BASE);
78 +
79 + /*
80 + * Mapping from the output timer irq lines from the CPU to the
81 + * GIC PPI inputs.
82 + */
83 + const int timer_irqs[] = {
84 + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
85 + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
86 + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
87 + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ
88 + };
89 +
90 + /*
91 + * Wire the outputs from each CPU's generic timer and the GICv3
92 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and
93 + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs.
94 + */
95 + for (int i = 0; i < sc->num_cpus; i++) {
96 + DeviceState *cpu_dev = DEVICE(&s->cpu[i]);
97 + int intidbase = AX3000_NUM_IRQS + i * GIC_INTERNAL;
98 + qemu_irq irq;
99 +
100 + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) {
101 + irq = qdev_get_gpio_in(gic_dev, intidbase + timer_irqs[j]);
102 + qdev_connect_gpio_out(cpu_dev, j, irq);
103 + }
104 +
105 + irq = qdev_get_gpio_in(gic_dev, intidbase + ARCH_GIC_MAINT_IRQ);
106 + qdev_connect_gpio_out_named(cpu_dev, "gicv3-maintenance-interrupt",
107 + 0, irq);
108 +
109 + sysbus_connect_irq(gic_sbd, i,
110 + qdev_get_gpio_in(cpu_dev, ARM_CPU_IRQ));
111 + sysbus_connect_irq(gic_sbd, i + sc->num_cpus,
112 + qdev_get_gpio_in(cpu_dev, ARM_CPU_FIQ));
113 + sysbus_connect_irq(gic_sbd, i + 2 * sc->num_cpus,
114 + qdev_get_gpio_in(cpu_dev, ARM_CPU_VIRQ));
115 + sysbus_connect_irq(gic_sbd, i + 3 * sc->num_cpus,
116 + qdev_get_gpio_in(cpu_dev, ARM_CPU_VFIQ));
117 + }
118 +
119 + /* DRAM */
120 + const struct {
121 + hwaddr addr;
122 + size_t size;
123 + const char *name;
124 + } dram_table[] = {
125 + { AX3000_DRAM0_BASE, AX3000_DRAM0_SIZE, "dram0" },
126 + { AX3000_DRAM1_BASE, AX3000_DRAM1_SIZE, "dram1" }
127 + };
128 +
129 + for (int i = 0; i < AX3000_NUM_BANKS; i++) {
130 + memory_region_init_ram(&s->dram[i], OBJECT(s), dram_table[i].name,
131 + dram_table[i].size, &error_fatal);
132 + memory_region_add_subregion(get_system_memory(), dram_table[i].addr,
133 + &s->dram[i]);
134 + }
135 +
136 + /* UARTs */
137 + const struct {
138 + hwaddr addr;
139 + unsigned int irq;
140 + } serial_table[] = {
141 + { AX3000_UART0_BASE, AX3000_UART0_IRQ },
142 + { AX3000_UART1_BASE, AX3000_UART1_IRQ },
143 + { AX3000_UART2_BASE, AX3000_UART2_IRQ },
144 + { AX3000_UART3_BASE, AX3000_UART3_IRQ }
145 + };
146 +
147 + for (int i = 0; i < AX3000_NUM_UARTS; i++) {
148 + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i));
149 + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
150 + return;
151 + }
152 +
153 + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr);
154 + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
155 + qdev_get_gpio_in(gic_dev, serial_table[i].irq));
156 + }
157 +
158 + /* Timer control */
159 + create_unimplemented_device("ax3000.timerctrl", AX3000_TIMER_CTRL, 32);
160 +}
161 +
162 +static void ax3000_class_init(ObjectClass *oc, const void *data)
163 +{
164 + DeviceClass *dc = DEVICE_CLASS(oc);
165 + Ax3000SoCClass *sc = AX3000_SOC_CLASS(oc);
166 +
167 + dc->desc = "Axiado SoC AX3000";
168 + dc->realize = ax3000_realize;
169 + sc->num_cpus = AX3000_NUM_CPUS;
170 +}
171 +
172 +static const TypeInfo axiado_soc_types[] = {
173 + {
174 + .name = TYPE_AX3000_SOC,
175 + .parent = TYPE_SYS_BUS_DEVICE,
176 + .instance_size = sizeof(Ax3000SoCState),
177 + .instance_init = ax3000_init,
178 + .class_init = ax3000_class_init,
179 + }
180 +};
181 +
182 +DEFINE_TYPES(axiado_soc_types)
hw/arm/meson.build
+3
@@ -110,6 +110,9 @@ arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
110 arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
111 arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
112
113 +arm_common_ss.add(when: ['CONFIG_AXIADO_SOC', 'TARGET_AARCH64'], if_true: files(
114 + 'ax3000-soc.c'))
115 +
116 arm_common_ss.add(files('boot.c'))
117
118 hw_common_arch += {'arm': arm_common_ss}
include/hw/arm/ax3000-soc.h new
+68
@@ -0,0 +1,68 @@
1 +/*
2 + * Axiado SoC AX3000
3 + *
4 + * Author: Kuan-Jui Chiu <kchiu@axiado.com>
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#ifndef AXIADO_AX3000_H
10 +#define AXIADO_AX3000_H
11 +
12 +#include "cpu.h"
13 +#include "hw/intc/arm_gicv3_common.h"
14 +#include "hw/char/cadence_uart.h"
15 +#include "hw/core/sysbus.h"
16 +#include "qemu/units.h"
17 +
18 +#define TYPE_AX3000_SOC "ax3000"
19 +OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX3000_SOC)
20 +
21 +#define AX3000_DRAM0_BASE 0x3C000000
22 +#define AX3000_DRAM0_SIZE (1088 * MiB)
23 +#define AX3000_DRAM1_BASE 0x400000000
24 +#define AX3000_DRAM1_SIZE (2 * GiB)
25 +
26 +#define AX3000_GIC_DIST_BASE 0x80300000
27 +#define AX3000_GIC_DIST_SIZE (64 * KiB)
28 +#define AX3000_GIC_REDIST_BASE 0x80380000
29 +#define AX3000_GIC_REDIST_SIZE (512 * KiB)
30 +
31 +#define AX3000_UART0_BASE 0x80520000
32 +#define AX3000_UART1_BASE 0x805a0000
33 +#define AX3000_UART2_BASE 0x80620000
34 +#define AX3000_UART3_BASE 0x80520800
35 +
36 +#define AX3000_TIMER_CTRL 0x8A020000
37 +#define AX3000_PLL_BASE 0x80000000
38 +
39 +enum Ax3000Configuration {
40 + AX3000_NUM_CPUS = 4,
41 + AX3000_NUM_IRQS = 224,
42 + AX3000_NUM_BANKS = 2,
43 + AX3000_NUM_UARTS = 4,
44 +};
45 +
46 +typedef struct Ax3000SoCState {
47 + SysBusDevice parent;
48 +
49 + ARMCPU cpu[AX3000_NUM_CPUS];
50 + GICv3State gic;
51 + MemoryRegion dram[AX3000_NUM_BANKS];
52 + CadenceUARTState uart[AX3000_NUM_UARTS];
53 +} Ax3000SoCState;
54 +
55 +typedef struct Ax3000SoCClass {
56 + SysBusDeviceClass parent;
57 +
58 + uint32_t num_cpus;
59 +} Ax3000SoCClass;
60 +
61 +enum Ax3000Irqs {
62 + AX3000_UART0_IRQ = 112,
63 + AX3000_UART1_IRQ = 113,
64 + AX3000_UART2_IRQ = 114,
65 + AX3000_UART3_IRQ = 170,
66 +};
67 +
68 +#endif /* AXIADO_AX3000_H */