Add four Octeon LMC register banks with the DDR training completion, leveling, MPR and clock responses used by firmware. Each LMC owns and resets its mutable register state.
Signed-off-by: Kirill A. Korinsky <[email protected]> --- hw/mips/octeon.c | 184 ++++++++++++++++++++++++++++++++++++++ hw/mips/octeon_internal.h | 3 + 2 files changed, 187 insertions(+) diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c index f3f8a74cbe..0553f8ea05 100644 --- a/hw/mips/octeon.c +++ b/hw/mips/octeon.c @@ -10,6 +10,7 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qemu/host-utils.h" +#include "qemu/timer.h" #include "qapi/error.h" #include "hw/char/serial-mm.h" #include "hw/core/clock.h" @@ -49,6 +50,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonEmmState, OCTEON_EMM) OBJECT_DECLARE_SIMPLE_TYPE(OcteonRstState, OCTEON_RST) #define TYPE_OCTEON_INTC "octeon-intc" OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC) +#define TYPE_OCTEON_LMC "octeon-lmc" +OBJECT_DECLARE_SIMPLE_TYPE(OcteonLmcState, OCTEON_LMC) #define TYPE_OCTEON_CSR_BANK "octeon-csr-bank" OBJECT_DECLARE_SIMPLE_TYPE(OcteonCsrBankState, OCTEON_CSR_BANK) @@ -243,6 +246,24 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonCsrBankState, OCTEON_CSR_BANK) #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_LMC_BASE 0x88000000 +#define OCTEON_LMC_SEQ_CTL 0x48 +#define OCTEON_LMC_SEQ_COMPLETE (1ULL << 5) +#define OCTEON_LMC_MPR_DATA0 0x70 +#define OCTEON_LMC_MPR_DATA1 0x78 +#define OCTEON_LMC_DCLK_CNT 0x1e0 +#define OCTEON_LMC_PHY_CTL 0x210 +#define OCTEON_LMC_PHY_DSK_COMPLETE (1ULL << 49) +#define OCTEON_LMC_RLEVEL_RANK 0x280 +#define OCTEON_LMC_RLEVEL_STATUS (3ULL << 54) +#define OCTEON_LMC_RLEVEL_DBG 0x2a8 +#define OCTEON_LMC_RLEVEL_MASK 0x3f0 +#define OCTEON_LMC_WLEVEL_RANK 0x2c0 +#define OCTEON_LMC_RANK_COUNT 4 +#define OCTEON_LMC_WLEVEL_STATUS (3ULL << 45) +#define OCTEON_LMC_WLEVEL_DBG 0x308 +#define OCTEON_LMC_WLEVEL_MASK (0x0fULL << 4) +#define OCTEON_LMC_STRIDE 0x1000000 #define OCTEON_RST_BOOT_C_MUL_SHIFT 30 #define OCTEON_RST_BOOT_PNR_MUL_SHIFT 24 @@ -351,6 +372,11 @@ struct OcteonIntcState { uint8_t ciu3_irq_line[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT]; }; +struct OcteonLmcState { + OcteonPeripheralState parent_obj; + GHashTable *regs; +}; + struct OcteonCsrBankState { OcteonPeripheralState parent_obj; MemoryRegion csr; @@ -416,6 +442,13 @@ static uint64_t octeon_atomic_write64(uint64_t *ptr, hwaddr addr, return new; } +static uint64_t octeon_clock_count(uint64_t hz) +{ + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + return muldiv64(now, hz, NANOSECONDS_PER_SECOND); +} + static void octeon_validate_clocks(OcteonMachineState *oms) { if (!oms->cpu_hz || !oms->ref_hz || !oms->io_hz || !oms->ddr_hz) { @@ -884,6 +917,94 @@ static void octeon_mio_emm_write(OcteonState *s, hwaddr reg, } } +static bool octeon_lmc_decode(hwaddr reg, OcteonLmcState **lmc, hwaddr *lreg, + OcteonState *s) +{ + hwaddr offset; + unsigned int index; + + if (reg < OCTEON_LMC_BASE) { + return false; + } + + offset = reg - OCTEON_LMC_BASE; + index = offset / OCTEON_LMC_STRIDE; + if (index >= OCTEON_LMC_COUNT) { + return false; + } + + *lmc = s->lmc[index]; + *lreg = offset % OCTEON_LMC_STRIDE; + return true; +} + +static uint64_t octeon_lmc_get(OcteonLmcState *lmc, hwaddr reg) +{ + uint64_t value; + + if (!octeon_reg_lookup(lmc->regs, reg, &value)) { + return 0; + } + return value; +} + +static bool octeon_lmc_rank_reg(hwaddr reg, hwaddr base) +{ + return reg >= base && + reg < base + OCTEON_LMC_RANK_COUNT * 8 && + ((reg - base) & 7) == 0; +} + +static uint64_t octeon_lmc_read(OcteonState *s, OcteonLmcState *lmc, + hwaddr reg, hwaddr addr, unsigned size) +{ + uint64_t value; + + switch (reg) { + case OCTEON_LMC_SEQ_CTL: + value = octeon_lmc_get(lmc, reg) | OCTEON_LMC_SEQ_COMPLETE; + break; + case OCTEON_LMC_MPR_DATA0: + value = ~0ULL; + break; + case OCTEON_LMC_MPR_DATA1: + value = 0xff; + break; + case OCTEON_LMC_DCLK_CNT: + value = octeon_clock_count(s->ddr_hz); + break; + case OCTEON_LMC_PHY_CTL: + value = octeon_lmc_get(lmc, reg) | OCTEON_LMC_PHY_DSK_COMPLETE; + break; + case OCTEON_LMC_RLEVEL_DBG: + value = OCTEON_LMC_RLEVEL_MASK; + break; + case OCTEON_LMC_WLEVEL_DBG: + value = OCTEON_LMC_WLEVEL_MASK; + break; + default: + if (octeon_lmc_rank_reg(reg, OCTEON_LMC_RLEVEL_RANK)) { + value = octeon_lmc_get(lmc, reg) | OCTEON_LMC_RLEVEL_STATUS; + } else if (octeon_lmc_rank_reg(reg, OCTEON_LMC_WLEVEL_RANK)) { + value = octeon_lmc_get(lmc, reg) | OCTEON_LMC_WLEVEL_STATUS; + } else { + value = octeon_lmc_get(lmc, reg); + } + break; + } + + return octeon_read64(value, addr, size); +} + +static void octeon_lmc_write(OcteonLmcState *lmc, hwaddr reg, + hwaddr addr, uint64_t value, unsigned size) +{ + uint64_t old = octeon_lmc_get(lmc, reg); + + value = octeon_write64(old, addr, value, size); + octeon_reg_store(lmc->regs, reg, value); +} + static bool octeon_ciu3_source_from_intsn(uint32_t intsn, unsigned int *source) { @@ -1688,7 +1809,9 @@ static const MemoryRegionOps octeon_ciu3_ops = { static uint64_t octeon_csr_read(void *opaque, hwaddr addr, unsigned size) { OcteonState *s = opaque; + OcteonLmcState *lmc; hwaddr reg = addr & ~7ULL; + hwaddr lreg; hwaddr ereg; uint64_t value; @@ -1726,6 +1849,10 @@ static uint64_t octeon_csr_read(void *opaque, hwaddr addr, unsigned size) break; } + if (octeon_lmc_decode(reg, &lmc, &lreg, s)) { + return octeon_lmc_read(s, lmc, lreg, addr, size); + } + if (octeon_mio_emm_decode(reg, &ereg)) { return octeon_mio_emm_read(s, ereg, addr, size); } @@ -1740,10 +1867,17 @@ static void octeon_csr_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { OcteonState *s = opaque; + OcteonLmcState *lmc; hwaddr reg = addr & ~7ULL; + hwaddr lreg; hwaddr ereg; uint64_t old; + if (octeon_lmc_decode(reg, &lmc, &lreg, s)) { + octeon_lmc_write(lmc, lreg, addr, value, size); + return; + } + if (octeon_mio_emm_decode(reg, &ereg)) { octeon_mio_emm_write(s, ereg, addr, value, size); return; @@ -2209,6 +2343,18 @@ static void octeon_intc_reset_hold(Object *obj, ResetType type) octeon_intc_update_all_cpus(s); } +static void octeon_lmc_reset_hold(Object *obj, ResetType type) +{ + OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_GET_CLASS(obj); + OcteonLmcState *lmc = OCTEON_LMC(obj); + + if (opc->parent_phases.hold) { + opc->parent_phases.hold(obj, type); + } + + g_hash_table_remove_all(lmc->regs); +} + static void octeon_csr_bank_reset_hold(Object *obj, ResetType type) { OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_GET_CLASS(obj); @@ -2256,6 +2402,16 @@ static void octeon_rst_finalize(Object *obj) g_hash_table_destroy(OCTEON_RST(obj)->regs); } +static void octeon_lmc_init(Object *obj) +{ + OCTEON_LMC(obj)->regs = octeon_reg_table_new(); +} + +static void octeon_lmc_finalize(Object *obj) +{ + g_hash_table_destroy(OCTEON_LMC(obj)->regs); +} + static void octeon_csr_bank_init(Object *obj) { OCTEON_CSR_BANK(obj)->values = octeon_reg_table_new(); @@ -2302,6 +2458,15 @@ static void octeon_intc_class_init(ObjectClass *klass, const void *data) NULL, &opc->parent_phases); } +static void octeon_lmc_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_lmc_reset_hold, + NULL, &opc->parent_phases); +} + static void octeon_csr_bank_class_init(ObjectClass *klass, const void *data) { OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_CLASS(klass); @@ -2326,10 +2491,16 @@ static OcteonPeripheralState *octeon_new_peripheral(OcteonState *s, static void octeon_create_peripherals(OcteonState *s) { + unsigned int i; + s->mio = OCTEON_MIO(octeon_new_peripheral(s, TYPE_OCTEON_MIO, 0)); s->emm = OCTEON_EMM(octeon_new_peripheral(s, TYPE_OCTEON_EMM, 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)); + for (i = 0; i < OCTEON_LMC_COUNT; i++) { + s->lmc[i] = OCTEON_LMC(octeon_new_peripheral(s, TYPE_OCTEON_LMC, + i)); + } s->csr_bank = OCTEON_CSR_BANK(octeon_new_peripheral( s, TYPE_OCTEON_CSR_BANK, 0)); } @@ -2341,7 +2512,12 @@ static void octeon_realize_peripheral(OcteonPeripheralState *dev) static void octeon_realize_peripherals(OcteonState *s) { + int i; + octeon_realize_peripheral(&s->csr_bank->parent_obj); + for (i = OCTEON_LMC_COUNT - 1; i >= 0; i--) { + octeon_realize_peripheral(&s->lmc[i]->parent_obj); + } octeon_realize_peripheral(&s->intc->parent_obj); octeon_realize_peripheral(&s->rst->parent_obj); octeon_realize_peripheral(&s->emm->parent_obj); @@ -2491,6 +2667,14 @@ static const TypeInfo octeon_machine_types[] = { .instance_size = sizeof(OcteonIntcState), .class_init = octeon_intc_class_init, }, + { + .name = TYPE_OCTEON_LMC, + .parent = TYPE_OCTEON_PERIPHERAL, + .instance_size = sizeof(OcteonLmcState), + .instance_init = octeon_lmc_init, + .instance_finalize = octeon_lmc_finalize, + .class_init = octeon_lmc_class_init, + }, { .name = TYPE_OCTEON_CSR_BANK, .parent = TYPE_OCTEON_PERIPHERAL, diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h index a262107b54..96443a9d64 100644 --- a/hw/mips/octeon_internal.h +++ b/hw/mips/octeon_internal.h @@ -21,6 +21,7 @@ #define OCTEON_MIO_BOOT_REG_CFG_COUNT 8 #define OCTEON_MIO_BOOT_LOC_SIZE 0x100 #define OCTEON_MIO_EMM_BUF_SIZE 512 +#define OCTEON_LMC_COUNT 4 #define OCTEON_TWSI_COUNT 2 #define OCTEON_TWSI_STAT_IDLE 0xf8 #define OCTEON_SPD_EEPROM_SIZE 139 @@ -42,6 +43,7 @@ typedef struct OcteonMioState OcteonMioState; typedef struct OcteonEmmState OcteonEmmState; typedef struct OcteonRstState OcteonRstState; typedef struct OcteonIntcState OcteonIntcState; +typedef struct OcteonLmcState OcteonLmcState; typedef struct OcteonCsrBankState OcteonCsrBankState; typedef struct OcteonCPUState { @@ -73,6 +75,7 @@ struct OcteonState { OcteonEmmState *emm; OcteonRstState *rst; OcteonIntcState *intc; + OcteonLmcState *lmc[OCTEON_LMC_COUNT]; OcteonCsrBankState *csr_bank; OcteonSpdEepromState *spd[OCTEON_SPD_EEPROM_COUNT]; SerialMM *uart; -- 2.56.0
