Model the legacy CIU mailbox, IPI, GPIO and UART summary registers. Mirror mailbox state into CIU3 interrupt sources so firmware using either controller observes consistent interrupt delivery.
Signed-off-by: Kirill A. Korinsky <[email protected]> --- hw/mips/octeon.c | 248 +++++++++++++++++++++++++++++++++++++- hw/mips/octeon_internal.h | 1 + 2 files changed, 248 insertions(+), 1 deletion(-) diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c index 567be0905a..638d96559f 100644 --- a/hw/mips/octeon.c +++ b/hw/mips/octeon.c @@ -69,6 +69,29 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC) #define OCTEON_DR1_BASE 0x020000000ULL #define OCTEON_DR1_SIZE (OCTEON_MAX_PHY_MEM_SIZE - OCTEON_DR0_SIZE) +#define OCTEON_CIU_BASE 0x1070000000000ULL +#define OCTEON_CIU_LEGACY_PAGE_SIZE (4 * KiB) +#define OCTEON_CIU_SIZE (1 * MiB + OCTEON_CIU_LEGACY_PAGE_SIZE) +#define OCTEON_CIU_FUSE 0x728 +#define OCTEON_CIU_SUM_IPI 0x0000000100000000ULL +#define OCTEON_CIU_SUM_UART 0x0000000800000000ULL +#define OCTEON_CIU_ENABLE0 0x88006f1800000000ULL +#define OCTEON_CIU_IPI_SUM_BASE 0x008 +#define OCTEON_CIU_IPI_SUM_STRIDE 0x10 +#define OCTEON_CIU_IPI_EN_BASE 0x210 +#define OCTEON_CIU_IPI_EN_STRIDE 0x20 +#define OCTEON_CIU_MBOX_SET_BASE 0x600 +#define OCTEON_CIU_MBOX_CLR_BASE 0x680 +#define OCTEON_CIU_MBOX_SET_STRIDE 8 +#define OCTEON_CIU_GPIO_RX_DAT 0x880 +#define OCTEON_CIU_GPIO_TX_SET 0x888 +#define OCTEON_CIU_GPIO_TX_CLR 0x890 +#define OCTEON_CIU_GPIO_BIT_CFG_STRIDE OCTEON_CSR_64BIT_SIZE +#define OCTEON_CIU_GPIO_BIT_CFGX(x) \ + (0x900 + (x) * OCTEON_CIU_GPIO_BIT_CFG_STRIDE) +#define OCTEON_CIU_GPIO_INPUTS ((1ULL << OCTEON_CIU_GPIO_COUNT) - 1) +#define OCTEON_CIU_GPIO_BIT_CFG_TX_OE (1ULL << 0) + #define OCTEON_CIU3_BASE 0x1010000000000ULL #define OCTEON_CIU3_SIZE 0xb0000000ULL #define OCTEON_CIU3_PP_RST 0x100 @@ -97,7 +120,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC) #define OCTEON_CIU3_ISC_SIZE (OCTEON_CIU3_NINTSN * 8) #define OCTEON_CIU3_MBOX_INTSN(cpu) ((cpu) + 0x4000U) #define OCTEON_CIU3_UART0_INTSN 0x8000 - /* * QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM; * the guest sees the virtual CVMSEG window, not this physical address. @@ -147,7 +169,14 @@ struct OcteonRstState { struct OcteonIntcState { OcteonPeripheralState parent_obj; + MemoryRegion ciu; MemoryRegion ciu3; + uint32_t ciu_mbox[OCTEON_MAX_CPUS]; + uint64_t ciu_ipi_en[OCTEON_MAX_CPUS]; + uint64_t ciu_gpio_rx; + uint64_t ciu_gpio_tx; + uint64_t ciu_gpio_bit_cfg[OCTEON_CIU_GPIO_COUNT]; + bool uart_pending; uint64_t ciu3_pp_rst; uint32_t ciu3_src_state[OCTEON_CIU3_SRC_COUNT]; uint64_t ciu3_src_ctl[OCTEON_CIU3_SRC_COUNT]; @@ -305,6 +334,12 @@ static void octeon_ciu3_clear_raw(OcteonState *s, unsigned int source) } while (old != cmp); } +static bool octeon_ciu3_set_mbox(OcteonState *s, unsigned int cpu) +{ + return octeon_ciu3_set_level(s, OCTEON_CIU3_SRC_MBOX0 + cpu, + qatomic_read(&s->intc->ciu_mbox[cpu]) != 0); +} + static bool octeon_ciu3_isc_decode(hwaddr reg, uint32_t base, uint32_t *intsn) { @@ -461,6 +496,12 @@ static void octeon_intc_update_cpu(OcteonState *s, unsigned int cpu_index) irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT; irq++) { level = false; + if (irq == 3) { + level = qatomic_read(&s->intc->ciu_mbox[cpu_index]) && + (qatomic_read(&s->intc->ciu_ipi_en[cpu_index]) & + OCTEON_CIU_SUM_IPI); + } + if (octeon_ciu3_pending_intsn(s, cpu_index, irq, &intsn)) { level = true; } @@ -527,6 +568,189 @@ static uint64_t octeon_ciu3_source_cpu_mask(OcteonState *s, unsigned int src) s, qatomic_read(&s->intc->ciu3_src_ctl[src])); } +static uint64_t octeon_ciu_gpio_rx_value(OcteonState *s) +{ + uint64_t value = 0; + unsigned int i; + + for (i = 0; i < OCTEON_CIU_GPIO_COUNT; i++) { + uint64_t mask = 1ULL << i; + + if (qatomic_read(&s->intc->ciu_gpio_bit_cfg[i]) & + OCTEON_CIU_GPIO_BIT_CFG_TX_OE) { + if (qatomic_read(&s->intc->ciu_gpio_tx) & mask) { + value |= mask; + } + } else if (qatomic_read(&s->intc->ciu_gpio_rx) & mask) { + value |= mask; + } + } + + return value; +} + +static uint64_t octeon_ciu_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s = opaque; + uint64_t value = 0; + hwaddr reg = addr & ~7ULL; + unsigned int cpu; + + if (reg == 0) { + if (qatomic_read(&s->intc->uart_pending)) { + value |= OCTEON_CIU_SUM_UART; + } + octeon_intc_request_cpu_update(s, 0); + return octeon_read64(value, addr, size); + } + + if (reg == OCTEON_CIU_IPI_EN_BASE) { + return octeon_read64(OCTEON_CIU_ENABLE0, addr, size); + } + + if (reg == OCTEON_CIU_FUSE) { + value = octeon_present_cpu_mask(s); + return octeon_read64(value, addr, size); + } + + if (reg == OCTEON_CIU_GPIO_RX_DAT) { + return octeon_read64(octeon_ciu_gpio_rx_value(s), addr, size); + } + + if (reg == OCTEON_CIU_GPIO_TX_SET || + reg == OCTEON_CIU_GPIO_TX_CLR) { + return octeon_read64(qatomic_read(&s->intc->ciu_gpio_tx), addr, size); + } + + if (reg >= OCTEON_CIU_GPIO_BIT_CFGX(0) && + reg < OCTEON_CIU_GPIO_BIT_CFGX(OCTEON_CIU_GPIO_COUNT)) { + unsigned int index = (reg - OCTEON_CIU_GPIO_BIT_CFGX(0)) / + OCTEON_CIU_GPIO_BIT_CFG_STRIDE; + + return octeon_read64(qatomic_read(&s->intc->ciu_gpio_bit_cfg[index]), + addr, size); + } + + if (reg >= OCTEON_CIU_IPI_SUM_BASE && + reg < OCTEON_CIU_IPI_SUM_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_SUM_STRIDE && + ((reg - OCTEON_CIU_IPI_SUM_BASE) & + (OCTEON_CIU_IPI_SUM_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_IPI_SUM_BASE) / OCTEON_CIU_IPI_SUM_STRIDE; + if (cpu < s->cpu_count && qatomic_read(&s->intc->ciu_mbox[cpu])) { + return octeon_read64(OCTEON_CIU_SUM_IPI, addr, size); + } + } + + if (reg >= OCTEON_CIU_IPI_EN_BASE && + reg < OCTEON_CIU_IPI_EN_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_EN_STRIDE && + ((reg - OCTEON_CIU_IPI_EN_BASE) & + (OCTEON_CIU_IPI_EN_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_IPI_EN_BASE) / OCTEON_CIU_IPI_EN_STRIDE; + if (cpu < s->cpu_count) { + return octeon_read64(qatomic_read(&s->intc->ciu_ipi_en[cpu]), + addr, size); + } + } + + if (reg >= OCTEON_CIU_MBOX_CLR_BASE && + reg < OCTEON_CIU_MBOX_CLR_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_CLR_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_MBOX_CLR_BASE) / OCTEON_CIU_MBOX_SET_STRIDE; + if (cpu < s->cpu_count) { + return octeon_read64(qatomic_read(&s->intc->ciu_mbox[cpu]), + addr, size); + } + } + + return 0; +} + +static void octeon_ciu_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s = opaque; + hwaddr reg = addr & ~7ULL; + uint64_t old; + unsigned int cpu; + + if (reg >= OCTEON_CIU_IPI_EN_BASE && + reg < OCTEON_CIU_IPI_EN_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_EN_STRIDE && + ((reg - OCTEON_CIU_IPI_EN_BASE) & + (OCTEON_CIU_IPI_EN_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_IPI_EN_BASE) / OCTEON_CIU_IPI_EN_STRIDE; + if (cpu < s->cpu_count) { + value = octeon_atomic_write64(&s->intc->ciu_ipi_en[cpu], addr, + value, size, UINT64_MAX, 0, &old); + if (old != value) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg >= OCTEON_CIU_MBOX_SET_BASE && + reg < OCTEON_CIU_MBOX_SET_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_SET_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_MBOX_SET_BASE) / OCTEON_CIU_MBOX_SET_STRIDE; + if (cpu < s->cpu_count) { + old = qatomic_fetch_or(&s->intc->ciu_mbox[cpu], value & UINT32_MAX); + if ((old | (value & UINT32_MAX)) != old && + octeon_ciu3_set_mbox(s, cpu)) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg >= OCTEON_CIU_MBOX_CLR_BASE && + reg < OCTEON_CIU_MBOX_CLR_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_CLR_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) == 0) { + cpu = (reg - OCTEON_CIU_MBOX_CLR_BASE) / OCTEON_CIU_MBOX_SET_STRIDE; + if (cpu < s->cpu_count) { + old = qatomic_fetch_and(&s->intc->ciu_mbox[cpu], + ~(value & UINT32_MAX)); + if ((old & (value & UINT32_MAX)) && + octeon_ciu3_set_mbox(s, cpu)) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg == OCTEON_CIU_GPIO_TX_SET) { + value = octeon_write64(0, addr, value, size) & + OCTEON_CIU_GPIO_INPUTS; + qatomic_or(&s->intc->ciu_gpio_tx, value); + return; + } + + if (reg == OCTEON_CIU_GPIO_TX_CLR) { + value = octeon_write64(0, addr, value, size) & + OCTEON_CIU_GPIO_INPUTS; + qatomic_and(&s->intc->ciu_gpio_tx, ~value); + return; + } + + if (reg >= OCTEON_CIU_GPIO_BIT_CFGX(0) && + reg < OCTEON_CIU_GPIO_BIT_CFGX(OCTEON_CIU_GPIO_COUNT)) { + unsigned int index = (reg - OCTEON_CIU_GPIO_BIT_CFGX(0)) / + OCTEON_CIU_GPIO_BIT_CFG_STRIDE; + + octeon_atomic_write64(&s->intc->ciu_gpio_bit_cfg[index], addr, value, + size, UINT64_MAX, 0, NULL); + return; + } +} + static void octeon_cpu_park_now(MIPSCPU *cpu) { CPUMIPSState *env = &cpu->env; @@ -591,6 +815,16 @@ static void octeon_ciu3_nmi(OcteonState *s, uint64_t mask) } } +static const MemoryRegionOps octeon_ciu_ops = { + .read = octeon_ciu_read, + .write = octeon_ciu_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + static uint64_t octeon_ciu3_read(void *opaque, hwaddr addr, unsigned size) { OcteonState *s = opaque; @@ -799,6 +1033,7 @@ void octeon_irq_set(void *opaque, int irq, int level) if (!octeon_ciu3_set_level(s, src, level)) { return; } + qatomic_set(&s->intc->uart_pending, level); break; default: return; @@ -1042,6 +1277,12 @@ static void octeon_intc_reset_hold(Object *obj, ResetType type) opc->parent_phases.hold(obj, type); } + memset(intc->ciu_mbox, 0, sizeof(intc->ciu_mbox)); + memset(intc->ciu_ipi_en, 0, sizeof(intc->ciu_ipi_en)); + qatomic_set(&intc->ciu_gpio_rx, OCTEON_CIU_GPIO_INPUTS); + qatomic_set(&intc->ciu_gpio_tx, 0); + memset(intc->ciu_gpio_bit_cfg, 0, sizeof(intc->ciu_gpio_bit_cfg)); + qatomic_set(&intc->uart_pending, false); qatomic_set(&intc->ciu3_pp_rst, octeon_secondary_cpu_mask(s)); memset(intc->ciu3_src_state, 0, sizeof(intc->ciu3_src_state)); memset(intc->ciu3_src_ctl, 0, sizeof(intc->ciu3_src_ctl)); @@ -1134,6 +1375,11 @@ static void mips_octeon_init(MachineState *machine) octeon_load_firmware(s); octeon_create_cpus(s); + memory_region_init_io(&s->intc->ciu, OBJECT(s->intc), &octeon_ciu_ops, s, + "octeon.ciu", OCTEON_CIU_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_CIU_BASE, + &s->intc->ciu); + memory_region_init_io(&s->intc->ciu3, OBJECT(s->intc), &octeon_ciu3_ops, s, "octeon.ciu3", OCTEON_CIU3_SIZE); diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h index fef88419b5..cd810f3dd5 100644 --- a/hw/mips/octeon_internal.h +++ b/hw/mips/octeon_internal.h @@ -14,6 +14,7 @@ #include "target/mips/cpu.h" #define OCTEON_MAX_CPUS 16 +#define OCTEON_CIU_GPIO_COUNT 32 #define OCTEON_CIU3_CP0_IRQ_COUNT 4 #define OCTEON_CIU3_IDT_COUNT (OCTEON_MAX_CPUS * 4) -- 2.56.0
