diff options
author | Cédric Le Goater <clg@kaod.org> | 2019-03-07 23:35:35 +0100 |
---|---|---|
committer | David Gibson <david@gibson.dropbear.id.au> | 2019-03-12 14:33:04 +1100 |
commit | c38536bc80fa0b8f44db50a3190bfd241143ab0b (patch) | |
tree | 1369abea0039deedf3d24704eba28f5b02e0d933 /hw/ppc | |
parent | ae85605531cde2856d0b37804dc9a32028d89a34 (diff) | |
download | qemu-c38536bc80fa0b8f44db50a3190bfd241143ab0b.zip qemu-c38536bc80fa0b8f44db50a3190bfd241143ab0b.tar.gz qemu-c38536bc80fa0b8f44db50a3190bfd241143ab0b.tar.bz2 |
ppc/pnv: add a PSI bridge model for POWER9
The PSI bridge on POWER9 is very similar to POWER8. The BAR is still
set through XSCOM but the controls are now entirely done with MMIOs.
More interrupts are defined and the interrupt controller interface has
changed to XIVE. The POWER9 model is a first example of the usage of
the notify() handler of the XiveNotifier interface, linking the PSI
XiveSource to its owning device model.
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20190307223548.20516-3-clg@kaod.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'hw/ppc')
-rw-r--r-- | hw/ppc/pnv.c | 18 | ||||
-rw-r--r-- | hw/ppc/pnv_psi.c | 329 |
2 files changed, 345 insertions, 2 deletions
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5bb2332..1cc454c 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -579,6 +579,7 @@ static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) Pnv9Chip *chip9 = PNV9_CHIP(chip); pnv_xive_pic_print_info(&chip9->xive, mon); + pnv_psi_pic_print_info(&chip9->psi, mon); } static void pnv_init(MachineState *machine) @@ -950,6 +951,11 @@ static void pnv_chip_power9_instance_init(Object *obj) TYPE_PNV_XIVE, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip9->xive), "chip", obj, &error_abort); + + object_initialize_child(obj, "psi", &chip9->psi, sizeof(chip9->psi), + TYPE_PNV9_PSI, &error_abort, NULL); + object_property_add_const_link(OBJECT(&chip9->psi), "chip", obj, + &error_abort); } static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) @@ -957,6 +963,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); Pnv9Chip *chip9 = PNV9_CHIP(dev); PnvChip *chip = PNV_CHIP(dev); + Pnv9Psi *psi9 = &chip9->psi; Error *local_err = NULL; pcc->parent_realize(dev, &local_err); @@ -982,6 +989,17 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) } pnv_xscom_add_subregion(chip, PNV9_XSCOM_XIVE_BASE, &chip9->xive.xscom_regs); + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip9->psi), PNV9_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip9->psi), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_PSIHB_BASE, + &PNV_PSI(psi9)->xscom_regs); } static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 067f733..5a923e4 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -22,6 +22,7 @@ #include "target/ppc/cpu.h" #include "qemu/log.h" #include "qapi/error.h" +#include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -114,6 +115,9 @@ #define PSIHB_BAR_MASK 0x0003fffffff00000ull #define PSIHB_FSPBAR_MASK 0x0003ffff00000000ull +#define PSIHB9_BAR_MASK 0x00fffffffff00000ull +#define PSIHB9_FSPBAR_MASK 0x00ffffff00000000ull + #define PSIHB_REG(addr) (((addr) >> 3) + PSIHB_XSCOM_BAR) static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) @@ -531,6 +535,7 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) } static const char compat_p8[] = "ibm,power8-psihb-x\0ibm,psihb-x"; +static const char compat_p9[] = "ibm,power9-psihb-x\0ibm,psihb-x"; static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) { @@ -550,8 +555,13 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) _FDT(fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))); _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", 2)); _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", 1)); - _FDT(fdt_setprop(fdt, offset, "compatible", compat_p8, - sizeof(compat_p8))); + if (ppc->chip_type == PNV_CHIP_POWER9) { + _FDT(fdt_setprop(fdt, offset, "compatible", compat_p9, + sizeof(compat_p9))); + } else { + _FDT(fdt_setprop(fdt, offset, "compatible", compat_p8, + sizeof(compat_p8))); + } return 0; } @@ -584,6 +594,308 @@ static const TypeInfo pnv_psi_power8_info = { .class_init = pnv_psi_power8_class_init, }; + +/* Common registers */ + +#define PSIHB9_CR 0x20 +#define PSIHB9_SEMR 0x28 + +/* P9 registers */ + +#define PSIHB9_INTERRUPT_CONTROL 0x58 +#define PSIHB9_IRQ_METHOD PPC_BIT(0) +#define PSIHB9_IRQ_RESET PPC_BIT(1) +#define PSIHB9_ESB_CI_BASE 0x60 +#define PSIHB9_ESB_CI_VALID 1 +#define PSIHB9_ESB_NOTIF_ADDR 0x68 +#define PSIHB9_ESB_NOTIF_VALID 1 +#define PSIHB9_IVT_OFFSET 0x70 +#define PSIHB9_IVT_OFF_SHIFT 32 + +#define PSIHB9_IRQ_LEVEL 0x78 /* assertion */ +#define PSIHB9_IRQ_LEVEL_PSI PPC_BIT(0) +#define PSIHB9_IRQ_LEVEL_OCC PPC_BIT(1) +#define PSIHB9_IRQ_LEVEL_FSI PPC_BIT(2) +#define PSIHB9_IRQ_LEVEL_LPCHC PPC_BIT(3) +#define PSIHB9_IRQ_LEVEL_LOCAL_ERR PPC_BIT(4) +#define PSIHB9_IRQ_LEVEL_GLOBAL_ERR PPC_BIT(5) +#define PSIHB9_IRQ_LEVEL_TPM PPC_BIT(6) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ1 PPC_BIT(7) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ2 PPC_BIT(8) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ3 PPC_BIT(9) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ4 PPC_BIT(10) +#define PSIHB9_IRQ_LEVEL_SBE_I2C PPC_BIT(11) +#define PSIHB9_IRQ_LEVEL_DIO PPC_BIT(12) +#define PSIHB9_IRQ_LEVEL_PSU PPC_BIT(13) +#define PSIHB9_IRQ_LEVEL_I2C_C PPC_BIT(14) +#define PSIHB9_IRQ_LEVEL_I2C_D PPC_BIT(15) +#define PSIHB9_IRQ_LEVEL_I2C_E PPC_BIT(16) +#define PSIHB9_IRQ_LEVEL_SBE PPC_BIT(19) + +#define PSIHB9_IRQ_STAT 0x80 /* P bit */ +#define PSIHB9_IRQ_STAT_PSI PPC_BIT(0) +#define PSIHB9_IRQ_STAT_OCC PPC_BIT(1) +#define PSIHB9_IRQ_STAT_FSI PPC_BIT(2) +#define PSIHB9_IRQ_STAT_LPCHC PPC_BIT(3) +#define PSIHB9_IRQ_STAT_LOCAL_ERR PPC_BIT(4) +#define PSIHB9_IRQ_STAT_GLOBAL_ERR PPC_BIT(5) +#define PSIHB9_IRQ_STAT_TPM PPC_BIT(6) +#define PSIHB9_IRQ_STAT_LPC_SIRQ1 PPC_BIT(7) +#define PSIHB9_IRQ_STAT_LPC_SIRQ2 PPC_BIT(8) +#define PSIHB9_IRQ_STAT_LPC_SIRQ3 PPC_BIT(9) +#define PSIHB9_IRQ_STAT_LPC_SIRQ4 PPC_BIT(10) +#define PSIHB9_IRQ_STAT_SBE_I2C PPC_BIT(11) +#define PSIHB9_IRQ_STAT_DIO PPC_BIT(12) +#define PSIHB9_IRQ_STAT_PSU PPC_BIT(13) + +static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno) +{ + PnvPsi *psi = PNV_PSI(xf); + uint64_t notif_port = psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)]; + bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID; + uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID; + + uint32_t offset = + (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); + uint64_t lisn = cpu_to_be64(offset + srcno); + + if (valid) { + cpu_physical_memory_write(notify_addr, &lisn, sizeof(lisn)); + } +} + +static uint64_t pnv_psi_p9_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + uint32_t reg = PSIHB_REG(addr); + uint64_t val = -1; + + switch (addr) { + case PSIHB9_CR: + case PSIHB9_SEMR: + /* FSP stuff */ + case PSIHB9_INTERRUPT_CONTROL: + case PSIHB9_ESB_CI_BASE: + case PSIHB9_ESB_NOTIF_ADDR: + case PSIHB9_IVT_OFFSET: + val = psi->regs[reg]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: read at 0x%" PRIx64 "\n", addr); + } + + return val; +} + +static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + Pnv9Psi *psi9 = PNV9_PSI(psi); + uint32_t reg = PSIHB_REG(addr); + MemoryRegion *sysmem = get_system_memory(); + + switch (addr) { + case PSIHB9_CR: + case PSIHB9_SEMR: + /* FSP stuff */ + break; + case PSIHB9_INTERRUPT_CONTROL: + if (val & PSIHB9_IRQ_RESET) { + device_reset(DEVICE(&psi9->source)); + } + psi->regs[reg] = val; + break; + + case PSIHB9_ESB_CI_BASE: + if (!(val & PSIHB9_ESB_CI_VALID)) { + if (psi->regs[reg] & PSIHB9_ESB_CI_VALID) { + memory_region_del_subregion(sysmem, &psi9->source.esb_mmio); + } + } else { + if (!(psi->regs[reg] & PSIHB9_ESB_CI_VALID)) { + memory_region_add_subregion(sysmem, + val & ~PSIHB9_ESB_CI_VALID, + &psi9->source.esb_mmio); + } + } + psi->regs[reg] = val; + break; + + case PSIHB9_ESB_NOTIF_ADDR: + psi->regs[reg] = val; + break; + case PSIHB9_IVT_OFFSET: + psi->regs[reg] = val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: write at 0x%" PRIx64 "\n", addr); + } +} + +static const MemoryRegionOps pnv_psi_p9_mmio_ops = { + .read = pnv_psi_p9_mmio_read, + .write = pnv_psi_p9_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_psi_p9_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + /* No read are expected */ + qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom read at 0x%" PRIx64 "\n", addr); + return -1; +} + +static void pnv_psi_p9_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + + /* XSCOM is only used to set the PSIHB MMIO region */ + switch (addr >> 3) { + case PSIHB_XSCOM_BAR: + pnv_psi_set_bar(psi, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom write at 0x%" PRIx64 "\n", + addr); + } +} + +static const MemoryRegionOps pnv_psi_p9_xscom_ops = { + .read = pnv_psi_p9_xscom_read, + .write = pnv_psi_p9_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + } +}; + +static void pnv_psi_power9_irq_set(PnvPsi *psi, int irq, bool state) +{ + uint32_t irq_method = psi->regs[PSIHB_REG(PSIHB9_INTERRUPT_CONTROL)]; + + if (irq > PSIHB9_NUM_IRQS) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); + return; + } + + if (irq_method & PSIHB9_IRQ_METHOD) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: LSI IRQ method no supported\n"); + return; + } + + /* Update LSI levels */ + if (state) { + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= PPC_BIT(irq); + } else { + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~PPC_BIT(irq); + } + + qemu_set_irq(psi->qirqs[irq], state); +} + +static void pnv_psi_power9_reset(void *dev) +{ + Pnv9Psi *psi = PNV9_PSI(dev); + + pnv_psi_reset(dev); + + if (memory_region_is_mapped(&psi->source.esb_mmio)) { + memory_region_del_subregion(get_system_memory(), &psi->source.esb_mmio); + } +} + +static void pnv_psi_power9_instance_init(Object *obj) +{ + Pnv9Psi *psi = PNV9_PSI(obj); + + object_initialize_child(obj, "source", &psi->source, sizeof(psi->source), + TYPE_XIVE_SOURCE, &error_abort, NULL); +} + +static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) +{ + PnvPsi *psi = PNV_PSI(dev); + XiveSource *xsrc = &PNV9_PSI(psi)->source; + Error *local_err = NULL; + int i; + + /* This is the only device with 4k ESB pages */ + object_property_set_int(OBJECT(xsrc), XIVE_ESB_4K, "shift", + &error_fatal); + object_property_set_int(OBJECT(xsrc), PSIHB9_NUM_IRQS, "nr-irqs", + &error_fatal); + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(psi), + &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + for (i = 0; i < xsrc->nr_irqs; i++) { + xive_source_irq_set_lsi(xsrc, i); + } + + psi->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs); + + /* XSCOM region for PSI registers */ + pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_p9_xscom_ops, + psi, "xscom-psi", PNV9_XSCOM_PSIHB_SIZE); + + /* MMIO region for PSI registers */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &pnv_psi_p9_mmio_ops, psi, + "psihb", PNV9_PSIHB_SIZE); + + pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); + + qemu_register_reset(pnv_psi_power9_reset, dev); +} + +static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); + + dc->desc = "PowerNV PSI Controller POWER9"; + dc->realize = pnv_psi_power9_realize; + + ppc->chip_type = PNV_CHIP_POWER9; + ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; + ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; + ppc->bar_mask = PSIHB9_BAR_MASK; + ppc->irq_set = pnv_psi_power9_irq_set; + + xfc->notify = pnv_psi_notify; +} + +static const TypeInfo pnv_psi_power9_info = { + .name = TYPE_PNV9_PSI, + .parent = TYPE_PNV_PSI, + .instance_size = sizeof(Pnv9Psi), + .instance_init = pnv_psi_power9_instance_init, + .class_init = pnv_psi_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_NOTIFIER }, + { }, + }, +}; + static void pnv_psi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -612,6 +924,19 @@ static void pnv_psi_register_types(void) { type_register_static(&pnv_psi_info); type_register_static(&pnv_psi_power8_info); + type_register_static(&pnv_psi_power9_info); } type_init(pnv_psi_register_types); + +void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) +{ + PnvPsi *psi = PNV_PSI(psi9); + + uint32_t offset = + (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); + + monitor_printf(mon, "PSIHB Source %08x .. %08x\n", + offset, offset + psi9->source.nr_irqs - 1); + xive_source_pic_print_info(&psi9->source, offset, mon); +} |