Add an AST1040 variant of the ASPEED SCU with reset values, APB clock calculation, clock-stop handling, and CPTRA page-select registers.
Expose the Caliptra page aperture as a QOM link so the SoC model can wire the remap window without callbacks.
Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
---
include/hw/misc/aspeed_scu.h | 10 ++
hw/misc/aspeed_scu.c | 251 +++++++++++++++++++++++++++++++++++
hw/misc/trace-events | 2 +
3 files changed, 263 insertions(+)
diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h
index c30940ab76..6445ae5cf4 100644
--- a/include/hw/misc/aspeed_scu.h
+++ b/include/hw/misc/aspeed_scu.h
@@ -22,6 +22,7 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU)
#define TYPE_ASPEED_2700_SCU TYPE_ASPEED_SCU "-ast2700"
#define TYPE_ASPEED_2700_SCUIO TYPE_ASPEED_SCU "io" "-ast2700"
#define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030"
+#define TYPE_ASPEED_1040_SCU TYPE_ASPEED_SCU "-ast1040"
#define ASPEED_SCU_NR_REGS (0x1A8 >> 2)
#define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2)
@@ -39,6 +40,15 @@ struct AspeedSCUState {
uint32_t hw_strap1;
uint32_t hw_strap2;
uint32_t hw_prot_key;
+
+ /*
+ * AST1040 only: alias region (the Caliptra MCI page aperture) whose
+ * target offset is selected by SCU_CPTRA_PAGE_REG0, linked in by the SoC.
+ * cptra_page_window_base is its base in the address map, used to reject a
+ * self-referential remap.
+ */
+ MemoryRegion *cptra_page_window;
+ uint64_t cptra_page_window_base;
};
#define AST2400_A1_SILICON_REV 0x02010303U
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
index 19da6c075f..028677880f 100644
--- a/hw/misc/aspeed_scu.c
+++ b/hw/misc/aspeed_scu.c
@@ -17,6 +17,7 @@
#include "qapi/visitor.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
+#include "qemu/range.h"
#include "qemu/guest-random.h"
#include "qemu/module.h"
#include "trace.h"
@@ -179,6 +180,41 @@
#define AST2700_SCUIO_CLK_DUTY_MEAS_RST TO_REG(0x388)
#define AST2700_SCUIO_FREQ_CNT_CTL TO_REG(0x3A0)
+/* AST1040 SCU */
+#define AST1040_SILICON_REV TO_REG(0x00)
+#define AST1040_HW_STRAP1 TO_REG(0x10)
+#define AST1040_HW_STRAP1_CLR TO_REG(0x14)
+#define AST1040_HW_STRAP1_LOCK TO_REG(0x20)
+#define AST1040_HW_STRAP1_SEC1 TO_REG(0x24)
+#define AST1040_SCU_CPTRA_PAGE_REG0 TO_REG(0x120)
+#define AST1040_SCU_CPTRA_PAGE_REG1 TO_REG(0x124)
+#define AST1040_SCU_CPTRA_PAGE_REG2 TO_REG(0x128)
+#define AST1040_SCU_CPTRA_PAGE_REG3 TO_REG(0x12C)
+#define AST1040_SCU_CPTRA_PAGE_REG4 TO_REG(0x130)
+#define AST1040_SCU_CPTRA_PAGE_REG5 TO_REG(0x134)
+#define AST1040_SCU_CLK_STOP_CTL_1 TO_REG(0x240)
+#define AST1040_SCU_CLK_STOP_CLR_1 TO_REG(0x244)
+#define AST1040_SCU_CLK_STOP_CTL_2 TO_REG(0x260)
+#define AST1040_SCU_CLK_STOP_CLR_2 TO_REG(0x264)
+#define AST1040_SCU_CLK_SEL_1 TO_REG(0x280)
+#define AST1040_SCU_CLK_SEL_2 TO_REG(0x284)
+#define AST1040_SCU_HPLL_PARAM TO_REG(0x300)
+#define AST1040_SCU_HPLL_EXT_PARAM TO_REG(0x304)
+#define AST1040_SCU_APLL_PARAM TO_REG(0x310)
+#define AST1040_SCU_APLL_EXT_PARAM TO_REG(0x314)
+#define AST1040_SCU_DPLL_PARAM TO_REG(0x320)
+#define AST1040_SCU_DPLL_EXT_PARAM TO_REG(0x324)
+#define AST1040_SCU_DPLL_PARAM_READ TO_REG(0x328)
+#define AST1040_SCU_DPLL_EXT_PARAM_READ TO_REG(0x32c)
+#define AST1040_SCU_UARTCLK_GEN TO_REG(0x330)
+#define AST1040_SCU_HUARTCLK_GEN TO_REG(0x334)
+#define AST1040_SCU_CLK_DUTY_MEAS_RST TO_REG(0x388)
+#define AST1040_SCU_FREQ_CNT_CTL TO_REG(0x3A0)
+#define AST1040_SCU_CLK_GET_PCLK_DIV(x) (((x) >> 18) & 0x7)
+
+#define ASPEED_AST1040_SCU_NR_REGS (0xE20 >> 2)
+#define AST1040_SCU_CPTRA_PAGE_SIZE 0x1000
+
#define SCU_IO_REGION_SIZE 0x1000
static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = {
@@ -1165,6 +1201,220 @@ static const TypeInfo aspeed_1030_scu_info = {
.class_init = aspeed_1030_scu_class_init,
};
+/* AST1040 SCU */
+
+static void aspeed_ast1040_scu_update_cptra_page(AspeedSCUState *s)
+{
+ hwaddr page_base = s->regs[AST1040_SCU_CPTRA_PAGE_REG0] &
+ ~(hwaddr)(AST1040_SCU_CPTRA_PAGE_SIZE - 1);
+
+ if (!s->cptra_page_window) {
+ return;
+ }
+
+ if (ranges_overlap(page_base, AST1040_SCU_CPTRA_PAGE_SIZE,
+ s->cptra_page_window_base,
+ AST1040_SCU_CPTRA_PAGE_SIZE)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: refusing self-referential remap to 0x%" HWADDR_PRIx
+ "\n", __func__, page_base);
+ memory_region_set_enabled(s->cptra_page_window, false);
+ return;
+ }
+
+ memory_region_set_alias_offset(s->cptra_page_window, page_base);
+ memory_region_set_enabled(s->cptra_page_window, true);
+}
+
+static uint64_t aspeed_ast1040_scu_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+
+ if (reg >= ASPEED_AST1040_SCU_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return 0;
+ }
+
+ trace_aspeed_ast1040_scu_read(offset, size, s->regs[reg]);
+ return s->regs[reg];
+}
+
+static void aspeed_ast1040_scu_write(void *opaque, hwaddr offset,
+ uint64_t data64, unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+ uint32_t data = data64;
+ bool updated = false;
+
+ if (reg >= ASPEED_AST1040_SCU_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return;
+ }
+
+ trace_aspeed_ast1040_scu_write(offset, size, data);
+
+ switch (reg) {
+ case AST1040_SCU_CLK_STOP_CTL_1:
+ case AST1040_SCU_CLK_STOP_CTL_2:
+ s->regs[reg] |= data;
+ updated = true;
+ break;
+ case AST1040_SCU_CLK_STOP_CLR_1:
+ case AST1040_SCU_CLK_STOP_CLR_2:
+ s->regs[reg - 1] &= ~data;
+ updated = true;
+ break;
+ case AST1040_SCU_FREQ_CNT_CTL:
+ s->regs[reg] = deposit32(s->regs[reg], 6, 1, !!(data & BIT(1)));
+ updated = true;
+ break;
+ case AST1040_SCU_CPTRA_PAGE_REG0:
+ s->regs[reg] = data;
+ aspeed_ast1040_scu_update_cptra_page(s);
+ updated = true;
+ break;
+ case AST1040_SCU_CPTRA_PAGE_REG1:
+ case AST1040_SCU_CPTRA_PAGE_REG2:
+ case AST1040_SCU_CPTRA_PAGE_REG3:
+ case AST1040_SCU_CPTRA_PAGE_REG4:
+ case AST1040_SCU_CPTRA_PAGE_REG5:
+ s->regs[reg] = data;
+ updated = true;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+
+ if (!updated) {
+ s->regs[reg] = data;
+ }
+}
+
+static const MemoryRegionOps aspeed_ast1040_scu_ops = {
+ .read = aspeed_ast1040_scu_read,
+ .write = aspeed_ast1040_scu_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 8,
+ .valid.unaligned = false,
+};
+
+static uint32_t aspeed_1040_scu_calc_hpll(AspeedSCUState *s, uint32_t hpll_reg)
+{
+ uint32_t multiplier = 1;
+ uint32_t clkin = aspeed_scu_get_clkin(s);
+
+ if (hpll_reg & SCU_AST2600_H_PLL_OFF) {
+ return 0;
+ }
+
+ if (!(hpll_reg & SCU_AST2600_H_PLL_BYPASS_EN)) {
+ uint32_t p = (hpll_reg >> 19) & 0xf;
+ uint32_t n = (hpll_reg >> 13) & 0x3f;
+ uint32_t m = hpll_reg & 0x1fff;
+
+ multiplier = ((m + 1) / (n + 1)) / (p + 1);
+ }
+
+ return clkin * multiplier;
+}
+
+static uint32_t aspeed_1040_scu_get_apb_freq(AspeedSCUState *s)
+{
+ AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s);
+ uint32_t hpll = asc->calc_hpll(s, s->regs[AST1040_SCU_HPLL_PARAM]);
+
+ return hpll /
+ (AST1040_SCU_CLK_GET_PCLK_DIV(s->regs[AST1040_SCU_CLK_SEL_1]) + 1)
+ / asc->apb_divider;
+}
+
+static void aspeed_ast1040_scu_reset_hold(Object *obj, ResetType type)
+{
+ AspeedSCUState *s = ASPEED_SCU(obj);
+ AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(obj);
+
+ memcpy(s->regs, asc->resets, asc->nr_regs * 4);
+ s->regs[AST1040_SILICON_REV] = s->silicon_rev;
+ s->regs[AST1040_HW_STRAP1] = s->hw_strap1;
+ aspeed_ast1040_scu_update_cptra_page(s);
+}
+
+static const uint32_t ast1040_a0_scu_resets[ASPEED_AST1040_SCU_NR_REGS] = {
+ [AST1040_HW_STRAP1_CLR] = 0xFFF0FFF0,
+ [AST1040_HW_STRAP1_LOCK] = 0x00000FFF,
+ [AST1040_HW_STRAP1_SEC1] = 0x000000FF,
+ [AST1040_SCU_CLK_STOP_CTL_1] = 0xffff8400,
+ [AST1040_SCU_CLK_STOP_CTL_2] = 0x00005f30,
+ [AST1040_SCU_CLK_SEL_1] = 0x86900000,
+ [AST1040_SCU_CLK_SEL_2] = 0x00400000,
+ [AST1040_SCU_HPLL_PARAM] = 0x10000027,
+ [AST1040_SCU_HPLL_EXT_PARAM] = 0x80000014,
+ [AST1040_SCU_APLL_PARAM] = 0x1000001f,
+ [AST1040_SCU_APLL_EXT_PARAM] = 0x8000000f,
+ [AST1040_SCU_DPLL_PARAM] = 0x106e42ce,
+ [AST1040_SCU_DPLL_EXT_PARAM] = 0x80000167,
+ [AST1040_SCU_DPLL_PARAM_READ] = 0x106e42ce,
+ [AST1040_SCU_DPLL_EXT_PARAM_READ] = 0x80000167,
+ [AST1040_SCU_UARTCLK_GEN] = 0x00014506,
+ [AST1040_SCU_HUARTCLK_GEN] = 0x000145c0,
+ [AST1040_SCU_CLK_DUTY_MEAS_RST] = 0x0c9100d2,
+ [AST1040_SCU_FREQ_CNT_CTL] = 0x00000080,
+};
+
+static const Property aspeed_1040_scu_props[] = {
+ DEFINE_PROP_UINT64("cptra-page-window-base", AspeedSCUState,
+ cptra_page_window_base, 0),
+};
+
+static void aspeed_1040_scu_init(Object *obj)
+{
+ AspeedSCUState *s = ASPEED_SCU(obj);
+
+ object_property_add_link(obj, "cptra-page-window", TYPE_MEMORY_REGION,
+ (Object **)&s->cptra_page_window,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static void aspeed_1040_scu_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass);
+
+ dc->desc = "ASPEED 1040 System Control Unit";
+ rc->phases.hold = aspeed_ast1040_scu_reset_hold;
+ device_class_set_props(dc, aspeed_1040_scu_props);
+ asc->resets = ast1040_a0_scu_resets;
+ asc->calc_hpll = aspeed_1040_scu_calc_hpll;
+ asc->get_apb = aspeed_1040_scu_get_apb_freq;
+ asc->apb_divider = 2;
+ asc->nr_regs = ASPEED_AST1040_SCU_NR_REGS;
+ asc->clkin_25Mhz = true;
+ asc->ops = &aspeed_ast1040_scu_ops;
+}
+
+static const TypeInfo aspeed_1040_scu_info = {
+ .name = TYPE_ASPEED_1040_SCU,
+ .parent = TYPE_ASPEED_SCU,
+ .instance_size = sizeof(AspeedSCUState),
+ .instance_init = aspeed_1040_scu_init,
+ .class_init = aspeed_1040_scu_class_init,
+};
+
static void aspeed_scu_register_types(void)
{
type_register_static(&aspeed_scu_info);
@@ -1174,6 +1424,7 @@ static void aspeed_scu_register_types(void)
type_register_static(&aspeed_1030_scu_info);
type_register_static(&aspeed_2700_scu_info);
type_register_static(&aspeed_2700_scuio_info);
+ type_register_static(&aspeed_1040_scu_info);
}
type_init(aspeed_scu_register_types);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b88accc437..82ddbf4d15 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -103,6 +103,8 @@ aspeed_ast2700_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%"
aspeed_ast2700_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
aspeed_ast2700_scuio_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
aspeed_ast2700_scuio_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
+aspeed_ast1040_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
+aspeed_ast1040_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
# mps2-scc.c
mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
--
2.43.0
© 2016 - 2026 Red Hat, Inc.