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 */