@@ -645,6 +645,15 @@ static void pnv_chip_init(Object *obj)
object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
}
+static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
+{
+ char *name;
+
+ name = g_strdup_printf("icp-%x", chip->chip_id);
+ memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio);
+}
+
static void pnv_chip_realize(DeviceState *dev, Error **errp)
{
PnvChip *chip = PNV_CHIP(dev);
@@ -667,6 +676,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
}
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
+ /* Interrupt Controller */
+ pnv_chip_icp_realize(chip, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
/* Cores */
pnv_chip_core_sanitize(chip, &error);
if (error) {
@@ -26,6 +26,128 @@
#include "hw/ppc/pnv_core.h"
#include "hw/ppc/pnv_xscom.h"
+static uint64_t pnv_core_icp_read(void *opaque, hwaddr addr, unsigned width)
+{
+ ICPState *icp = opaque;
+ bool byte0 = (width == 1 && (addr & 0x3) == 0);
+ uint64_t val = 0xffffffff;
+
+ switch (addr & 0xffc) {
+ case 0: /* poll */
+ val = icp_ipoll(icp, NULL);
+ if (byte0) {
+ val >>= 24;
+ } else if (width != 4) {
+ goto bad_access;
+ }
+ break;
+ case 4: /* xirr */
+ if (byte0) {
+ val = icp_ipoll(icp, NULL) >> 24;
+ } else if (width == 4) {
+ val = icp_accept(icp);
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 12:
+ if (byte0) {
+ val = icp->mfrr;
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 16:
+ if (width == 4) {
+ val = icp->links[0];
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 20:
+ if (width == 4) {
+ val = icp->links[1];
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 24:
+ if (width == 4) {
+ val = icp->links[2];
+ } else {
+ goto bad_access;
+ }
+ break;
+ default:
+bad_access:
+ qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
+ HWADDR_PRIx"/%d\n", addr, width);
+ }
+
+ return val;
+}
+
+static void pnv_core_icp_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ ICPState *icp = opaque;
+ bool byte0 = (width == 1 && (addr & 0x3) == 0);
+
+ switch (addr & 0xffc) {
+ case 4: /* xirr */
+ if (byte0) {
+ icp_set_cppr(icp, val);
+ } else if (width == 4) {
+ icp_eoi(icp, val);
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 12:
+ if (byte0) {
+ icp_set_mfrr(icp, val);
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 16:
+ if (width == 4) {
+ icp->links[0] = val;
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 20:
+ if (width == 4) {
+ icp->links[1] = val;
+ } else {
+ goto bad_access;
+ }
+ break;
+ case 24:
+ if (width == 4) {
+ icp->links[2] = val;
+ } else {
+ goto bad_access;
+ }
+ break;
+ default:
+bad_access:
+ qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
+ HWADDR_PRIx"/%d\n", addr, width);
+ }
+}
+
+static const MemoryRegionOps pnv_core_icp_ops = {
+ .read = pnv_core_icp_read,
+ .write = pnv_core_icp_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
static void powernv_cpu_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
@@ -129,6 +251,14 @@ static void pnv_core_realize_child(Object *child, Error **errp)
}
}
+static ICPState *xics_get_icp_per_pir(XICSState* xics, int pir)
+{
+ int index = xics_get_cpu_index_by_pir(pir);
+ assert(index != -1);
+
+ return &xics->ss[index];
+}
+
static void pnv_core_realize(DeviceState *dev, Error **errp)
{
PnvCore *pc = PNV_CORE(OBJECT(dev));
@@ -140,6 +270,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
void *obj;
int i, j;
char name[32];
+ PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
pc->threads = g_malloc0(size * cc->nr_threads);
for (i = 0; i < cc->nr_threads; i++) {
@@ -169,8 +300,15 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
pc, name, PNV_XSCOM_EX_CORE_SIZE);
- return;
+ pc->icp_mmios = g_new0(MemoryRegion, cc->nr_threads);
+ for (i = 0; i < cc->nr_threads; i++) {
+ ICPState *icp = xics_get_icp_per_pir(&pnv->xics, pc->pir + i);
+ snprintf(name, sizeof(name), "icp-core.%d", cc->core_id);
+ memory_region_init_io(&pc->icp_mmios[i], OBJECT(dev),
+ &pnv_core_icp_ops, icp, name, 0x1000);
+ }
+ return;
err:
while (--i >= 0) {
obj = pc->threads + i * size;
@@ -55,6 +55,7 @@ typedef struct PnvChip {
MemoryRegion xscom_mmio;
MemoryRegion xscom;
AddressSpace xscom_as;
+ MemoryRegion icp_mmio;
PnvLpcController lpc;
} PnvChip;
@@ -128,4 +129,23 @@ typedef struct PnvMachineState {
#define PNV_XSCOM_BASE(chip) \
(chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
+/*
+ * XSCOM 0x20109CA defines the ICP BAR:
+ *
+ * 0:29 : bits 14 to 43 of address to define 1 MB region.
+ * 30 : 1 to enable ICP to receive loads/stores against its BAR region
+ * 31:63 : Constant 0
+ *
+ * Usually defined as :
+ *
+ * 0xffffe00200000000 -> 0x0003ffff80000000
+ * 0xffffe00600000000 -> 0x0003ffff80100000
+ * 0xffffe02200000000 -> 0x0003ffff80800000
+ * 0xffffe02600000000 -> 0x0003ffff80900000
+ *
+ * TODO: make a macro using the chip hw id
+ */
+#define PNV_ICP_BASE(chip) 0x0003ffff80000000ull
+#define PNV_ICP_SIZE 0x0000000000100000ull
+
#endif /* _PPC_PNV_H */
@@ -38,6 +38,8 @@ typedef struct PnvCore {
uint32_t pir;
MemoryRegion xscom_regs;
+
+ MemoryRegion *icp_mmios;
} PnvCore;
typedef struct PnvCoreClass {
@@ -120,6 +120,11 @@ struct ICPState {
bool cap_irq_xics_enabled;
XICSState *xics;
+
+ /*
+ * for XICSNative (not used by Linux).
+ */
+ uint32_t links[3];
};
#define TYPE_ICS_BASE "ics-base"
This provides access to the MMIO based Interrupt Presentation Controllers (ICP) as found on a POWER8 system. Each chip of a PowerNV machine has a global container MMIO region for all ICPs. Each thread of the chip has its own ICP MMIO subregion, indexed by its PIR number, holding a XIVE (External Interrupt Vector Entry). The regions also provide a mean to link with the ICPState to a CPU. Signed-off-by: Cédric Le Goater <clg@kaod.org> --- Changes since v5: - moved the ICP container region to PnvChip - moved the ICP subregions to PnvCore - removed XICSNative - renamed patch Changes since v4: - replaced the pir_table by memory subregions using an ICP. - removed the find_icp() and cpu_setup() handlers which became useless with the memory regions. - removed the superfluous inits done in xics_native_initfn. This is covered in the parent class init. - took ownership of the patch. hw/ppc/pnv.c | 16 ++++++ hw/ppc/pnv_core.c | 140 +++++++++++++++++++++++++++++++++++++++++++++- include/hw/ppc/pnv.h | 20 +++++++ include/hw/ppc/pnv_core.h | 2 + include/hw/ppc/xics.h | 5 ++ 5 files changed, 182 insertions(+), 1 deletion(-)