Add MIO boot bus configuration, boot location storage and fuse responses. Keep mutable state and memory mappings in a resettable MIO device.
Signed-off-by: Kirill A. Korinsky <[email protected]> --- 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.56.0
