Add MIO boot bus configuration, boot location storage and fuse
responses. Keep mutable state and memory mappings in a resettable MIO
device.
---
hw/mips/octeon.c | 248 +++++++++++++++++++++++++++++++++++++-
hw/mips/octeon_internal.h | 4 +
2 files changed, 250 insertions(+), 2 deletions(-)
diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c
index 007fc4b537..59754cb741 100644
--- a/hw/mips/octeon.c
+++ b/hw/mips/octeon.c
@@ -39,6 +39,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonMachineState, OCTEON_MACHINE)
OBJECT_DECLARE_TYPE(OcteonPeripheralState, OcteonPeripheralClass,
OCTEON_PERIPHERAL)
+#define TYPE_OCTEON_MIO "octeon-mio"
+OBJECT_DECLARE_SIMPLE_TYPE(OcteonMioState, OCTEON_MIO)
#define TYPE_OCTEON_RST "octeon-rst"
OBJECT_DECLARE_SIMPLE_TYPE(OcteonRstState, OCTEON_RST)
#define TYPE_OCTEON_INTC "octeon-intc"
@@ -126,12 +128,33 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonCsrBankState,
OCTEON_CSR_BANK)
#define OCTEON_CIU3_UART0_INTSN 0x8000
#define OCTEON_CSR_BASE 0x1180000000000ULL
#define OCTEON_CSR_SIZE 0x100000000ULL
+#define OCTEON_MIO_BOOT_REG_CFGX(x) \
+ (0x0000000 + (x) * OCTEON_CSR_64BIT_SIZE)
+#define OCTEON_MIO_BOOT_REG_CFG_INDEX(reg) \
+ ((reg) / OCTEON_CSR_64BIT_SIZE)
+#define OCTEON_MIO_BOOT_REG_CFG_BASE 0x000000000000ffffULL
+#define OCTEON_MIO_BOOT_LOC_CFGX(x) \
+ (0x0000080 + ((x) & 1) * OCTEON_CSR_64BIT_SIZE)
+#define OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg) \
+ (((reg) - OCTEON_MIO_BOOT_LOC_CFGX(0)) / OCTEON_CSR_64BIT_SIZE)
+#define OCTEON_MIO_BOOT_LOC_ADR 0x0000090
+#define OCTEON_MIO_BOOT_LOC_DAT 0x0000098
+#define OCTEON_MIO_BOOT_LOC_CFG_BASE 0x000000000ffffff8ULL
+#define OCTEON_MIO_BOOT_LOC_CFG_BASE_SHIFT 3
+#define OCTEON_MIO_BOOT_LOC_CFG_EN 0x0000000080000000ULL
+#define OCTEON_MIO_BOOT_LOC_ADR_MASK 0xf8
+/* Fuses are left unprogrammed; firmware only needs reads to complete. */
+#define OCTEON_MIO_FUS_DAT2 0x0001410
+#define OCTEON_MIO_FUS_RCMD 0x0001500
+#define OCTEON_MIO_FUS_RCMD_PEND 0x0000000000001000ULL
+#define OCTEON_MIO_FUS_RCMD_DAT 0x0000000000ff0000ULL
#define OCTEON_RST_BOOT 0x6001600
#define OCTEON_RST_SOFT_RST 0x6001680
/* U-Boot uses the reset controller to park and release secondary cores. */
#define OCTEON_RST_PP_POWER 0x6001700
#define OCTEON_RST_BOOT_C_MUL_SHIFT 30
#define OCTEON_RST_BOOT_PNR_MUL_SHIFT 24
+
/*
* QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM;
* the guest sees the virtual CVMSEG window, not this physical address.
@@ -153,6 +176,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonCsrBankState,
OCTEON_CSR_BANK)
#define OCTEON_FLASH_ENV_OFFSET \
(OCTEON_FLASH_SIZE - OCTEON_FLASH_ENV_SECTOR_SIZE)
#define OCTEON_FLASH_WIDTH 2
+#define OCTEON_MIO_BOOT_REG_CFG_RESET_BASE (OCTEON_FLASH_RESET_BASE >> 16)
+#define OCTEON_BOOTBUS_LED_CS 4
+#define OCTEON_BOOTBUS_LED_SIZE (64 * KiB)
struct OcteonMachineState {
MachineState parent_obj;
@@ -174,6 +200,21 @@ struct OcteonPeripheralClass {
ResettablePhases parent_phases;
};
+struct OcteonMioState {
+ OcteonPeripheralState parent_obj;
+ MemoryRegion bootbus_led;
+ MemoryRegion mio_boot_loc;
+ uint64_t mio_boot_reg_cfg[OCTEON_MIO_BOOT_REG_CFG_COUNT];
+ uint64_t mio_boot_loc_cfg[2];
+ uint64_t mio_boot_loc_adr;
+ uint64_t mio_fus_rcmd;
+ hwaddr mio_boot_loc_base;
+ uint8_t mio_boot_loc_data[OCTEON_MIO_BOOT_LOC_SIZE];
+ bool mio_boot_loc_mapped;
+ hwaddr bootbus_led_base;
+ bool bootbus_led_mapped;
+};
+
struct OcteonRstState {
OcteonPeripheralState parent_obj;
GHashTable *regs;
@@ -318,6 +359,89 @@ static void octeon_csr_store(OcteonState *s, uint64_t reg,
uint64_t value)
octeon_reg_store(s->csr_bank->values, reg, value);
}
+static hwaddr octeon_mio_boot_loc_base(uint64_t cfg)
+{
+ uint64_t base = (cfg & OCTEON_MIO_BOOT_LOC_CFG_BASE) >>
+ OCTEON_MIO_BOOT_LOC_CFG_BASE_SHIFT;
+
+ return base << 7;
+}
+
+static void octeon_mio_boot_loc_update(OcteonState *s)
+{
+ MemoryRegion *sysmem = get_system_memory();
+ bool enabled = s->mio->mio_boot_loc_cfg[0] & OCTEON_MIO_BOOT_LOC_CFG_EN;
+ hwaddr base = octeon_mio_boot_loc_base(s->mio->mio_boot_loc_cfg[0]);
+
+ if (s->mio->mio_boot_loc_mapped) {
+ if (enabled && base == s->mio->mio_boot_loc_base) {
+ return;
+ }
+ memory_region_del_subregion(sysmem, &s->mio->mio_boot_loc);
+ s->mio->mio_boot_loc_mapped = false;
+ }
+
+ if (enabled) {
+ memory_region_add_subregion_overlap(sysmem, base,
+ &s->mio->mio_boot_loc, 1);
+ s->mio->mio_boot_loc_base = base;
+ s->mio->mio_boot_loc_mapped = true;
+ }
+}
+
+static hwaddr octeon_mio_boot_reg_cfg_base(uint64_t cfg)
+{
+ return (cfg & OCTEON_MIO_BOOT_REG_CFG_BASE) << 16;
+}
+
+static void octeon_bootbus_led_update(OcteonState *s)
+{
+ MemoryRegion *sysmem = get_system_memory();
+ hwaddr base = octeon_mio_boot_reg_cfg_base(
+ s->mio->mio_boot_reg_cfg[OCTEON_BOOTBUS_LED_CS]);
+
+ if (s->mio->bootbus_led_mapped) {
+ if (base == s->mio->bootbus_led_base) {
+ return;
+ }
+ memory_region_del_subregion(sysmem, &s->mio->bootbus_led);
+ s->mio->bootbus_led_mapped = false;
+ }
+
+ if (base) {
+ memory_region_add_subregion(sysmem, base, &s->mio->bootbus_led);
+ s->mio->bootbus_led_base = base;
+ s->mio->bootbus_led_mapped = true;
+ }
+}
+
+static uint64_t octeon_mio_boot_loc_dat_read(OcteonState *s)
+{
+ unsigned int offset = s->mio->mio_boot_loc_adr &
+ OCTEON_MIO_BOOT_LOC_ADR_MASK;
+ uint64_t value = ldq_be_p(s->mio->mio_boot_loc_data + offset);
+
+ s->mio->mio_boot_loc_adr =
+ (s->mio->mio_boot_loc_adr + OCTEON_CSR_64BIT_SIZE) &
+ OCTEON_MIO_BOOT_LOC_ADR_MASK;
+ return value;
+}
+
+static void octeon_mio_boot_loc_dat_write(OcteonState *s, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ unsigned int offset = s->mio->mio_boot_loc_adr &
+ OCTEON_MIO_BOOT_LOC_ADR_MASK;
+ uint64_t old = ldq_be_p(s->mio->mio_boot_loc_data + offset);
+
+ value = octeon_write64(old, addr, value, size);
+ stq_be_p(s->mio->mio_boot_loc_data + offset, value);
+ memory_region_set_dirty(&s->mio->mio_boot_loc, offset, sizeof(value));
+ s->mio->mio_boot_loc_adr =
+ (s->mio->mio_boot_loc_adr + OCTEON_CSR_64BIT_SIZE) &
+ OCTEON_MIO_BOOT_LOC_ADR_MASK;
+}
+
static bool octeon_ciu3_source_from_intsn(uint32_t intsn,
unsigned int *source)
{
@@ -1125,7 +1249,28 @@ static uint64_t octeon_csr_read(void *opaque, hwaddr
addr, unsigned size)
hwaddr reg = addr & ~7ULL;
uint64_t value;
+ if (reg >= OCTEON_MIO_BOOT_REG_CFGX(0) &&
+ reg < OCTEON_MIO_BOOT_REG_CFGX(OCTEON_MIO_BOOT_REG_CFG_COUNT)) {
+ unsigned int index = OCTEON_MIO_BOOT_REG_CFG_INDEX(reg);
+
+ return octeon_read64(s->mio->mio_boot_reg_cfg[index], addr, size);
+ }
+
switch (reg) {
+ case OCTEON_MIO_BOOT_LOC_CFGX(0):
+ case OCTEON_MIO_BOOT_LOC_CFGX(1): {
+ unsigned int index = OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg);
+
+ return octeon_read64(s->mio->mio_boot_loc_cfg[index], addr, size);
+ }
+ case OCTEON_MIO_BOOT_LOC_ADR:
+ return octeon_read64(s->mio->mio_boot_loc_adr, addr, size);
+ case OCTEON_MIO_BOOT_LOC_DAT:
+ return octeon_read64(octeon_mio_boot_loc_dat_read(s), addr, size);
+ case OCTEON_MIO_FUS_DAT2:
+ return 0;
+ case OCTEON_MIO_FUS_RCMD:
+ return octeon_read64(s->mio->mio_fus_rcmd, addr, size);
case OCTEON_RST_BOOT:
value = ((uint64_t)(s->cpu_hz / s->ref_hz) <<
OCTEON_RST_BOOT_C_MUL_SHIFT) |
@@ -1156,11 +1301,56 @@ static void octeon_csr_write(void *opaque, hwaddr addr,
return;
}
+ if (reg == OCTEON_MIO_BOOT_LOC_CFGX(0) ||
+ reg == OCTEON_MIO_BOOT_LOC_CFGX(1)) {
+ unsigned int index = OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg);
+
+ old = s->mio->mio_boot_loc_cfg[index];
+ s->mio->mio_boot_loc_cfg[index] =
+ octeon_write64(old, addr, value, size);
+ if (index == 0) {
+ octeon_mio_boot_loc_update(s);
+ }
+ return;
+ }
+
+ if (reg == OCTEON_MIO_BOOT_LOC_ADR) {
+ old = s->mio->mio_boot_loc_adr;
+ value = octeon_write64(old, addr, value, size);
+ s->mio->mio_boot_loc_adr = value & OCTEON_MIO_BOOT_LOC_ADR_MASK;
+ return;
+ }
+
+ if (reg == OCTEON_MIO_BOOT_LOC_DAT) {
+ octeon_mio_boot_loc_dat_write(s, addr, value, size);
+ return;
+ }
+
+ if (reg == OCTEON_MIO_FUS_RCMD) {
+ value = octeon_write64(s->mio->mio_fus_rcmd, addr, value, size);
+ s->mio->mio_fus_rcmd = value & ~(OCTEON_MIO_FUS_RCMD_PEND |
+ OCTEON_MIO_FUS_RCMD_DAT);
+ return;
+ }
+
if (reg == OCTEON_RST_PP_POWER) {
octeon_rst_pp_power_write(s, addr, value, size);
return;
}
+ if (reg >= OCTEON_MIO_BOOT_REG_CFGX(0) &&
+ reg < OCTEON_MIO_BOOT_REG_CFGX(OCTEON_MIO_BOOT_REG_CFG_COUNT)) {
+ unsigned int index = OCTEON_MIO_BOOT_REG_CFG_INDEX(reg);
+
+ old = s->mio->mio_boot_reg_cfg[index];
+ s->mio->mio_boot_reg_cfg[index] =
+ octeon_write64(old, addr, value, size);
+ if (index == OCTEON_BOOTBUS_LED_CS) {
+ octeon_bootbus_led_update(s);
+ }
+ return;
+ }
+
if (!octeon_csr_lookup(s, reg, &old)) {
old = 0;
}
@@ -1411,6 +1601,42 @@ static void octeon_init_flash(OcteonState *s)
&s->boot_flash_alias);
}
+static void octeon_init_mio_boot_loc(OcteonState *s)
+{
+ memory_region_init_ram(&s->mio->bootbus_led, OBJECT(s->mio),
+ "octeon.bootbus-led",
+ OCTEON_BOOTBUS_LED_SIZE, &error_fatal);
+ memory_region_init_ram_ptr(&s->mio->mio_boot_loc, OBJECT(s->mio),
+ "octeon.mio-boot-loc",
+ OCTEON_MIO_BOOT_LOC_SIZE,
+ s->mio->mio_boot_loc_data);
+}
+
+
+static void octeon_mio_reset_hold(Object *obj, ResetType type)
+{
+ OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_GET_CLASS(obj);
+ OcteonMioState *mio = OCTEON_MIO(obj);
+ OcteonState *s = mio->parent_obj.board;
+
+ if (opc->parent_phases.hold) {
+ opc->parent_phases.hold(obj, type);
+ }
+
+ if (mio->bootbus_led_mapped) {
+ memory_region_del_subregion(get_system_memory(), &mio->bootbus_led);
+ mio->bootbus_led_mapped = false;
+ }
+ memset(mio->mio_boot_reg_cfg, 0, sizeof(mio->mio_boot_reg_cfg));
+ mio->mio_boot_reg_cfg[0] = OCTEON_MIO_BOOT_REG_CFG_RESET_BASE;
+ mio->mio_boot_loc_cfg[0] = 0;
+ mio->mio_boot_loc_cfg[1] = 0;
+ mio->mio_boot_loc_adr = 0;
+ mio->mio_fus_rcmd = 0;
+ memset(mio->mio_boot_loc_data, 0, sizeof(mio->mio_boot_loc_data));
+ octeon_mio_boot_loc_update(s);
+}
+
static void octeon_rst_reset_hold(Object *obj, ResetType type)
{
OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_GET_CLASS(obj);
@@ -1498,6 +1724,15 @@ static void octeon_csr_bank_finalize(Object *obj)
g_hash_table_destroy(OCTEON_CSR_BANK(obj)->values);
}
+static void octeon_mio_class_init(ObjectClass *klass, const void *data)
+{
+ OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ resettable_class_set_parent_phases(rc, NULL, octeon_mio_reset_hold,
+ NULL, &opc->parent_phases);
+}
+
static void octeon_rst_class_init(ObjectClass *klass, const void *data)
{
OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_CLASS(klass);
@@ -1540,10 +1775,11 @@ static OcteonPeripheralState
*octeon_new_peripheral(OcteonState *s,
static void octeon_create_peripherals(OcteonState *s)
{
+ s->mio = OCTEON_MIO(octeon_new_peripheral(s, TYPE_OCTEON_MIO, 0));
s->rst = OCTEON_RST(octeon_new_peripheral(s, TYPE_OCTEON_RST, 0));
s->intc = OCTEON_INTC(octeon_new_peripheral(s, TYPE_OCTEON_INTC, 0));
s->csr_bank = OCTEON_CSR_BANK(octeon_new_peripheral(
- s, TYPE_OCTEON_CSR_BANK, 0));
+ s, TYPE_OCTEON_CSR_BANK, 0));
}
static void octeon_realize_peripheral(OcteonPeripheralState *dev)
@@ -1553,9 +1789,10 @@ static void
octeon_realize_peripheral(OcteonPeripheralState *dev)
static void octeon_realize_peripherals(OcteonState *s)
{
+ octeon_realize_peripheral(&s->csr_bank->parent_obj);
octeon_realize_peripheral(&s->intc->parent_obj);
octeon_realize_peripheral(&s->rst->parent_obj);
- octeon_realize_peripheral(&s->csr_bank->parent_obj);
+ octeon_realize_peripheral(&s->mio->parent_obj);
}
static void mips_octeon_init(MachineState *machine)
@@ -1581,6 +1818,7 @@ static void mips_octeon_init(MachineState *machine)
octeon_map_ram(s);
octeon_init_flash(s);
+ octeon_init_mio_boot_loc(s);
octeon_load_firmware(s);
octeon_create_cpus(s);
@@ -1667,6 +1905,12 @@ static const TypeInfo octeon_machine_types[] = {
.class_init = octeon_peripheral_class_init,
.abstract = true,
},
+ {
+ .name = TYPE_OCTEON_MIO,
+ .parent = TYPE_OCTEON_PERIPHERAL,
+ .instance_size = sizeof(OcteonMioState),
+ .class_init = octeon_mio_class_init,
+ },
{
.name = TYPE_OCTEON_RST,
.parent = TYPE_OCTEON_PERIPHERAL,
diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h
index 6327103f52..7b45daba10 100644
--- a/hw/mips/octeon_internal.h
+++ b/hw/mips/octeon_internal.h
@@ -17,6 +17,8 @@
#define OCTEON_CIU_GPIO_COUNT 32
#define OCTEON_CIU3_CP0_IRQ_COUNT 4
#define OCTEON_CIU3_IDT_COUNT (OCTEON_MAX_CPUS * 4)
+#define OCTEON_MIO_BOOT_REG_CFG_COUNT 8
+#define OCTEON_MIO_BOOT_LOC_SIZE 0x100
typedef enum OcteonIRQ {
OCTEON_IRQ_UART,
@@ -30,6 +32,7 @@ typedef enum OcteonCiu3Source {
} OcteonCiu3Source;
typedef struct OcteonState OcteonState;
+typedef struct OcteonMioState OcteonMioState;
typedef struct OcteonRstState OcteonRstState;
typedef struct OcteonIntcState OcteonIntcState;
typedef struct OcteonCsrBankState OcteonCsrBankState;
@@ -57,6 +60,7 @@ struct OcteonState {
MemoryRegion boot_flash;
MemoryRegion boot_flash_alias;
MemoryRegion cvmseg;
+ OcteonMioState *mio;
OcteonRstState *rst;
OcteonIntcState *intc;
OcteonCsrBankState *csr_bank;
--
2.54.0