The AST1040 is based on an ARM Cortex-M4F CPU core. Since QEMU
currently does not provide Cortex-M4F support, use the existing
Cortex-M4 CPU model as a temporary replacement.

This initial implementation provides the basic infrastructure
required to boot firmware and run a minimal firmware shell,
including:

- ARM Cortex-M4 CPU integration
- NVIC interrupt controller support
- Internal SDRAM and SRAM memory regions
- SCU integration
- UART devices and interrupt wiring

AST1040 SCU behavior is compatible with the AST2700 SCUIO model,
so reuse the existing AST2700 SCUIO implementation directly
instead of introducing another identical SCU model. This reduces
duplicate code and helps minimize long-term codebase maintenance.

Several peripherals are currently modeled as unimplemented
devices and can be added incrementally in future updates.

Signed-off-by: Jamin Lin <[email protected]>
---
 hw/arm/aspeed_ast1040.c | 246 ++++++++++++++++++++++++++++++++++++++++
 hw/arm/meson.build      |   3 +-
 2 files changed, 248 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/aspeed_ast1040.c

diff --git a/hw/arm/aspeed_ast1040.c b/hw/arm/aspeed_ast1040.c
new file mode 100644
index 0000000000..615f9c4e15
--- /dev/null
+++ b/hw/arm/aspeed_ast1040.c
@@ -0,0 +1,246 @@
+/*
+ * ASPEED AST1040 SoC
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "system/address-spaces.h"
+#include "system/system.h"
+#include "hw/core/qdev-clock.h"
+#include "hw/misc/unimp.h"
+#include "hw/arm/aspeed_soc.h"
+
+static const hwaddr aspeed_soc_ast1040_memmap[] = {
+    [ASPEED_DEV_SDRAM]     = 0x00000000,
+    [ASPEED_DEV_FMC]       = 0x74000000,
+    [ASPEED_DEV_SPI0]      = 0x74010000,
+    [ASPEED_DEV_SPI1]      = 0x74020000,
+    [ASPEED_DEV_PWM]       = 0x740C0000,
+    [ASPEED_DEV_UDC]       = 0x74120000,
+    [ASPEED_DEV_SRAM]      = 0x74B80000,
+    [ASPEED_DEV_ADC]       = 0x74C00000,
+    [ASPEED_DEV_JTAG0]     = 0x74C01000,
+    [ASPEED_DEV_SCU]       = 0x74C02000,
+    [ASPEED_DEV_ESPI]      = 0x74C05000,
+    [ASPEED_DEV_JTAG1]     = 0x74C09000,
+    [ASPEED_DEV_GPIO]      = 0x74C0B000,
+    [ASPEED_DEV_SGPIOM0]   = 0x74C0C000,
+    [ASPEED_DEV_SGPIOM1]   = 0x74C0D000,
+    [ASPEED_DEV_I2C]       = 0x74C0F000,
+    [ASPEED_DEV_I3C]       = 0x74C20000,
+    [ASPEED_DEV_UART0]     = 0x74C33000,
+    [ASPEED_DEV_UART1]     = 0x74C33100,
+    [ASPEED_DEV_UART2]     = 0x74C33200,
+    [ASPEED_DEV_UART3]     = 0x74C33300,
+    [ASPEED_DEV_UART4]     = 0x74C33400,
+    [ASPEED_DEV_UART5]     = 0x74C33500,
+    [ASPEED_DEV_UART6]     = 0x74C33600,
+    [ASPEED_DEV_UART7]     = 0x74C33700,
+    [ASPEED_DEV_UART8]     = 0x74C33800,
+    [ASPEED_DEV_UART9]     = 0x74C33900,
+    [ASPEED_DEV_UART10]    = 0x74C33A00,
+    [ASPEED_DEV_UART11]    = 0x74C33B00,
+    [ASPEED_DEV_UART12]    = 0x74C33C00,
+    [ASPEED_DEV_WDT]       = 0x74C37000,
+    [ASPEED_DEV_TIMER1]    = 0x74C3A000,
+};
+
+static const int aspeed_soc_ast1040_irqmap[] = {
+    [ASPEED_DEV_ESPI]      = 10,
+    [ASPEED_DEV_I2C]       = 64, /* 64 ~ 77 */
+    [ASPEED_DEV_ADC]       = 80,
+    [ASPEED_DEV_GPIO]      = 82,
+    [ASPEED_DEV_SGPIOM0]   = 85,
+    [ASPEED_DEV_TIMER1]    = 92,
+    [ASPEED_DEV_I3C]       = 96, /* 96 ~ 103 */
+    [ASPEED_DEV_WDT]       = 112,
+    [ASPEED_DEV_FMC]       = 121,
+    [ASPEED_DEV_SPI0]      = 122,
+    [ASPEED_DEV_SPI1]      = 123,
+    [ASPEED_DEV_PWM]       = 125,
+    [ASPEED_DEV_UART0]     = 135,
+    [ASPEED_DEV_UART1]     = 136,
+    [ASPEED_DEV_UART2]     = 137,
+    [ASPEED_DEV_UART3]     = 138,
+    [ASPEED_DEV_UART4]     = 139,
+    [ASPEED_DEV_UART5]     = 140,
+    [ASPEED_DEV_UART6]     = 141,
+    [ASPEED_DEV_UART7]     = 142,
+    [ASPEED_DEV_UART8]     = 143,
+    [ASPEED_DEV_UART9]     = 144,
+    [ASPEED_DEV_UART10]    = 145,
+    [ASPEED_DEV_UART11]    = 146,
+    [ASPEED_DEV_UART12]    = 147,
+    [ASPEED_DEV_JTAG0]     = 162,
+};
+
+static qemu_irq aspeed_soc_ast1040_get_irq(AspeedSoCState *s, int dev)
+{
+    Aspeed10x0SoCState *a = ASPEED10X0_SOC(s);
+    AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+
+    return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]);
+}
+
+static void aspeed_soc_ast1040_init(Object *obj)
+{
+    Aspeed10x0SoCState *a = ASPEED10X0_SOC(obj);
+    AspeedSoCState *s = ASPEED_SOC(obj);
+    AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+    int i;
+    object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M);
+
+    s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+
+    /* AST1040 uses the AST2700 IO SCU model */
+    object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCUIO);
+    qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev);
+
+    object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1");
+    object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2");
+
+    for (i = 0; i < sc->uarts_num; i++) {
+        object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM);
+    }
+
+    object_initialize_child(obj, "pwm", &s->pwm, TYPE_UNIMPLEMENTED_DEVICE);
+    object_initialize_child(obj, "espi", &s->espi, TYPE_UNIMPLEMENTED_DEVICE);
+    object_initialize_child(obj, "udc", &s->udc, TYPE_UNIMPLEMENTED_DEVICE);
+    object_initialize_child(obj, "sgpiom", &s->sgpiom[0],
+                            TYPE_UNIMPLEMENTED_DEVICE);
+    object_initialize_child(obj, "jtag[0]", &s->jtag[0],
+                            TYPE_UNIMPLEMENTED_DEVICE);
+    object_initialize_child(obj, "jtag[1]", &s->jtag[1],
+                            TYPE_UNIMPLEMENTED_DEVICE);
+}
+
+static void aspeed_soc_ast1040_realize(DeviceState *dev_soc, Error **errp)
+{
+    Aspeed10x0SoCState *a = ASPEED10X0_SOC(dev_soc);
+    AspeedSoCState *s = ASPEED_SOC(dev_soc);
+    AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+    DeviceState *armv7m;
+    Error *err = NULL;
+    char name[64];
+    int uart;
+    int i;
+
+    if (!clock_has_source(s->sysclk)) {
+        error_setg(errp, "sysclk clock must be wired up by the board code");
+        return;
+    }
+
+    /* AST1040 CPU Core */
+    armv7m = DEVICE(&a->armv7m);
+    qdev_prop_set_uint32(armv7m, "num-irq", 256);
+    qdev_prop_set_string(armv7m, "cpu-type",
+                         aspeed_soc_cpu_type(sc->valid_cpu_types));
+    qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
+    object_property_set_link(OBJECT(&a->armv7m), "memory",
+                             OBJECT(s->memory), &error_abort);
+    sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort);
+
+    /* Internal SDRAM */
+    snprintf(name, sizeof(name), "aspeed.sdram.%d",
+             CPU(a->armv7m.cpu)->cpu_index);
+    memory_region_init_ram(&s->sdram, OBJECT(s), name, sc->sdram_size, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SDRAM],
+                                &s->sdram);
+
+    /* Internal SRAM */
+    snprintf(name, sizeof(name), "aspeed.sram.%d",
+             CPU(a->armv7m.cpu)->cpu_index);
+    memory_region_init_ram(&s->sram, OBJECT(s), name, sc->sram_size, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM],
+                                &s->sram);
+
+    /* SCU */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) {
+        return;
+    }
+    aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0,
+                    sc->memmap[ASPEED_DEV_SCU]);
+
+    /* UART */
+    for (i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) {
+        if (!aspeed_soc_uart_realize(s->memory, &s->uart[i],
+                                     sc->memmap[uart], errp)) {
+            return;
+        }
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+                           aspeed_soc_ast1040_get_irq(s, uart));
+    }
+
+    /* Unimplemented peripherals */
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->pwm),
+                                  "aspeed.pwm",
+                                  sc->memmap[ASPEED_DEV_PWM], 0x10000);
+
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->espi),
+                                  "aspeed.espi",
+                                  sc->memmap[ASPEED_DEV_ESPI], 0x1000);
+
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->udc),
+                                  "aspeed.udc",
+                                  sc->memmap[ASPEED_DEV_UDC], 0x4000);
+
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->sgpiom[0]),
+                                  "aspeed.sgpiom",
+                                  sc->memmap[ASPEED_DEV_SGPIOM0], 0x1000);
+
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->jtag[0]),
+                                  "aspeed.jtag0",
+                                  sc->memmap[ASPEED_DEV_JTAG0], 0x100);
+
+    aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->jtag[1]),
+                                  "aspeed.jtag1",
+                                  sc->memmap[ASPEED_DEV_JTAG1], 0x100);
+}
+
+static void aspeed_soc_ast1040_class_init(ObjectClass *klass, const void *data)
+{
+    static const char * const valid_cpu_types[] = {
+        ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */
+        NULL
+    };
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc);
+
+    /* Reason: The Aspeed SoC can only be instantiated from a board */
+    dc->user_creatable = false;
+    dc->realize = aspeed_soc_ast1040_realize;
+
+    sc->valid_cpu_types = valid_cpu_types;
+    sc->silicon_rev     = AST1040_A0_SILICON_REV;
+    sc->sdram_size      = 16 * MiB;
+    sc->sram_size       = 512 * KiB;
+    sc->uarts_num       = 13;
+    sc->uarts_base      = ASPEED_DEV_UART0;
+    sc->irqmap          = aspeed_soc_ast1040_irqmap;
+    sc->memmap          = aspeed_soc_ast1040_memmap;
+    sc->num_cpus        = 1;
+}
+
+static const TypeInfo aspeed_soc_ast1040_types[] = {
+    {
+        .name           = "ast1040-a0",
+        .parent         = TYPE_ASPEED10X0_SOC,
+        .instance_init  = aspeed_soc_ast1040_init,
+        .class_init     = aspeed_soc_ast1040_class_init,
+    }
+};
+
+DEFINE_TYPES(aspeed_soc_ast1040_types)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 80068f70bb..fa3a848492 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -62,7 +62,8 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
   'aspeed_ast2600_gb200nvl.c',
   'aspeed_ast2600_rainier.c',
   'aspeed_ast10x0.c',
-  'aspeed_ast10x0_evb.c'))
+  'aspeed_ast10x0_evb.c',
+  'aspeed_ast1040.c'))
 arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: 
files(
   'aspeed_ast1700.c',
   'aspeed_ast27x0.c',
-- 
2.43.0

Reply via email to