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 <[email protected]> --- 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
