All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8)
@ 2017-03-08 10:52 Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper Cédric Le Goater
                   ` (7 more replies)
  0 siblings, 8 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

Hello,

Here is a series adding support for the interrupt controller as found
on a POWER8 system. POWER9 uses a different interrupt controller
called XIVE, still to be worked on.

The initial patches are small extensions to the recently added
XICSFabric framework. The PowerNV machine is then extended to hold the
Interrupt Source Control (ICS) and the Interrupt Control Presenter
objects of the system. After that, we use a memory region to model the
Interrupt Management area which contains the ICPs registers of a
POWER8 PowerNV system.

Last is a PSI (Processor Service Interface) bridge model to handle the
external interrupt, a minimal model for the OCC (on-chip Controller)
and support for the LPC controller on POWER8+ cpus.

To test, grab a kernel and a rootfs image here :

  https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/zImage.epapr
  https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/rootfs.cpio.xz

The full patchset is available here :

   https://github.com/legoater/qemu/commits/powernv-ipmi-2.9

Thanks,

C.

Benjamin Herrenschmidt (3):
  ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  ppc/pnv: Add OCC model stub with interrupt support
  ppc/pnv: Add support for POWER8+ LPC Controller

Cédric Le Goater (5):
  ppc/xics: add a xics_get_cpu_index_by_pir() helper
  ppc/xics: add an ics_eoi() handler to XICSFabric
  ppc/pnv: create the ICP and ICS objects under the machine
  ppc/pnv: add memory regions for the ICP registers
  ppc/pnv: map the ICP memory regions

 hw/intc/xics.c             |  20 +-
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               | 224 ++++++++++++++++-
 hw/ppc/pnv_core.c          | 158 +++++++++++-
 hw/ppc/pnv_lpc.c           |  47 +++-
 hw/ppc/pnv_occ.c           | 136 +++++++++++
 hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/ppc.c               |  16 ++
 hw/ppc/spapr.c             |  11 +
 include/hw/ppc/pnv.h       |  34 +++
 include/hw/ppc/pnv_core.h  |   1 +
 include/hw/ppc/pnv_lpc.h   |   9 +
 include/hw/ppc/pnv_occ.h   |  38 +++
 include/hw/ppc/pnv_psi.h   |  61 +++++
 include/hw/ppc/pnv_xscom.h |   6 +
 include/hw/ppc/xics.h      |   7 +
 target/ppc/cpu.h           |  10 +
 17 files changed, 1343 insertions(+), 20 deletions(-)
 create mode 100644 hw/ppc/pnv_occ.c
 create mode 100644 hw/ppc/pnv_psi.c
 create mode 100644 include/hw/ppc/pnv_occ.h
 create mode 100644 include/hw/ppc/pnv_psi.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-14  5:38   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric Cédric Le Goater
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

This helper will be used to translate the server number of the XIVE
(which is a PIR) into an ICPState index number (which is a cpu index).

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/xics.c        | 11 +++++++++++
 hw/ppc/ppc.c          | 16 ++++++++++++++++
 include/hw/ppc/xics.h |  1 +
 target/ppc/cpu.h      | 10 ++++++++++
 4 files changed, 38 insertions(+)

diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index e740989a1162..209e1a75ecb9 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -49,6 +49,17 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
     return -1;
 }
 
+int xics_get_cpu_index_by_pir(int pir)
+{
+    PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir);
+
+    if (cpu) {
+        return cpu->parent_obj.cpu_index;
+    }
+
+    return -1;
+}
+
 void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 5f93083d4a16..94bbe382a73a 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -1379,6 +1379,22 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id)
     return NULL;
 }
 
+PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
+{
+    CPUState *cs;
+
+    CPU_FOREACH(cs) {
+        PowerPCCPU *cpu = POWERPC_CPU(cs);
+        CPUPPCState *env = &cpu->env;
+
+        if (env->spr_cb[SPR_PIR].default_value == pir) {
+            return cpu;
+        }
+    }
+
+    return NULL;
+}
+
 void ppc_cpu_parse_features(const char *cpu_model)
 {
     CPUClass *cc;
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 9a5e715fe553..42bd24e975cb 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -173,6 +173,7 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu);
 
 /* Internal XICS interfaces */
 int xics_get_cpu_index_by_dt_id(int cpu_dt_id);
+int xics_get_cpu_index_by_pir(int pir);
 
 void icp_set_cppr(ICPState *icp, uint8_t cppr);
 void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 7c4a1f50b38b..24a5af95cb45 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -2518,5 +2518,15 @@ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu);
  */
 PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id);
 
+/**
+ * ppc_get_vcpu_by_pir_id:
+ * @pir: Processor Identifier Register (SPR_PIR)
+ *
+ * Searches for a CPU by @pir.
+ *
+ * Returns: a PowerPCCPU struct
+ */
+PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
+
 void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len);
 #endif /* PPC_CPU_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-14  5:40   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine Cédric Le Goater
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

This handler will be required by PowerPC machines using multiple ICS
objects, like this is the case for PowerNV. Also update the sPAPR
machine to use the new handler.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/xics.c        |  9 +++------
 hw/ppc/spapr.c        | 11 +++++++++++
 include/hw/ppc/xics.h |  2 ++
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 209e1a75ecb9..e6fecd6e1a89 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -169,7 +169,7 @@ void ics_resend(ICSState *ics)
     }
 }
 
-static void ics_eoi(ICSState *ics, int nr)
+void ics_eoi(ICSState *ics, int nr)
 {
     ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
 
@@ -268,7 +268,6 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
 {
     XICSFabric *xi = icp->xics;
     XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi);
-    ICSState *ics;
     uint32_t irq;
 
     /* Send EOI -> ICS */
@@ -276,10 +275,8 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
     trace_xics_icp_eoi(icp->cs->cpu_index, xirr, icp->xirr);
     irq = xirr & XISR_MASK;
 
-    ics = xic->ics_get(xi, irq);
-    if (ics) {
-        ics_eoi(ics, irq);
-    }
+    xic->ics_eoi(xi, irq);
+
     if (!XISR(icp)) {
         icp_resend(icp);
     }
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index c3bb99160545..043629cc5c54 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -3024,6 +3024,16 @@ static void spapr_ics_resend(XICSFabric *dev)
     ics_resend(spapr->ics);
 }
 
+static void spapr_ics_eoi(XICSFabric *xi, int irq)
+{
+    ICSState *ics;
+
+    ics = spapr_ics_get(xi, irq);
+    if (ics) {
+        ics_eoi(ics, irq);
+    }
+}
+
 static ICPState *spapr_icp_get(XICSFabric *xi, int server)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(xi);
@@ -3094,6 +3104,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     vhc->get_patbe = spapr_get_patbe;
     xic->ics_get = spapr_ics_get;
     xic->ics_resend = spapr_ics_resend;
+    xic->ics_eoi = spapr_ics_eoi;
     xic->icp_get = spapr_icp_get;
     ispc->print_info = spapr_pic_print_info;
 }
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 42bd24e975cb..00b003b2392d 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -155,6 +155,7 @@ typedef struct XICSFabricClass {
     InterfaceClass parent;
     ICSState *(*ics_get)(XICSFabric *xi, int irq);
     void (*ics_resend)(XICSFabric *xi);
+    void (*ics_eoi)(XICSFabric *xi, int irq);
     ICPState *(*icp_get)(XICSFabric *xi, int server);
 } XICSFabricClass;
 
@@ -189,6 +190,7 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon);
 void ics_pic_print_info(ICSState *ics, Monitor *mon);
 
 void ics_resend(ICSState *ics);
+void ics_eoi(ICSState *ics, int irq);
 void icp_resend(ICPState *ss);
 
 typedef struct sPAPRMachineState sPAPRMachineState;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-14  5:45   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers Cédric Le Goater
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

Like this is done for the sPAPR machine, we use a simple array under
the PowerNV machine to store the Interrupt Control Presenters (ICP)
objects, one for each vCPU. This array is indexed by 'cpu_index' of
the CPUState.

We use a list to hold the different Interrupt Control Sources (ICS)
objects, as PowerNV needs to handle multiple sources: for PCI-E and
for the Processor Service Interface (PSI).

Finally, to interface with the XICS layer which manipulates the ICP
and ICS objects, we extend the PowerNV machine with an XICSFabric
interface and its associated handlers.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c          | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h  |  4 +++
 include/hw/ppc/xics.h |  1 +
 3 files changed, 94 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 09f0d22defb8..461d3535e99c 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -32,6 +32,8 @@
 #include "exec/address-spaces.h"
 #include "qemu/cutils.h"
 #include "qapi/visitor.h"
+#include "monitor/monitor.h"
+#include "hw/intc/intc.h"
 
 #include "hw/ppc/pnv_xscom.h"
 
@@ -416,6 +418,23 @@ static void ppc_powernv_init(MachineState *machine)
         machine->cpu_model = "POWER8";
     }
 
+    /* Create the Interrupt Control Presenters before the vCPUs */
+    pnv->nr_servers = pnv->num_chips * smp_cores * smp_threads;
+    pnv->icps = g_new0(ICPState, pnv->nr_servers);
+    for (i = 0; i < pnv->nr_servers; i++) {
+        ICPState *icp = &pnv->icps[i];
+        object_initialize(icp, sizeof(*icp), TYPE_ICP);
+        qdev_set_parent_bus(DEVICE(icp), sysbus_get_default());
+        object_property_add_child(OBJECT(pnv), "icp[*]", OBJECT(icp),
+                                  &error_fatal);
+        object_property_add_const_link(OBJECT(icp), "xics", OBJECT(pnv),
+                                       &error_fatal);
+        object_property_set_bool(OBJECT(icp), true, "realized", &error_fatal);
+    }
+
+    /* and the list of Interrupt Control Sources */
+    QLIST_INIT(&pnv->ics);
+
     /* Create the processor chips */
     chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
     if (!object_class_by_name(chip_typename)) {
@@ -742,6 +761,48 @@ static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
     visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp);
 }
 
+static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(xi);
+    ICSState *ics;
+
+    QLIST_FOREACH(ics, &pnv->ics, list) {
+        if (ics_valid_irq(ics, irq)) {
+            return ics;
+        }
+    }
+    return NULL;
+}
+
+static void pnv_ics_resend(XICSFabric *xi)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(xi);
+    ICSState *ics;
+
+    QLIST_FOREACH(ics, &pnv->ics, list) {
+        ics_resend(ics);
+    }
+}
+
+static void pnv_ics_eoi(XICSFabric *xi, int irq)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(xi);
+    ICSState *ics;
+
+    QLIST_FOREACH(ics, &pnv->ics, list) {
+        if (ics_valid_irq(ics, irq)) {
+            ics_eoi(ics, irq);
+        }
+    }
+}
+
+static ICPState *pnv_icp_get(XICSFabric *xi, int server)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(xi);
+
+    return (server < pnv->nr_servers) ? &pnv->icps[server] : NULL;
+}
+
 static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name,
                               void *opaque, Error **errp)
 {
@@ -783,9 +844,27 @@ static void powernv_machine_class_props_init(ObjectClass *oc)
                               NULL);
 }
 
+static void pnv_pic_print_info(InterruptStatsProvider *obj,
+                               Monitor *mon)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(obj);
+    ICSState *ics;
+    int i;
+
+    for (i = 0; i < pnv->nr_servers; i++) {
+        icp_pic_print_info(&pnv->icps[i], mon);
+    }
+
+    QLIST_FOREACH(ics, &pnv->ics, list) {
+        ics_pic_print_info(ics, mon);
+    }
+}
+
 static void powernv_machine_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
+    XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
+    InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
 
     mc->desc = "IBM PowerNV (Non-Virtualized)";
     mc->init = ppc_powernv_init;
@@ -796,6 +875,11 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data)
     mc->no_parallel = 1;
     mc->default_boot_order = NULL;
     mc->default_ram_size = 1 * G_BYTE;
+    xic->icp_get = pnv_icp_get;
+    xic->ics_get = pnv_ics_get;
+    xic->ics_eoi = pnv_ics_eoi;
+    xic->ics_resend = pnv_ics_resend;
+    ispc->print_info = pnv_pic_print_info;
 
     powernv_machine_class_props_init(oc);
 }
@@ -806,6 +890,11 @@ static const TypeInfo powernv_machine_info = {
     .instance_size = sizeof(PnvMachineState),
     .instance_init = powernv_machine_initfn,
     .class_init    = powernv_machine_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_XICS_FABRIC },
+        { TYPE_INTERRUPT_STATS_PROVIDER },
+        { },
+    },
 };
 
 static void powernv_machine_register_types(void)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index df98a72006e4..6a0b004cea93 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -22,6 +22,7 @@
 #include "hw/boards.h"
 #include "hw/sysbus.h"
 #include "hw/ppc/pnv_lpc.h"
+#include "hw/ppc/xics.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -114,6 +115,9 @@ typedef struct PnvMachineState {
     PnvChip      **chips;
 
     ISABus       *isa_bus;
+    ICPState     *icps;
+    uint32_t     nr_servers;
+    QLIST_HEAD(, ICSState) ics;
 } PnvMachineState;
 
 #define PNV_FDT_ADDR          0x01000000
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 00b003b2392d..c2032cac55f6 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -115,6 +115,7 @@ struct ICSState {
     qemu_irq *qirqs;
     ICSIRQState *irqs;
     XICSFabric *xics;
+    QLIST_ENTRY(ICSState) list;
 };
 
 static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
                   ` (2 preceding siblings ...)
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-08 11:24   ` Philippe Mathieu-Daudé
  2017-03-14  5:49   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions Cédric Le Goater
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

This provides to a PowerNV chip (POWER8) access to the Interrupt
Management area, which contains the registers of the Interrupt Control
Presenters of each thread. These are used to accept, return, forward
interrupts in the system.

This area is modeled with a per-chip container memory region holding
all the ICP registers. Each thread of a chip is then associated with
its ICP registers using a memory subregion indexed by its PIR number
in the overall region.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c              |  20 +++++++
 hw/ppc/pnv_core.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h      |  20 +++++++
 include/hw/ppc/pnv_core.h |   1 +
 include/hw/ppc/xics.h     |   3 +
 5 files changed, 190 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 461d3535e99c..7b13b08deadf 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -658,6 +658,16 @@ 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);
+    g_free(name);
+}
+
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
@@ -680,6 +690,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
 
+    /* Interrupt Management Area. This is the memory region holding
+     * all the Interrupt Control Presenter (ICP) registers */
+    pnv_chip_icp_realize(chip, &error);
+    if (error) {
+        error_propagate(errp, error);
+        return;
+    }
+
     /* Cores */
     pnv_chip_core_sanitize(chip, &error);
     if (error) {
@@ -709,6 +727,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         object_property_set_int(OBJECT(pnv_core),
                                 pcc->core_pir(chip, core_hwid),
                                 "pir", &error_fatal);
+        object_property_add_const_link(OBJECT(pnv_core), "xics",
+                                       qdev_get_machine(), &error_fatal);
         object_property_set_bool(OBJECT(pnv_core), true, "realized",
                                  &error_fatal);
         object_unref(OBJECT(pnv_core));
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index d79d530b4881..8633afbff795 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -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(XICSFabric *xi, int pir)
+{
+    int index = xics_get_cpu_index_by_pir(pir);
+    assert(index != -1);
+
+    return xics_icp_get(xi, index);
+}
+
 static void pnv_core_realize(DeviceState *dev, Error **errp)
 {
     PnvCore *pc = PNV_CORE(OBJECT(dev));
@@ -140,6 +270,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
     void *obj;
     int i, j;
     char name[32];
+    Object *xi;
+
+    xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
+    if (!xi) {
+        error_setg(errp, "%s: required link 'xics' not found: %s",
+                   __func__, error_get_pretty(local_err));
+        return;
+    }
 
     pc->threads = g_malloc0(size * cc->nr_threads);
     for (i = 0; i < cc->nr_threads; i++) {
@@ -169,6 +307,14 @@ 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);
+
+    pc->icp_mmios = g_new0(MemoryRegion, cc->nr_threads);
+    for (i = 0; i < cc->nr_threads; i++) {
+        ICPState *icp = xics_get_icp_per_pir(XICS_FABRIC(xi), 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:
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 6a0b004cea93..f11215ea31f2 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -55,6 +55,7 @@ typedef struct PnvChip {
     MemoryRegion xscom_mmio;
     MemoryRegion xscom;
     AddressSpace xscom_as;
+    MemoryRegion icp_mmio;
 
     PnvLpcController lpc;
 } PnvChip;
@@ -130,4 +131,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 */
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
index 2955a41c901f..f2fad8f6361b 100644
--- a/include/hw/ppc/pnv_core.h
+++ b/include/hw/ppc/pnv_core.h
@@ -38,6 +38,7 @@ typedef struct PnvCore {
     uint32_t pir;
 
     MemoryRegion xscom_regs;
+    MemoryRegion *icp_mmios;
 } PnvCore;
 
 typedef struct PnvCoreClass {
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index c2032cac55f6..a3dcdf93bbe3 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -78,6 +78,9 @@ struct ICPState {
     bool cap_irq_xics_enabled;
 
     XICSFabric *xics;
+
+    /* for the PowerNV ICP registers (not used by Linux). */
+    uint32_t links[3];
 };
 
 #define TYPE_ICS_BASE "ics-base"
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
                   ` (3 preceding siblings ...)
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-14  5:52   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Cédric Le Goater

and populate the device tree accordingly for the guest to start using
interrupts. This also links the ICP object to its associated CPUState
(only used by KVM to control the kernel vCPU).

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c      | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv_core.c | 12 ++++++++----
 2 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7b13b08deadf..0ae11cc3a2ca 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -35,6 +35,7 @@
 #include "monitor/monitor.h"
 #include "hw/intc/intc.h"
 
+#include "hw/ppc/xics.h"
 #include "hw/ppc/pnv_xscom.h"
 
 #include "hw/isa/isa.h"
@@ -216,6 +217,47 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
                        servers_prop, sizeof(servers_prop))));
 }
 
+static void powernv_populate_icp(PnvChip *chip, void *fdt, int offset,
+                          uint32_t pir, uint32_t count)
+{
+    uint64_t addr;
+    char *name;
+    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
+    uint32_t irange[2], i, rsize;
+    uint64_t *reg;
+
+    /*
+     * TODO: add multichip ICP BAR
+     */
+    addr = PNV_ICP_BASE(chip) | (pir << 12);
+
+    irange[0] = cpu_to_be32(pir);
+    irange[1] = cpu_to_be32(count);
+
+    rsize = sizeof(uint64_t) * 2 * count;
+    reg = g_malloc(rsize);
+    for (i = 0; i < count; i++) {
+        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
+        reg[i * 2 + 1] = cpu_to_be64(0x1000);
+    }
+
+    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
+    offset = fdt_add_subnode(fdt, offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
+    _FDT((fdt_setprop_string(fdt, offset, "device_type",
+                              "PowerPC-External-Interrupt-Presentation")));
+    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
+    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
+                       irange, sizeof(irange))));
+    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
+    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
+    g_free(reg);
+}
+
 static void powernv_populate_chip(PnvChip *chip, void *fdt)
 {
     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
@@ -229,6 +271,10 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
 
         powernv_create_core_node(chip, pnv_core, fdt);
+
+        /* Interrupt Control Presenters (ICP). One per thread. */
+        powernv_populate_icp(chip, fdt, 0, pnv_core->pir,
+                             CPU_CORE(pnv_core)->nr_threads);
     }
 
     if (chip->ram_size) {
@@ -697,6 +743,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         error_propagate(errp, error);
         return;
     }
+    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
 
     /* Cores */
     pnv_chip_core_sanitize(chip, &error);
@@ -711,6 +758,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
              && (i < chip->nr_cores); core_hwid++) {
         char core_name[32];
         void *pnv_core = chip->cores + i * typesize;
+        int j;
 
         if (!(chip->cores_mask & (1ull << core_hwid))) {
             continue;
@@ -738,6 +786,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
                                 PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
                                                        core_hwid),
                                 &PNV_CORE(pnv_core)->xscom_regs);
+
+        /* Map the ICP registers for each thread */
+        for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
+            memory_region_add_subregion(&chip->icp_mmio,
+                                 (pcc->core_pir(chip, core_hwid) + j) << 12,
+                                  &PNV_CORE(pnv_core)->icp_mmios[j]);
+        }
         i++;
     }
     g_free(typename);
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 8633afbff795..d28fa445b11b 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -25,6 +25,7 @@
 #include "hw/ppc/pnv.h"
 #include "hw/ppc/pnv_core.h"
 #include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/xics.h"
 
 static uint64_t pnv_core_icp_read(void *opaque, hwaddr addr, unsigned width)
 {
@@ -165,7 +166,7 @@ static void powernv_cpu_reset(void *opaque)
     env->msr |= MSR_HVB; /* Hypervisor mode */
 }
 
-static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
+static void powernv_cpu_init(PowerPCCPU *cpu, XICSFabric *xi, Error **errp)
 {
     CPUPPCState *env = &cpu->env;
     int core_pir;
@@ -185,6 +186,9 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
     cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
 
     qemu_register_reset(powernv_cpu_reset, cpu);
+
+    /* xics_cpu_setup() assigns the CPU to the ICPState */
+    xics_cpu_setup(xi, cpu);
 }
 
 /*
@@ -232,7 +236,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static void pnv_core_realize_child(Object *child, Error **errp)
+static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
 {
     Error *local_err = NULL;
     CPUState *cs = CPU(child);
@@ -244,7 +248,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
         return;
     }
 
-    powernv_cpu_init(cpu, &local_err);
+    powernv_cpu_init(cpu, xi, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -298,7 +302,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
     for (j = 0; j < cc->nr_threads; j++) {
         obj = pc->threads + j * size;
 
-        pnv_core_realize_child(obj, &local_err);
+        pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err);
         if (local_err) {
             goto err;
         }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
                   ` (4 preceding siblings ...)
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-15  6:16   ` David Gibson
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 7/8] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 8/8] ppc/pnv: Add support for POWER8+ LPC Controller Cédric Le Goater
  7 siblings, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson
  Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt, Cédric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The PSI (Processor Service Interface) Controller is one of the engines
of the "Bridge" unit which connects the different interfaces to the
Power Processor.

This adds just enough of the PSI bridge to handle various on-chip and
the one external interrupt. The rest of PSI has to do with the link to
the IBM FSP service processor which we don't plan to emulate (not used
on OpenPower machines).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.9
      - changed the XSCOM interface to fit new model
      - QOMified the model
      - reworked set_xive and worked around a skiboot bug
      - removed the 'psi_mmio_to_xscom' mapping array ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  35 ++-
 hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   8 +
 include/hw/ppc/pnv_psi.h   |  61 +++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 685 insertions(+), 7 deletions(-)
 create mode 100644 hw/ppc/pnv_psi.c
 create mode 100644 include/hw/ppc/pnv_psi.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 001293423c8d..dc19ee17fa57 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0ae11cc3a2ca..85b00bf235c6 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -356,15 +356,22 @@ static void ppc_powernv_reset(void)
  * have a CPLD that will collect the SerIRQ and shoot them as a
  * single level interrupt to the P8 chip. So let's setup a hook
  * for doing just that.
- *
- * Note: The actual interrupt input isn't emulated yet, this will
- * come with the PSI bridge model.
  */
 static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
 {
-    /* We don't yet emulate the PSI bridge which provides the external
-     * interrupt, so just drop interrupts on the floor
-     */
+    PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
+    uint32_t old_state = pnv->cpld_irqstate;
+    PnvChip *chip = opaque;
+
+    if (level) {
+        pnv->cpld_irqstate |= 1u << n;
+    } else {
+        pnv->cpld_irqstate &= ~(1u << n);
+    }
+    if (pnv->cpld_irqstate != old_state) {
+        pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
+                        pnv->cpld_irqstate != 0);
+    }
 }
 
 static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
@@ -702,6 +709,11 @@ static void pnv_chip_init(Object *obj)
 
     object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
     object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
+
+    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
+    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
+    object_property_add_const_link(OBJECT(&chip->psi), "xics",
+                                   OBJECT(qdev_get_machine()), &error_abort);
 }
 
 static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
@@ -722,6 +734,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     char *typename = pnv_core_typename(pcc->cpu_model);
     size_t typesize = object_type_get_instance_size(typename);
     int i, core_hwid;
+    MachineState *machine = MACHINE(qdev_get_machine());
+    PnvMachineState *pnv = POWERNV_MACHINE(machine);
 
     if (!object_class_by_name(typename)) {
         error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
@@ -797,6 +811,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     }
     g_free(typename);
 
+
+    /* Processor Service Interface (PSI) Host Bridge */
+    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
+                             &error_fatal);
+    pnv_xscom_add_subregion(chip, PNV_XSCOM_PSI_BASE, &chip->psi.xscom_regs);
+
+    /* link in the PSI ICS */
+    QLIST_INSERT_HEAD(&pnv->ics, &chip->psi.ics, list);
+
     /* Create LPC controller */
     object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
                              &error_fatal);
diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
new file mode 100644
index 000000000000..6ba688aac075
--- /dev/null
+++ b/hw/ppc/pnv_psi.c
@@ -0,0 +1,583 @@
+/*
+ * QEMU PowerNV PowerPC PSI interface
+ *
+ * Copyright (c) 2016, IBM Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "target/ppc/cpu.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+
+#include "exec/address-spaces.h"
+
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv_psi.h"
+
+#include <libfdt.h>
+
+#define PSIHB_XSCOM_FIR_RW      0x00
+#define PSIHB_XSCOM_FIR_AND     0x01
+#define PSIHB_XSCOM_FIR_OR      0x02
+#define PSIHB_XSCOM_FIRMASK_RW  0x03
+#define PSIHB_XSCOM_FIRMASK_AND 0x04
+#define PSIHB_XSCOM_FIRMASK_OR  0x05
+#define PSIHB_XSCOM_FIRACT0     0x06
+#define PSIHB_XSCOM_FIRACT1     0x07
+#define PSIHB_XSCOM_BAR         0x0a
+#define   PSIHB_BAR_EN                  0x0000000000000001ull
+#define PSIHB_XSCOM_FSPBAR      0x0b
+#define PSIHB_XSCOM_CR          0x0e
+#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
+#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
+#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
+#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
+#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
+#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
+#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
+#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
+#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
+#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
+          /* and more ... */
+#define PSIHB_XSCOM_SEMR        0x0f
+#define PSIHB_XSCOM_XIVR_PSI    0x10
+#define   PSIHB_XIVR_SERVER_SH  40
+#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
+#define   PSIHB_XIVR_PRIO_SH    32
+#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
+#define   PSIHB_XIVR_SRC_SH             29
+#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
+#define   PSIHB_XIVR_PENDING    0x01000000ull
+#define PSIHB_XSCOM_SCR         0x12
+#define PSIHB_XSCOM_CCR         0x13
+#define PSIHB_XSCOM_DMA_UPADD   0x14
+#define PSIHB_XSCOM_IRQ_STAT    0x15
+#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
+#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
+#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
+#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
+#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
+#define PSIHB_XSCOM_XIVR_OCC    0x16
+#define PSIHB_XSCOM_XIVR_FSI    0x17
+#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
+#define PSIHB_XSCOM_XIVR_LOCERR 0x19
+#define PSIHB_XSCOM_XIVR_EXT    0x1a
+#define PSIHB_XSCOM_IRSN        0x1b
+#define   PSIHB_IRSN_COMP_SH            45
+#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
+#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
+#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
+#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
+#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
+#define   PSIHB_IRSN_COMPMASK_SH        13
+#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
+
+/*
+ * These are the values of the registers when accessed through the
+ * MMIO region. The relation is xscom = (mmio + 0x50) >> 3
+ */
+#define PSIHB_MMIO_BAR          0x00
+#define PSIHB_MMIO_FSPBAR       0x08
+#define PSIHB_MMIO_CR           0x20
+#define PSIHB_MMIO_SEMR         0x28
+#define PSIHB_MMIO_XIVR_PSI     0x30
+#define PSIHB_MMIO_SCR          0x40
+#define PSIHB_MMIO_CCR          0x48
+#define PSIHB_MMIO_DMA_UPADD    0x50
+#define PSIHB_MMIO_IRQ_STAT     0x58
+#define PSIHB_MMIO_XIVR_OCC     0x60
+#define PSIHB_MMIO_XIVR_FSI     0x68
+#define PSIHB_MMIO_XIVR_LPCI2C  0x70
+#define PSIHB_MMIO_XIVR_LOCERR  0x78
+#define PSIHB_MMIO_XIVR_EXT     0x80
+#define PSIHB_MMIO_IRSN         0x88
+#define PSIHB_MMIO_MAX          0x100
+
+static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
+{
+    MemoryRegion *sysmem = get_system_memory();
+    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
+
+    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
+
+    /* Update MR, always remove it first */
+    if (old & PSIHB_BAR_EN) {
+        memory_region_del_subregion(sysmem, &psi->regs_mr);
+    }
+    /* Then add it back if needed */
+    if (bar & PSIHB_BAR_EN) {
+        uint64_t addr = bar & 0x0003fffffff00000;
+        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
+    }
+}
+
+static void pnv_psi_update_fsp_mr(PnvPsi *psi)
+{
+    /* XXX Update FSP MR if/when we support FSP BAR */
+}
+
+static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr)
+{
+    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
+
+    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
+
+    /* Check some bit changes */
+    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
+        pnv_psi_update_fsp_mr(psi);
+    }
+}
+
+static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val)
+{
+    uint32_t offset;
+    ICSState *ics = &psi->ics;
+
+    /* In this model we ignore the up/down enable bits for now
+     * as SW doesn't use them (other than setting them at boot).
+     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
+     * it and finally we ignore reset (XXX fix that ?)
+     */
+    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
+                                         PSIHB_IRSN_IRQ_MUX |
+                                         PSIHB_IRSN_DOWNSTREAM_EN |
+                                         PSIHB_IRSN_DOWNSTREAM_EN |
+                                         PSIHB_IRSN_DOWNSTREAM_EN);
+
+    /* We ignore the compare mask as well, our ICS emulation is too
+     * simplistic to make any use if it, and we extract the offset
+     * from the compare value
+     */
+    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
+    ics->offset = offset;
+}
+
+static bool pnv_psi_irq_bits(PnvPsi *psi, PnvPsiIrq irq,
+                             uint32_t *out_xivr_reg,
+                             uint32_t *out_stat_reg,
+                             uint64_t *out_stat_bit)
+{
+    switch (irq) {
+    case PSIHB_IRQ_PSI:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
+        *out_stat_reg = PSIHB_XSCOM_CR;
+        *out_stat_bit = PSIHB_CR_PSI_IRQ;
+        break;
+    case PSIHB_IRQ_FSP:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
+        *out_stat_reg = PSIHB_XSCOM_CR;
+        *out_stat_bit = PSIHB_CR_FSP_IRQ;
+        break;
+    case PSIHB_IRQ_OCC:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
+        break;
+    case PSIHB_IRQ_FSI:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
+        break;
+    case PSIHB_IRQ_LPC_I2C:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
+        break;
+    case PSIHB_IRQ_LOCAL_ERR:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
+        break;
+    case PSIHB_IRQ_EXTERNAL:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
+        break;
+    default:
+        return false;
+    }
+    return true;
+}
+
+void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
+{
+    ICSState *ics = &psi->ics;
+    uint32_t xivr_reg;
+    uint32_t stat_reg;
+    uint64_t stat_bit;
+    uint32_t src;
+    bool masked;
+
+    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
+        return;
+    }
+
+    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
+    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
+    if (state) {
+        psi->regs[stat_reg] |= stat_bit;
+        /* XXX optimization: check mask here. That means re-evaluating
+         * when unmasking, thus TODO
+         */
+        qemu_irq_raise(ics->qirqs[src]);
+    } else {
+        psi->regs[stat_reg] &= ~stat_bit;
+
+        /* FSP and PSI are muxed so don't lower if either still set */
+        if (stat_reg != PSIHB_XSCOM_CR ||
+            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
+            qemu_irq_lower(ics->qirqs[src]);
+        } else {
+            state = true;
+        }
+    }
+
+    /* XXX Note about the emulation of the pending bit: This isn't
+     * entirely correct. The pending bit should be cleared when the
+     * EOI has been received. However, we don't have callbacks on
+     * EOI (especially not under KVM) so no way to emulate that
+     * properly, so instead we just set that bit as the logical
+     * "output" of the XIVR (ie pending & !masked)
+     * XXX TODO: Also update it on set_xivr
+     */
+    if (state && !masked) {
+        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
+    } else {
+        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
+    }
+}
+
+static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val)
+{
+    ICSState *ics = &psi->ics;
+    uint16_t server;
+    uint8_t prio;
+    uint8_t src;
+    int icp_index;
+
+    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
+            (val & (PSIHB_XIVR_SERVER_MSK |
+                    PSIHB_XIVR_PRIO_MSK |
+                    PSIHB_XIVR_SRC_MSK));
+    val = psi->regs[reg];
+    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
+    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
+    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
+    if (src > PSIHB_IRQ_EXTERNAL) {
+        /* XXX Generate error ? */
+        return;
+    }
+
+    /*
+     * Linux fills the irq xivr with the hw processor id plus the
+     * link bits. shift back to get something valid.
+     */
+    server >>= 2;
+
+    /*
+     * When skiboot initializes PSIHB, it fills the xives with
+     * server=0, prio=0xff, but we don't have a CPU with a pir=0. So
+     * skip that case.
+     */
+    if (prio != 0xff) {
+        icp_index = xics_get_cpu_index_by_pir(server);
+        assert(icp_index != -1);
+    } else {
+        if (server) {
+            qemu_log_mask(LOG_GUEST_ERROR, "PSI: bogus server %d for IRQ %d\n",
+                          server, src);
+        }
+        icp_index = server;
+    }
+
+    /* Now because of source remapping, weird things can happen
+     * if you change the source number dynamically, our simple ICS
+     * doesn't deal with remapping. So we just poke a different
+     * ICS entry based on what source number was written. This will
+     * do for now but a more accurate implementation would instead
+     * use a fixed server/prio and a remapper of the generated irq.
+     */
+    ics_simple_write_xive(ics, src, icp_index, prio, prio);
+}
+
+static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio)
+{
+    uint64_t val = 0xffffffffffffffffull;
+
+    switch (offset) {
+    case PSIHB_XSCOM_FIR_RW:
+    case PSIHB_XSCOM_FIRACT0:
+    case PSIHB_XSCOM_FIRACT1:
+    case PSIHB_XSCOM_BAR:
+    case PSIHB_XSCOM_FSPBAR:
+    case PSIHB_XSCOM_CR:
+    case PSIHB_XSCOM_XIVR_PSI:
+    case PSIHB_XSCOM_XIVR_OCC:
+    case PSIHB_XSCOM_XIVR_FSI:
+    case PSIHB_XSCOM_XIVR_LPCI2C:
+    case PSIHB_XSCOM_XIVR_LOCERR:
+    case PSIHB_XSCOM_XIVR_EXT:
+    case PSIHB_XSCOM_IRQ_STAT:
+    case PSIHB_XSCOM_SEMR:
+    case PSIHB_XSCOM_DMA_UPADD:
+    case PSIHB_XSCOM_IRSN:
+        val = psi->regs[offset];
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
+                      offset);
+    }
+    return val;
+}
+
+static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val,
+                              bool mmio)
+{
+    switch (offset) {
+    case PSIHB_XSCOM_FIR_RW:
+    case PSIHB_XSCOM_FIRACT0:
+    case PSIHB_XSCOM_FIRACT1:
+    case PSIHB_XSCOM_SEMR:
+    case PSIHB_XSCOM_DMA_UPADD:
+        psi->regs[offset] = val;
+        break;
+    case PSIHB_XSCOM_FIR_OR:
+        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
+        break;
+    case PSIHB_XSCOM_FIR_AND:
+        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
+        break;
+    case PSIHB_XSCOM_BAR:
+        /* Only XSCOM can write this one */
+        if (!mmio) {
+            pnv_psi_set_bar(psi, val);
+        }
+        break;
+    case PSIHB_XSCOM_FSPBAR:
+        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
+        pnv_psi_update_fsp_mr(psi);
+        break;
+    case PSIHB_XSCOM_CR:
+        pnv_psi_set_cr(psi, val);
+        break;
+    case PSIHB_XSCOM_SCR:
+        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
+        break;
+    case PSIHB_XSCOM_CCR:
+        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
+        break;
+    case PSIHB_XSCOM_XIVR_PSI:
+    case PSIHB_XSCOM_XIVR_OCC:
+    case PSIHB_XSCOM_XIVR_FSI:
+    case PSIHB_XSCOM_XIVR_LPCI2C:
+    case PSIHB_XSCOM_XIVR_LOCERR:
+    case PSIHB_XSCOM_XIVR_EXT:
+        pnv_psi_set_xivr(psi, offset, val);
+        break;
+    case PSIHB_XSCOM_IRQ_STAT:
+        /* Read only, should we generate an error ? */
+        break;
+    case PSIHB_XSCOM_IRSN:
+        pnv_psi_set_irsn(psi, val);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
+                      offset);
+    }
+}
+
+static inline uint32_t psi_mmio_to_xscom(hwaddr addr)
+{
+    return (addr >> 3) + PSIHB_XSCOM_BAR;
+}
+
+static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    return pnv_psi_reg_read(opaque, psi_mmio_to_xscom(addr), true);
+}
+
+static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    pnv_psi_reg_write(opaque, psi_mmio_to_xscom(addr), val, true);
+}
+
+static const MemoryRegionOps psi_mmio_ops = {
+    .read = pnv_psi_mmio_read,
+    .write = pnv_psi_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_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvPsi *psi = PNV_PSI(opaque);
+    uint32_t offset = addr >> 3;
+
+    return pnv_psi_reg_read(psi, offset, false);
+}
+
+static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvPsi *psi = PNV_PSI(opaque);
+    uint32_t offset = addr >> 3;
+
+    pnv_psi_reg_write(psi, offset, val, false);
+}
+
+static const MemoryRegionOps pnv_psi_xscom_ops = {
+    .read = pnv_psi_xscom_read,
+    .write = pnv_psi_xscom_write,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void pnv_psi_init(Object *obj)
+{
+    PnvPsi *psi = PNV_PSI(obj);
+
+    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
+    qdev_set_parent_bus(DEVICE(&psi->ics), sysbus_get_default());
+    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
+}
+
+static void pnv_psi_realize(DeviceState *dev, Error **errp)
+{
+    PnvPsi *psi = PNV_PSI(dev);
+    ICSState *ics = &psi->ics;
+    Object *obj;
+    Error *err = NULL, *local_err = NULL;
+    unsigned int i;
+
+    /* Initialize MMIO region */
+    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
+                          "psihb", PNV_PSIHB_BAR_SIZE);
+
+    /* Default BAR. Use object properties ? */
+    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
+
+    /* Default sources in XIVR */
+    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
+            (0ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
+            (1ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
+            (2ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
+            (3ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
+            (4ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
+            (5ull << PSIHB_XIVR_SRC_SH);
+
+    /* get XICSFabric from chip */
+    obj = object_property_get_link(OBJECT(dev), "xics", &err);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'xics' not found: %s",
+                   __func__, error_get_pretty(err));
+        return;
+    }
+
+    /*
+     * PSI interrupt control source
+     */
+    object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
+    object_property_add_const_link(OBJECT(ics), "xics", obj, &err);
+    object_property_set_bool(OBJECT(ics), true, "realized",  &local_err);
+    error_propagate(&err, local_err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    for (i = 0; i < ics->nr_irqs; i++) {
+        ics_set_irq_type(ics, i, true);
+    }
+
+    /* XScom region for PSI registers */
+    pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
+                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE);
+}
+
+static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+{
+    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
+    char *name;
+    int offset;
+    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
+    uint32_t reg[] = {
+        cpu_to_be32(lpc_pcba),
+        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
+    };
+
+    name = g_strdup_printf("psihb@%x", lpc_pcba);
+    offset = fdt_add_subnode(fdt, xscom_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    _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,
+                      sizeof(compat))));
+    return 0;
+}
+
+
+static void pnv_psi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+    xdc->populate = pnv_psi_populate;
+
+    dc->realize = pnv_psi_realize;
+}
+
+static const TypeInfo pnv_psi_info = {
+    .name          = TYPE_PNV_PSI,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(PnvPsi),
+    .instance_init = pnv_psi_init,
+    .class_init    = pnv_psi_class_init,
+    .interfaces    = (InterfaceInfo[]) {
+        { TYPE_PNV_XSCOM_INTERFACE },
+        { }
+    }
+};
+
+static void pnv_psi_register_types(void)
+{
+    type_register_static(&pnv_psi_info);
+}
+
+type_init(pnv_psi_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index f11215ea31f2..f93ec32603b7 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -23,6 +23,7 @@
 #include "hw/sysbus.h"
 #include "hw/ppc/pnv_lpc.h"
 #include "hw/ppc/xics.h"
+#include "hw/ppc/pnv_psi.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -58,6 +59,7 @@ typedef struct PnvChip {
     MemoryRegion icp_mmio;
 
     PnvLpcController lpc;
+    PnvPsi       psi;
 } PnvChip;
 
 typedef struct PnvChipClass {
@@ -119,6 +121,8 @@ typedef struct PnvMachineState {
     ICPState     *icps;
     uint32_t     nr_servers;
     QLIST_HEAD(, ICSState) ics;
+
+    uint32_t     cpld_irqstate;
 } PnvMachineState;
 
 #define PNV_FDT_ADDR          0x01000000
@@ -150,4 +154,8 @@ typedef struct PnvMachineState {
 #define PNV_ICP_BASE(chip)   0x0003ffff80000000ull
 #define PNV_ICP_SIZE         0x0000000000100000ull
 
+#define PNV_PSIHB_BAR         0x0003fffe80000000ull
+#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
+
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
new file mode 100644
index 000000000000..ac3c5f8362e3
--- /dev/null
+++ b/include/hw/ppc/pnv_psi.h
@@ -0,0 +1,61 @@
+/*
+ * QEMU PowerPC PowerNV PSI controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_PSI_H
+#define _PPC_PNV_PSI_H
+
+#define TYPE_PNV_PSI "pnv-psi"
+#define PNV_PSI(obj) \
+     OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI)
+
+#define PSIHB_XSCOM_MAX         0x20
+
+typedef struct XICSState XICSState;
+
+typedef struct PnvPsi {
+    DeviceState parent;
+
+    MemoryRegion regs_mr;
+
+    /* FSP region not supported */
+    /* MemoryRegion fsp_mr; */
+
+    /* Interrupt generation */
+    ICSState ics;
+
+    /* Registers */
+    uint64_t regs[PSIHB_XSCOM_MAX];
+
+    MemoryRegion xscom_regs;
+} PnvPsi;
+
+typedef enum PnvPsiIrq {
+    PSIHB_IRQ_PSI, /* internal use only */
+    PSIHB_IRQ_FSP, /* internal use only */
+    PSIHB_IRQ_OCC,
+    PSIHB_IRQ_FSI,
+    PSIHB_IRQ_LPC_I2C,
+    PSIHB_IRQ_LOCAL_ERR,
+    PSIHB_IRQ_EXTERNAL,
+} PnvPsiIrq;
+
+#define PSI_NUM_INTERRUPTS 6
+
+extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state);
+
+#endif /* _PPC_PNV_PSI_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 0faa1847bf13..2938abd74955 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
 #define PNV_XSCOM_LPC_BASE        0xb0020
 #define PNV_XSCOM_LPC_SIZE        0x4
 
+#define PNV_XSCOM_PSI_BASE        0x2010900
+#define PNV_XSCOM_PSI_SIZE        0x20
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 7/8] ppc/pnv: Add OCC model stub with interrupt support
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
                   ` (5 preceding siblings ...)
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 8/8] ppc/pnv: Add support for POWER8+ LPC Controller Cédric Le Goater
  7 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson
  Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt, Cédric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The OCC is an on-chip microcontroller based on a ppc405 core used
for various power management tasks. It comes with a pile of additional
hardware sitting on the PIB (aka XSCOM bus). At this point we don't
emulate it (nor plan to do so). However there is one facility which
is provided by the surrounding hardware that we do need, which is the
interrupt generation facility. OPAL uses it to send itself interrupts
under some circumstances and there are other uses around the corner.

So this implement just enough to support this.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.9
      - changed the XSCOM interface to fit new model
      - QOMified the model ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  10 ++++
 hw/ppc/pnv_occ.c           | 136 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   2 +
 include/hw/ppc/pnv_occ.h   |  38 +++++++++++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 190 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_occ.c
 create mode 100644 include/hw/ppc/pnv_occ.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index dc19ee17fa57..ef67ea820158 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 85b00bf235c6..ca5a078bc6c6 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -714,6 +714,11 @@ static void pnv_chip_init(Object *obj)
     object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
     object_property_add_const_link(OBJECT(&chip->psi), "xics",
                                    OBJECT(qdev_get_machine()), &error_abort);
+
+    object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
+    object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
+    object_property_add_const_link(OBJECT(&chip->occ), "psi",
+                                   OBJECT(&chip->psi), &error_abort);
 }
 
 static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
@@ -824,6 +829,11 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
                              &error_fatal);
     pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
+
+    /* Create the simplified OCC model */
+    object_property_set_bool(OBJECT(&chip->occ), true, "realized",
+                             &error_fatal);
+    pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs);
 }
 
 static Property pnv_chip_properties[] = {
diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c
new file mode 100644
index 000000000000..32f167b77ea3
--- /dev/null
+++ b/hw/ppc/pnv_occ.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU PowerNV Emulation of a few OCC related registers
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "target/ppc/cpu.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv_occ.h"
+
+#define OCB_OCI_OCCMISC         0x4020
+#define OCB_OCI_OCCMISC_AND     0x4021
+#define OCB_OCI_OCCMISC_OR      0x4022
+
+static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val)
+{
+    bool irq_state;
+
+    val &= 0xffff000000000000ull;
+
+    occ->occmisc = val;
+    irq_state = !!(val >> 63);
+    pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state);
+}
+
+static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvOCC *occ = PNV_OCC(opaque);
+    uint32_t offset = addr >> 3;
+    uint64_t val = 0;
+
+    switch (offset) {
+    case OCB_OCI_OCCMISC:
+        val = occ->occmisc;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+    return val;
+}
+
+static void pnv_occ_xscom_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvOCC *occ = PNV_OCC(opaque);
+    uint32_t offset = addr >> 3;
+
+    switch (offset) {
+    case OCB_OCI_OCCMISC_AND:
+        pnv_occ_set_misc(occ, occ->occmisc & val);
+        break;
+    case OCB_OCI_OCCMISC_OR:
+        pnv_occ_set_misc(occ, occ->occmisc | val);
+        break;
+    case OCB_OCI_OCCMISC:
+        pnv_occ_set_misc(occ, val);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+}
+
+static const MemoryRegionOps pnv_occ_xscom_ops = {
+    .read = pnv_occ_xscom_read,
+    .write = pnv_occ_xscom_write,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+
+static void pnv_occ_realize(DeviceState *dev, Error **errp)
+{
+    PnvOCC *occ = PNV_OCC(dev);
+    Object *obj;
+    Error *error = NULL;
+
+    occ->occmisc = 0;
+
+    /* get PSI object from chip */
+    obj = object_property_get_link(OBJECT(dev), "psi", &error);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'psi' not found: %s",
+                   __func__, error_get_pretty(error));
+        return;
+    }
+    occ->psi = PNV_PSI(obj);
+
+    /* XScom region for OCC registers */
+    pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops,
+                  occ, "xscom-occ", PNV_XSCOM_OCC_SIZE);
+}
+
+static void pnv_occ_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pnv_occ_realize;
+}
+
+static const TypeInfo pnv_occ_type_info = {
+    .name          = TYPE_PNV_OCC,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(PnvOCC),
+    .class_init    = pnv_occ_class_init,
+};
+
+static void pnv_occ_register_types(void)
+{
+    type_register_static(&pnv_occ_type_info);
+}
+
+type_init(pnv_occ_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index f93ec32603b7..481f276699fa 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -24,6 +24,7 @@
 #include "hw/ppc/pnv_lpc.h"
 #include "hw/ppc/xics.h"
 #include "hw/ppc/pnv_psi.h"
+#include "hw/ppc/pnv_occ.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -60,6 +61,7 @@ typedef struct PnvChip {
 
     PnvLpcController lpc;
     PnvPsi       psi;
+    PnvOCC       occ;
 } PnvChip;
 
 typedef struct PnvChipClass {
diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h
new file mode 100644
index 000000000000..94c9b6fe1384
--- /dev/null
+++ b/include/hw/ppc/pnv_occ.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU PowerNV Emulation of a few OCC related registers
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_OCC_H
+#define _PPC_PNV_OCC_H
+
+#define TYPE_PNV_OCC "pnv-occ"
+#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC)
+
+typedef struct PnvPsi PnvPsi;
+
+typedef struct PnvOCC {
+    DeviceState xd;
+
+    /* OCC Misc interrupt */
+    uint64_t occmisc;
+
+    PnvPsi *psi;
+
+    MemoryRegion xscom_regs;
+} PnvOCC;
+
+#endif /* _PPC_PNV_OCC_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 2938abd74955..226ff61900de 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -63,6 +63,9 @@ typedef struct PnvXScomInterfaceClass {
 #define PNV_XSCOM_PSI_BASE        0x2010900
 #define PNV_XSCOM_PSI_SIZE        0x20
 
+#define PNV_XSCOM_OCC_BASE        0x0066000
+#define PNV_XSCOM_OCC_SIZE        0x6000
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [Qemu-devel] [PATCH for-2.10 8/8] ppc/pnv: Add support for POWER8+ LPC Controller
  2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
                   ` (6 preceding siblings ...)
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 7/8] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
@ 2017-03-08 10:52 ` Cédric Le Goater
  7 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 10:52 UTC (permalink / raw)
  To: David Gibson
  Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt, Cédric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

It adds the Naples chip which supports proper LPC interrupts via the
LPC controller rather than via an external CPLD.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.9
      - ported on latest PowerNV patchset ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
---
 hw/ppc/pnv.c             | 15 ++++++++++++++-
 hw/ppc/pnv_lpc.c         | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 include/hw/ppc/pnv_lpc.h |  9 +++++++++
 3 files changed, 68 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index ca5a078bc6c6..65dfe5b4c97b 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -376,7 +376,14 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
 
 static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
 {
-     /* XXX TODO */
+    PnvChip *chip = opaque;
+    PnvLpcController *lpc = &chip->lpc;
+
+    /* The Naples HW latches the 1 levels, clearing is done by SW */
+    if (level) {
+        lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
+        pnv_lpc_eval_irqs(lpc);
+    }
 }
 
 static ISABus *pnv_isa_create(PnvChip *chip)
@@ -719,6 +726,12 @@ static void pnv_chip_init(Object *obj)
     object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
     object_property_add_const_link(OBJECT(&chip->occ), "psi",
                                    OBJECT(&chip->psi), &error_abort);
+
+    /*
+     * The LPC controller needs PSI to generate interrupts
+     */
+    object_property_add_const_link(OBJECT(&chip->lpc), "psi",
+                                   OBJECT(&chip->psi), &error_abort);
 }
 
 static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 78db52415b11..20cbb6a0dbbd 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -250,6 +250,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
+void pnv_lpc_eval_irqs(PnvLpcController *lpc)
+{
+    bool lpc_to_opb_irq = false;
+
+    /* Update LPC controller to OPB line */
+    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
+        uint32_t irqs;
+
+        irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
+        lpc_to_opb_irq = (irqs != 0);
+    }
+
+    /* We don't honor the polarity register, it's pointless and unused
+     * anyway
+     */
+    if (lpc_to_opb_irq) {
+        lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
+    } else {
+        lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
+    }
+
+    /* Update OPB internal latch */
+    lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
+
+    /* Reflect the interrupt */
+    pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
+}
+
 static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
 {
     PnvLpcController *lpc = opaque;
@@ -300,12 +328,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
         break;
     case LPC_HC_IRQSER_CTRL:
         lpc->lpc_hc_irqser_ctrl = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_IRQMASK:
         lpc->lpc_hc_irqmask = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_IRQSTAT:
         lpc->lpc_hc_irqstat &= ~val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_ERROR_ADDRESS:
         break;
@@ -363,14 +394,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
     switch (addr) {
     case OPB_MASTER_LS_IRQ_STAT:
         lpc->opb_irq_stat &= ~val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_MASK:
-        /* XXX Filter out reserved bits */
         lpc->opb_irq_mask = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_POL:
-        /* XXX Filter out reserved bits */
         lpc->opb_irq_pol = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_INPUT:
         /* Read only */
@@ -398,6 +430,8 @@ static const MemoryRegionOps opb_master_ops = {
 static void pnv_lpc_realize(DeviceState *dev, Error **errp)
 {
     PnvLpcController *lpc = PNV_LPC(dev);
+    Object *obj;
+    Error *error = NULL;
 
     /* Reg inits */
     lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
@@ -441,6 +475,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
     pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
                           &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
                           PNV_XSCOM_LPC_SIZE);
+
+    /* get PSI object from chip */
+    obj = object_property_get_link(OBJECT(dev), "psi", &error);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'psi' not found: %s",
+                   __func__, error_get_pretty(error));
+        return;
+    }
+    lpc->psi = PNV_PSI(obj);
 }
 
 static void pnv_lpc_class_init(ObjectClass *klass, void *data)
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index 38e5506975aa..53040026c37b 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -23,6 +23,8 @@
 #define PNV_LPC(obj) \
      OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
 
+typedef struct PnvPsi PnvPsi;
+
 typedef struct PnvLpcController {
     DeviceState parent;
 
@@ -62,6 +64,13 @@ typedef struct PnvLpcController {
 
     /* XSCOM registers */
     MemoryRegion xscom_regs;
+
+    /* PSI to generate interrupts */
+    PnvPsi *psi;
 } PnvLpcController;
 
+#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
+
+void pnv_lpc_eval_irqs(PnvLpcController *lpc);
+
 #endif /* _PPC_PNV_LPC_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers Cédric Le Goater
@ 2017-03-08 11:24   ` Philippe Mathieu-Daudé
  2017-03-08 13:33     ` Cédric Le Goater
  2017-03-14  5:49   ` David Gibson
  1 sibling, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2017-03-08 11:24 UTC (permalink / raw)
  To: Cédric Le Goater, David Gibson; +Cc: qemu-ppc, qemu-devel

Hi Cédric,

On 03/08/2017 07:52 AM, Cédric Le Goater wrote:
> This provides to a PowerNV chip (POWER8) access to the Interrupt
> Management area, which contains the registers of the Interrupt Control
> Presenters of each thread. These are used to accept, return, forward
> interrupts in the system.
>
> This area is modeled with a per-chip container memory region holding
> all the ICP registers. Each thread of a chip is then associated with
> its ICP registers using a memory subregion indexed by its PIR number
> in the overall region.
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c              |  20 +++++++
>  hw/ppc/pnv_core.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h      |  20 +++++++
>  include/hw/ppc/pnv_core.h |   1 +
>  include/hw/ppc/xics.h     |   3 +
>  5 files changed, 190 insertions(+)
>
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 461d3535e99c..7b13b08deadf 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -658,6 +658,16 @@ 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);
> +    g_free(name);
> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
> @@ -680,6 +690,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      }
>      sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
>
> +    /* Interrupt Management Area. This is the memory region holding
> +     * all the Interrupt Control Presenter (ICP) registers */
> +    pnv_chip_icp_realize(chip, &error);
> +    if (error) {
> +        error_propagate(errp, error);
> +        return;
> +    }
> +
>      /* Cores */
>      pnv_chip_core_sanitize(chip, &error);
>      if (error) {
> @@ -709,6 +727,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          object_property_set_int(OBJECT(pnv_core),
>                                  pcc->core_pir(chip, core_hwid),
>                                  "pir", &error_fatal);
> +        object_property_add_const_link(OBJECT(pnv_core), "xics",
> +                                       qdev_get_machine(), &error_fatal);
>          object_property_set_bool(OBJECT(pnv_core), true, "realized",
>                                   &error_fatal);
>          object_unref(OBJECT(pnv_core));
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index d79d530b4881..8633afbff795 100644
> --- a/hw/ppc/pnv_core.c
> +++ b/hw/ppc/pnv_core.c
> @@ -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);

hard to read, an inline function should produce the same code at be more 
easily reviewable.

> +    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(XICSFabric *xi, int pir)
> +{
> +    int index = xics_get_cpu_index_by_pir(pir);
> +    assert(index != -1);
> +
> +    return xics_icp_get(xi, index);
> +}
> +
>  static void pnv_core_realize(DeviceState *dev, Error **errp)
>  {
>      PnvCore *pc = PNV_CORE(OBJECT(dev));
> @@ -140,6 +270,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>      void *obj;
>      int i, j;
>      char name[32];
> +    Object *xi;
> +
> +    xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
> +    if (!xi) {
> +        error_setg(errp, "%s: required link 'xics' not found: %s",
> +                   __func__, error_get_pretty(local_err));
> +        return;
> +    }
>
>      pc->threads = g_malloc0(size * cc->nr_threads);
>      for (i = 0; i < cc->nr_threads; i++) {
> @@ -169,6 +307,14 @@ 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);
> +
> +    pc->icp_mmios = g_new0(MemoryRegion, cc->nr_threads);
> +    for (i = 0; i < cc->nr_threads; i++) {
> +        ICPState *icp = xics_get_icp_per_pir(XICS_FABRIC(xi), 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:
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 6a0b004cea93..f11215ea31f2 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -55,6 +55,7 @@ typedef struct PnvChip {
>      MemoryRegion xscom_mmio;
>      MemoryRegion xscom;
>      AddressSpace xscom_as;
> +    MemoryRegion icp_mmio;
>
>      PnvLpcController lpc;
>  } PnvChip;
> @@ -130,4 +131,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 */
> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
> index 2955a41c901f..f2fad8f6361b 100644
> --- a/include/hw/ppc/pnv_core.h
> +++ b/include/hw/ppc/pnv_core.h
> @@ -38,6 +38,7 @@ typedef struct PnvCore {
>      uint32_t pir;
>
>      MemoryRegion xscom_regs;
> +    MemoryRegion *icp_mmios;
>  } PnvCore;
>
>  typedef struct PnvCoreClass {
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index c2032cac55f6..a3dcdf93bbe3 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -78,6 +78,9 @@ struct ICPState {
>      bool cap_irq_xics_enabled;
>
>      XICSFabric *xics;
> +
> +    /* for the PowerNV ICP registers (not used by Linux). */
> +    uint32_t links[3];
>  };
>
>  #define TYPE_ICS_BASE "ics-base"
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers
  2017-03-08 11:24   ` Philippe Mathieu-Daudé
@ 2017-03-08 13:33     ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-08 13:33 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/08/2017 12:24 PM, Philippe Mathieu-Daudé wrote:
> Hi Cédric,
> 
> On 03/08/2017 07:52 AM, Cédric Le Goater wrote:
>> This provides to a PowerNV chip (POWER8) access to the Interrupt
>> Management area, which contains the registers of the Interrupt Control
>> Presenters of each thread. These are used to accept, return, forward
>> interrupts in the system.
>>
>> This area is modeled with a per-chip container memory region holding
>> all the ICP registers. Each thread of a chip is then associated with
>> its ICP registers using a memory subregion indexed by its PIR number
>> in the overall region.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c              |  20 +++++++
>>  hw/ppc/pnv_core.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h      |  20 +++++++
>>  include/hw/ppc/pnv_core.h |   1 +
>>  include/hw/ppc/xics.h     |   3 +
>>  5 files changed, 190 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 461d3535e99c..7b13b08deadf 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -658,6 +658,16 @@ 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);
>> +    g_free(name);
>> +}
>> +
>>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>> @@ -680,6 +690,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>      }
>>      sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
>>
>> +    /* Interrupt Management Area. This is the memory region holding
>> +     * all the Interrupt Control Presenter (ICP) registers */
>> +    pnv_chip_icp_realize(chip, &error);
>> +    if (error) {
>> +        error_propagate(errp, error);
>> +        return;
>> +    }
>> +
>>      /* Cores */
>>      pnv_chip_core_sanitize(chip, &error);
>>      if (error) {
>> @@ -709,6 +727,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          object_property_set_int(OBJECT(pnv_core),
>>                                  pcc->core_pir(chip, core_hwid),
>>                                  "pir", &error_fatal);
>> +        object_property_add_const_link(OBJECT(pnv_core), "xics",
>> +                                       qdev_get_machine(), &error_fatal);
>>          object_property_set_bool(OBJECT(pnv_core), true, "realized",
>>                                   &error_fatal);
>>          object_unref(OBJECT(pnv_core));
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> index d79d530b4881..8633afbff795 100644
>> --- a/hw/ppc/pnv_core.c
>> +++ b/hw/ppc/pnv_core.c
>> @@ -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);
> 
> hard to read, an inline function should produce the same code at be more easily reviewable.

true. I can improve that if a resend is needed.

Thanks,

C.

>> +    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(XICSFabric *xi, int pir)
>> +{
>> +    int index = xics_get_cpu_index_by_pir(pir);
>> +    assert(index != -1);
>> +
>> +    return xics_icp_get(xi, index);
>> +}
>> +
>>  static void pnv_core_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvCore *pc = PNV_CORE(OBJECT(dev));
>> @@ -140,6 +270,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>>      void *obj;
>>      int i, j;
>>      char name[32];
>> +    Object *xi;
>> +
>> +    xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
>> +    if (!xi) {
>> +        error_setg(errp, "%s: required link 'xics' not found: %s",
>> +                   __func__, error_get_pretty(local_err));
>> +        return;
>> +    }
>>
>>      pc->threads = g_malloc0(size * cc->nr_threads);
>>      for (i = 0; i < cc->nr_threads; i++) {
>> @@ -169,6 +307,14 @@ 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);
>> +
>> +    pc->icp_mmios = g_new0(MemoryRegion, cc->nr_threads);
>> +    for (i = 0; i < cc->nr_threads; i++) {
>> +        ICPState *icp = xics_get_icp_per_pir(XICS_FABRIC(xi), 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:
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index 6a0b004cea93..f11215ea31f2 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -55,6 +55,7 @@ typedef struct PnvChip {
>>      MemoryRegion xscom_mmio;
>>      MemoryRegion xscom;
>>      AddressSpace xscom_as;
>> +    MemoryRegion icp_mmio;
>>
>>      PnvLpcController lpc;
>>  } PnvChip;
>> @@ -130,4 +131,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 */
>> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
>> index 2955a41c901f..f2fad8f6361b 100644
>> --- a/include/hw/ppc/pnv_core.h
>> +++ b/include/hw/ppc/pnv_core.h
>> @@ -38,6 +38,7 @@ typedef struct PnvCore {
>>      uint32_t pir;
>>
>>      MemoryRegion xscom_regs;
>> +    MemoryRegion *icp_mmios;
>>  } PnvCore;
>>
>>  typedef struct PnvCoreClass {
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index c2032cac55f6..a3dcdf93bbe3 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -78,6 +78,9 @@ struct ICPState {
>>      bool cap_irq_xics_enabled;
>>
>>      XICSFabric *xics;
>> +
>> +    /* for the PowerNV ICP registers (not used by Linux). */
>> +    uint32_t links[3];
>>  };
>>
>>  #define TYPE_ICS_BASE "ics-base"
>>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper Cédric Le Goater
@ 2017-03-14  5:38   ` David Gibson
  2017-03-14  8:11     ` Cédric Le Goater
  2017-03-14 17:00     ` Cédric Le Goater
  0 siblings, 2 replies; 29+ messages in thread
From: David Gibson @ 2017-03-14  5:38 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3202 bytes --]

On Wed, Mar 08, 2017 at 11:52:44AM +0100, Cédric Le Goater wrote:
> This helper will be used to translate the server number of the XIVE
> (which is a PIR) into an ICPState index number (which is a cpu index).
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

This seems a slightly roundabout way of doing things.  Why not just
have the vcpu_by_pir() interface, then have the XICSFabric implementor
go directly from PIR to xics server state.

> ---
>  hw/intc/xics.c        | 11 +++++++++++
>  hw/ppc/ppc.c          | 16 ++++++++++++++++
>  include/hw/ppc/xics.h |  1 +
>  target/ppc/cpu.h      | 10 ++++++++++
>  4 files changed, 38 insertions(+)
> 
> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
> index e740989a1162..209e1a75ecb9 100644
> --- a/hw/intc/xics.c
> +++ b/hw/intc/xics.c
> @@ -49,6 +49,17 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>      return -1;
>  }
>  
> +int xics_get_cpu_index_by_pir(int pir)
> +{
> +    PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir);
> +
> +    if (cpu) {
> +        return cpu->parent_obj.cpu_index;
> +    }
> +
> +    return -1;
> +}
> +
>  void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
>  {
>      CPUState *cs = CPU(cpu);
> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> index 5f93083d4a16..94bbe382a73a 100644
> --- a/hw/ppc/ppc.c
> +++ b/hw/ppc/ppc.c
> @@ -1379,6 +1379,22 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id)
>      return NULL;
>  }
>  
> +PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
> +{
> +    CPUState *cs;
> +
> +    CPU_FOREACH(cs) {
> +        PowerPCCPU *cpu = POWERPC_CPU(cs);
> +        CPUPPCState *env = &cpu->env;
> +
> +        if (env->spr_cb[SPR_PIR].default_value == pir) {
> +            return cpu;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
>  void ppc_cpu_parse_features(const char *cpu_model)
>  {
>      CPUClass *cc;
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index 9a5e715fe553..42bd24e975cb 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -173,6 +173,7 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu);
>  
>  /* Internal XICS interfaces */
>  int xics_get_cpu_index_by_dt_id(int cpu_dt_id);
> +int xics_get_cpu_index_by_pir(int pir);
>  
>  void icp_set_cppr(ICPState *icp, uint8_t cppr);
>  void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
> diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
> index 7c4a1f50b38b..24a5af95cb45 100644
> --- a/target/ppc/cpu.h
> +++ b/target/ppc/cpu.h
> @@ -2518,5 +2518,15 @@ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu);
>   */
>  PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id);
>  
> +/**
> + * ppc_get_vcpu_by_pir_id:
> + * @pir: Processor Identifier Register (SPR_PIR)
> + *
> + * Searches for a CPU by @pir.
> + *
> + * Returns: a PowerPCCPU struct
> + */
> +PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
> +
>  void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len);
>  #endif /* PPC_CPU_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric Cédric Le Goater
@ 2017-03-14  5:40   ` David Gibson
  2017-03-14  8:12     ` Cédric Le Goater
  0 siblings, 1 reply; 29+ messages in thread
From: David Gibson @ 2017-03-14  5:40 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3599 bytes --]

On Wed, Mar 08, 2017 at 11:52:45AM +0100, Cédric Le Goater wrote:
> This handler will be required by PowerPC machines using multiple ICS
> objects, like this is the case for PowerNV. Also update the sPAPR
> machine to use the new handler.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

I don't see why you need a new callback for this.  In this and the
subsequent patches, it looks like the callback is always just use the
fabric's ics_get() to find the right ICS state, then call ics_eoi() on
it.


> ---
>  hw/intc/xics.c        |  9 +++------
>  hw/ppc/spapr.c        | 11 +++++++++++
>  include/hw/ppc/xics.h |  2 ++
>  3 files changed, 16 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
> index 209e1a75ecb9..e6fecd6e1a89 100644
> --- a/hw/intc/xics.c
> +++ b/hw/intc/xics.c
> @@ -169,7 +169,7 @@ void ics_resend(ICSState *ics)
>      }
>  }
>  
> -static void ics_eoi(ICSState *ics, int nr)
> +void ics_eoi(ICSState *ics, int nr)
>  {
>      ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
>  
> @@ -268,7 +268,6 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
>  {
>      XICSFabric *xi = icp->xics;
>      XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi);
> -    ICSState *ics;
>      uint32_t irq;
>  
>      /* Send EOI -> ICS */
> @@ -276,10 +275,8 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
>      trace_xics_icp_eoi(icp->cs->cpu_index, xirr, icp->xirr);
>      irq = xirr & XISR_MASK;
>  
> -    ics = xic->ics_get(xi, irq);
> -    if (ics) {
> -        ics_eoi(ics, irq);
> -    }
> +    xic->ics_eoi(xi, irq);
> +
>      if (!XISR(icp)) {
>          icp_resend(icp);
>      }
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index c3bb99160545..043629cc5c54 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -3024,6 +3024,16 @@ static void spapr_ics_resend(XICSFabric *dev)
>      ics_resend(spapr->ics);
>  }
>  
> +static void spapr_ics_eoi(XICSFabric *xi, int irq)
> +{
> +    ICSState *ics;
> +
> +    ics = spapr_ics_get(xi, irq);
> +    if (ics) {
> +        ics_eoi(ics, irq);
> +    }
> +}
> +
>  static ICPState *spapr_icp_get(XICSFabric *xi, int server)
>  {
>      sPAPRMachineState *spapr = SPAPR_MACHINE(xi);
> @@ -3094,6 +3104,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
>      vhc->get_patbe = spapr_get_patbe;
>      xic->ics_get = spapr_ics_get;
>      xic->ics_resend = spapr_ics_resend;
> +    xic->ics_eoi = spapr_ics_eoi;
>      xic->icp_get = spapr_icp_get;
>      ispc->print_info = spapr_pic_print_info;
>  }
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index 42bd24e975cb..00b003b2392d 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -155,6 +155,7 @@ typedef struct XICSFabricClass {
>      InterfaceClass parent;
>      ICSState *(*ics_get)(XICSFabric *xi, int irq);
>      void (*ics_resend)(XICSFabric *xi);
> +    void (*ics_eoi)(XICSFabric *xi, int irq);
>      ICPState *(*icp_get)(XICSFabric *xi, int server);
>  } XICSFabricClass;
>  
> @@ -189,6 +190,7 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon);
>  void ics_pic_print_info(ICSState *ics, Monitor *mon);
>  
>  void ics_resend(ICSState *ics);
> +void ics_eoi(ICSState *ics, int irq);
>  void icp_resend(ICPState *ss);
>  
>  typedef struct sPAPRMachineState sPAPRMachineState;

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine Cédric Le Goater
@ 2017-03-14  5:45   ` David Gibson
  2017-03-14  9:47     ` Cédric Le Goater
  0 siblings, 1 reply; 29+ messages in thread
From: David Gibson @ 2017-03-14  5:45 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 7722 bytes --]

On Wed, Mar 08, 2017 at 11:52:46AM +0100, Cédric Le Goater wrote:
> Like this is done for the sPAPR machine, we use a simple array under
> the PowerNV machine to store the Interrupt Control Presenters (ICP)
> objects, one for each vCPU. This array is indexed by 'cpu_index' of
> the CPUState.
> 
> We use a list to hold the different Interrupt Control Sources (ICS)
> objects, as PowerNV needs to handle multiple sources: for PCI-E and
> for the Processor Service Interface (PSI).
> 
> Finally, to interface with the XICS layer which manipulates the ICP
> and ICS objects, we extend the PowerNV machine with an XICSFabric
> interface and its associated handlers.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c          | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h  |  4 +++
>  include/hw/ppc/xics.h |  1 +
>  3 files changed, 94 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 09f0d22defb8..461d3535e99c 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -32,6 +32,8 @@
>  #include "exec/address-spaces.h"
>  #include "qemu/cutils.h"
>  #include "qapi/visitor.h"
> +#include "monitor/monitor.h"
> +#include "hw/intc/intc.h"
>  
>  #include "hw/ppc/pnv_xscom.h"
>  
> @@ -416,6 +418,23 @@ static void ppc_powernv_init(MachineState *machine)
>          machine->cpu_model = "POWER8";
>      }
>  
> +    /* Create the Interrupt Control Presenters before the vCPUs */
> +    pnv->nr_servers = pnv->num_chips * smp_cores * smp_threads;
> +    pnv->icps = g_new0(ICPState, pnv->nr_servers);
> +    for (i = 0; i < pnv->nr_servers; i++) {
> +        ICPState *icp = &pnv->icps[i];
> +        object_initialize(icp, sizeof(*icp), TYPE_ICP);
> +        qdev_set_parent_bus(DEVICE(icp), sysbus_get_default());

Your fixup for the PAPR case suggests this is not the right approach
to the device initialization.

> +        object_property_add_child(OBJECT(pnv), "icp[*]", OBJECT(icp),
> +                                  &error_fatal);

This isn't an urgent problem, but I dislike using the icp[*]
behaviour as a rule (it's fixed now, but it just to be you could
easily get O(n^3) behaviour using it).  In this case you already have
a handle on a specific id for the object.

As well as being a bit less expensive, using an explicit index means
that the exposed QOM address will definitely match an otherwise
meaningful id, rather than just lining up by accident if all the
orders remain the same.

> +        object_property_add_const_link(OBJECT(icp), "xics", OBJECT(pnv),
> +                                       &error_fatal);
> +        object_property_set_bool(OBJECT(icp), true, "realized", &error_fatal);
> +    }
> +
> +    /* and the list of Interrupt Control Sources */
> +    QLIST_INIT(&pnv->ics);
> +
>      /* Create the processor chips */
>      chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
>      if (!object_class_by_name(chip_typename)) {
> @@ -742,6 +761,48 @@ static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
>      visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp);
>  }
>  
> +static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
> +    ICSState *ics;
> +
> +    QLIST_FOREACH(ics, &pnv->ics, list) {
> +        if (ics_valid_irq(ics, irq)) {
> +            return ics;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static void pnv_ics_resend(XICSFabric *xi)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
> +    ICSState *ics;
> +
> +    QLIST_FOREACH(ics, &pnv->ics, list) {
> +        ics_resend(ics);
> +    }
> +}
> +
> +static void pnv_ics_eoi(XICSFabric *xi, int irq)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
> +    ICSState *ics;
> +
> +    QLIST_FOREACH(ics, &pnv->ics, list) {
> +        if (ics_valid_irq(ics, irq)) {

Unless something is badly wrong, this should only return true for
exactly one iteration through the loop.  Which makes this equivalent
to ics_get() followed by ics_eoi().

> +            ics_eoi(ics, irq);
> +        }
> +    }
> +}
> +
> +static ICPState *pnv_icp_get(XICSFabric *xi, int server)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
> +
> +    return (server < pnv->nr_servers) ? &pnv->icps[server] : NULL;
> +}
> +
>  static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name,
>                                void *opaque, Error **errp)
>  {
> @@ -783,9 +844,27 @@ static void powernv_machine_class_props_init(ObjectClass *oc)
>                                NULL);
>  }
>  
> +static void pnv_pic_print_info(InterruptStatsProvider *obj,
> +                               Monitor *mon)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
> +    ICSState *ics;
> +    int i;
> +
> +    for (i = 0; i < pnv->nr_servers; i++) {
> +        icp_pic_print_info(&pnv->icps[i], mon);
> +    }
> +
> +    QLIST_FOREACH(ics, &pnv->ics, list) {
> +        ics_pic_print_info(ics, mon);
> +    }
> +}
> +
>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
>  {
>      MachineClass *mc = MACHINE_CLASS(oc);
> +    XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
> +    InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
>  
>      mc->desc = "IBM PowerNV (Non-Virtualized)";
>      mc->init = ppc_powernv_init;
> @@ -796,6 +875,11 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data)
>      mc->no_parallel = 1;
>      mc->default_boot_order = NULL;
>      mc->default_ram_size = 1 * G_BYTE;
> +    xic->icp_get = pnv_icp_get;
> +    xic->ics_get = pnv_ics_get;
> +    xic->ics_eoi = pnv_ics_eoi;
> +    xic->ics_resend = pnv_ics_resend;
> +    ispc->print_info = pnv_pic_print_info;
>  
>      powernv_machine_class_props_init(oc);
>  }
> @@ -806,6 +890,11 @@ static const TypeInfo powernv_machine_info = {
>      .instance_size = sizeof(PnvMachineState),
>      .instance_init = powernv_machine_initfn,
>      .class_init    = powernv_machine_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_XICS_FABRIC },
> +        { TYPE_INTERRUPT_STATS_PROVIDER },
> +        { },
> +    },
>  };
>  
>  static void powernv_machine_register_types(void)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index df98a72006e4..6a0b004cea93 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -22,6 +22,7 @@
>  #include "hw/boards.h"
>  #include "hw/sysbus.h"
>  #include "hw/ppc/pnv_lpc.h"
> +#include "hw/ppc/xics.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -114,6 +115,9 @@ typedef struct PnvMachineState {
>      PnvChip      **chips;
>  
>      ISABus       *isa_bus;
> +    ICPState     *icps;
> +    uint32_t     nr_servers;
> +    QLIST_HEAD(, ICSState) ics;
>  } PnvMachineState;
>  
>  #define PNV_FDT_ADDR          0x01000000
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index 00b003b2392d..c2032cac55f6 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -115,6 +115,7 @@ struct ICSState {
>      qemu_irq *qirqs;
>      ICSIRQState *irqs;
>      XICSFabric *xics;
> +    QLIST_ENTRY(ICSState) list;
>  };
>  
>  static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers Cédric Le Goater
  2017-03-08 11:24   ` Philippe Mathieu-Daudé
@ 2017-03-14  5:49   ` David Gibson
  1 sibling, 0 replies; 29+ messages in thread
From: David Gibson @ 2017-03-14  5:49 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 10626 bytes --]

On Wed, Mar 08, 2017 at 11:52:47AM +0100, Cédric Le Goater wrote:
> This provides to a PowerNV chip (POWER8) access to the Interrupt
> Management area, which contains the registers of the Interrupt Control
> Presenters of each thread. These are used to accept, return, forward
> interrupts in the system.
> 
> This area is modeled with a per-chip container memory region holding
> all the ICP registers. Each thread of a chip is then associated with
> its ICP registers using a memory subregion indexed by its PIR number
> in the overall region.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Putting the MMIO stuff into the PNV code seems wrong.  Instead I'd
expect a subclass of TYPE_ICP which implements the MMIO stuff locally,
and exposes an already construct MR, which the pnv code can then
insert into the overall address space.

> ---
>  hw/ppc/pnv.c              |  20 +++++++
>  hw/ppc/pnv_core.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h      |  20 +++++++
>  include/hw/ppc/pnv_core.h |   1 +
>  include/hw/ppc/xics.h     |   3 +
>  5 files changed, 190 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 461d3535e99c..7b13b08deadf 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -658,6 +658,16 @@ 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);
> +    g_free(name);
> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
> @@ -680,6 +690,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      }
>      sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
>  
> +    /* Interrupt Management Area. This is the memory region holding
> +     * all the Interrupt Control Presenter (ICP) registers */
> +    pnv_chip_icp_realize(chip, &error);
> +    if (error) {
> +        error_propagate(errp, error);
> +        return;
> +    }
> +
>      /* Cores */
>      pnv_chip_core_sanitize(chip, &error);
>      if (error) {
> @@ -709,6 +727,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          object_property_set_int(OBJECT(pnv_core),
>                                  pcc->core_pir(chip, core_hwid),
>                                  "pir", &error_fatal);
> +        object_property_add_const_link(OBJECT(pnv_core), "xics",
> +                                       qdev_get_machine(), &error_fatal);
>          object_property_set_bool(OBJECT(pnv_core), true, "realized",
>                                   &error_fatal);
>          object_unref(OBJECT(pnv_core));
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index d79d530b4881..8633afbff795 100644
> --- a/hw/ppc/pnv_core.c
> +++ b/hw/ppc/pnv_core.c
> @@ -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(XICSFabric *xi, int pir)
> +{
> +    int index = xics_get_cpu_index_by_pir(pir);
> +    assert(index != -1);
> +
> +    return xics_icp_get(xi, index);
> +}
> +
>  static void pnv_core_realize(DeviceState *dev, Error **errp)
>  {
>      PnvCore *pc = PNV_CORE(OBJECT(dev));
> @@ -140,6 +270,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>      void *obj;
>      int i, j;
>      char name[32];
> +    Object *xi;
> +
> +    xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
> +    if (!xi) {
> +        error_setg(errp, "%s: required link 'xics' not found: %s",
> +                   __func__, error_get_pretty(local_err));
> +        return;
> +    }
>  
>      pc->threads = g_malloc0(size * cc->nr_threads);
>      for (i = 0; i < cc->nr_threads; i++) {
> @@ -169,6 +307,14 @@ 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);
> +
> +    pc->icp_mmios = g_new0(MemoryRegion, cc->nr_threads);
> +    for (i = 0; i < cc->nr_threads; i++) {
> +        ICPState *icp = xics_get_icp_per_pir(XICS_FABRIC(xi), 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:
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 6a0b004cea93..f11215ea31f2 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -55,6 +55,7 @@ typedef struct PnvChip {
>      MemoryRegion xscom_mmio;
>      MemoryRegion xscom;
>      AddressSpace xscom_as;
> +    MemoryRegion icp_mmio;
>  
>      PnvLpcController lpc;
>  } PnvChip;
> @@ -130,4 +131,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 */
> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
> index 2955a41c901f..f2fad8f6361b 100644
> --- a/include/hw/ppc/pnv_core.h
> +++ b/include/hw/ppc/pnv_core.h
> @@ -38,6 +38,7 @@ typedef struct PnvCore {
>      uint32_t pir;
>  
>      MemoryRegion xscom_regs;
> +    MemoryRegion *icp_mmios;
>  } PnvCore;
>  
>  typedef struct PnvCoreClass {
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index c2032cac55f6..a3dcdf93bbe3 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -78,6 +78,9 @@ struct ICPState {
>      bool cap_irq_xics_enabled;
>  
>      XICSFabric *xics;
> +
> +    /* for the PowerNV ICP registers (not used by Linux). */
> +    uint32_t links[3];
>  };
>  
>  #define TYPE_ICS_BASE "ics-base"

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions Cédric Le Goater
@ 2017-03-14  5:52   ` David Gibson
  2017-03-14 10:02     ` Cédric Le Goater
  0 siblings, 1 reply; 29+ messages in thread
From: David Gibson @ 2017-03-14  5:52 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6845 bytes --]

On Wed, Mar 08, 2017 at 11:52:48AM +0100, Cédric Le Goater wrote:
> and populate the device tree accordingly for the guest to start using
> interrupts. This also links the ICP object to its associated CPUState
> (only used by KVM to control the kernel vCPU).
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c      | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ppc/pnv_core.c | 12 ++++++++----
>  2 files changed, 63 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 7b13b08deadf..0ae11cc3a2ca 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -35,6 +35,7 @@
>  #include "monitor/monitor.h"
>  #include "hw/intc/intc.h"
>  
> +#include "hw/ppc/xics.h"
>  #include "hw/ppc/pnv_xscom.h"
>  
>  #include "hw/isa/isa.h"
> @@ -216,6 +217,47 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
>                         servers_prop, sizeof(servers_prop))));
>  }
>  
> +static void powernv_populate_icp(PnvChip *chip, void *fdt, int offset,
> +                          uint32_t pir, uint32_t count)
> +{
> +    uint64_t addr;
> +    char *name;
> +    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
> +    uint32_t irange[2], i, rsize;
> +    uint64_t *reg;
> +
> +    /*
> +     * TODO: add multichip ICP BAR
> +     */
> +    addr = PNV_ICP_BASE(chip) | (pir << 12);
> +
> +    irange[0] = cpu_to_be32(pir);
> +    irange[1] = cpu_to_be32(count);
> +
> +    rsize = sizeof(uint64_t) * 2 * count;
> +    reg = g_malloc(rsize);
> +    for (i = 0; i < count; i++) {
> +        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
> +        reg[i * 2 + 1] = cpu_to_be64(0x1000);
> +    }
> +
> +    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
> +    offset = fdt_add_subnode(fdt, offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
> +    _FDT((fdt_setprop_string(fdt, offset, "device_type",
> +                              "PowerPC-External-Interrupt-Presentation")));
> +    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
> +    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
> +                       irange, sizeof(irange))));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
> +    g_free(reg);
> +}
> +
>  static void powernv_populate_chip(PnvChip *chip, void *fdt)
>  {
>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> @@ -229,6 +271,10 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>  
>          powernv_create_core_node(chip, pnv_core, fdt);
> +
> +        /* Interrupt Control Presenters (ICP). One per thread. */
> +        powernv_populate_icp(chip, fdt, 0, pnv_core->pir,

All the xicp nodes under the root bus, is that correct?

> +                             CPU_CORE(pnv_core)->nr_threads);
>      }
>  
>      if (chip->ram_size) {
> @@ -697,6 +743,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          error_propagate(errp, error);
>          return;
>      }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
>  
>      /* Cores */
>      pnv_chip_core_sanitize(chip, &error);
> @@ -711,6 +758,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>               && (i < chip->nr_cores); core_hwid++) {
>          char core_name[32];
>          void *pnv_core = chip->cores + i * typesize;
> +        int j;
>  
>          if (!(chip->cores_mask & (1ull << core_hwid))) {
>              continue;
> @@ -738,6 +786,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>                                  PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
>                                                         core_hwid),
>                                  &PNV_CORE(pnv_core)->xscom_regs);
> +
> +        /* Map the ICP registers for each thread */
> +        for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
> +            memory_region_add_subregion(&chip->icp_mmio,
> +                                 (pcc->core_pir(chip, core_hwid) + j) << 12,
> +                                  &PNV_CORE(pnv_core)->icp_mmios[j]);
> +        }
>          i++;
>      }
>      g_free(typename);
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index 8633afbff795..d28fa445b11b 100644
> --- a/hw/ppc/pnv_core.c
> +++ b/hw/ppc/pnv_core.c
> @@ -25,6 +25,7 @@
>  #include "hw/ppc/pnv.h"
>  #include "hw/ppc/pnv_core.h"
>  #include "hw/ppc/pnv_xscom.h"
> +#include "hw/ppc/xics.h"
>  
>  static uint64_t pnv_core_icp_read(void *opaque, hwaddr addr, unsigned width)
>  {
> @@ -165,7 +166,7 @@ static void powernv_cpu_reset(void *opaque)
>      env->msr |= MSR_HVB; /* Hypervisor mode */
>  }
>  
> -static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
> +static void powernv_cpu_init(PowerPCCPU *cpu, XICSFabric *xi, Error **errp)
>  {
>      CPUPPCState *env = &cpu->env;
>      int core_pir;
> @@ -185,6 +186,9 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>      cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
>  
>      qemu_register_reset(powernv_cpu_reset, cpu);
> +
> +    /* xics_cpu_setup() assigns the CPU to the ICPState */
> +    xics_cpu_setup(xi, cpu);
>  }
>  
>  /*
> @@ -232,7 +236,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
>      .endianness = DEVICE_BIG_ENDIAN,
>  };
>  
> -static void pnv_core_realize_child(Object *child, Error **errp)
> +static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
>  {
>      Error *local_err = NULL;
>      CPUState *cs = CPU(child);
> @@ -244,7 +248,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
>          return;
>      }
>  
> -    powernv_cpu_init(cpu, &local_err);
> +    powernv_cpu_init(cpu, xi, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);
>          return;
> @@ -298,7 +302,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>      for (j = 0; j < cc->nr_threads; j++) {
>          obj = pc->threads + j * size;
>  
> -        pnv_core_realize_child(obj, &local_err);
> +        pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err);
>          if (local_err) {
>              goto err;
>          }

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-14  5:38   ` David Gibson
@ 2017-03-14  8:11     ` Cédric Le Goater
  2017-03-14 17:00     ` Cédric Le Goater
  1 sibling, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-14  8:11 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/14/2017 06:38 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:44AM +0100, Cédric Le Goater wrote:
>> This helper will be used to translate the server number of the XIVE
>> (which is a PIR) into an ICPState index number (which is a cpu index).
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> This seems a slightly roundabout way of doing things.  Why not just
> have the vcpu_by_pir() interface, then have the XICSFabric implementor
> go directly from PIR to xics server state.

yes. This is only used to find ICPs and if we introduce a new ICP 
type object, as you suggest, we might not need anymore this interface. 
I will take a look.

Thanks,

C.   
 
>> ---
>>  hw/intc/xics.c        | 11 +++++++++++
>>  hw/ppc/ppc.c          | 16 ++++++++++++++++
>>  include/hw/ppc/xics.h |  1 +
>>  target/ppc/cpu.h      | 10 ++++++++++
>>  4 files changed, 38 insertions(+)
>>
>> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
>> index e740989a1162..209e1a75ecb9 100644
>> --- a/hw/intc/xics.c
>> +++ b/hw/intc/xics.c
>> @@ -49,6 +49,17 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>>      return -1;
>>  }
>>  
>> +int xics_get_cpu_index_by_pir(int pir)
>> +{
>> +    PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir);
>> +
>> +    if (cpu) {
>> +        return cpu->parent_obj.cpu_index;
>> +    }
>> +
>> +    return -1;
>> +}
>> +
>>  void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
>>  {
>>      CPUState *cs = CPU(cpu);
>> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
>> index 5f93083d4a16..94bbe382a73a 100644
>> --- a/hw/ppc/ppc.c
>> +++ b/hw/ppc/ppc.c
>> @@ -1379,6 +1379,22 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id)
>>      return NULL;
>>  }
>>  
>> +PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
>> +{
>> +    CPUState *cs;
>> +
>> +    CPU_FOREACH(cs) {
>> +        PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +        CPUPPCState *env = &cpu->env;
>> +
>> +        if (env->spr_cb[SPR_PIR].default_value == pir) {
>> +            return cpu;
>> +        }
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>>  void ppc_cpu_parse_features(const char *cpu_model)
>>  {
>>      CPUClass *cc;
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index 9a5e715fe553..42bd24e975cb 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -173,6 +173,7 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu);
>>  
>>  /* Internal XICS interfaces */
>>  int xics_get_cpu_index_by_dt_id(int cpu_dt_id);
>> +int xics_get_cpu_index_by_pir(int pir);
>>  
>>  void icp_set_cppr(ICPState *icp, uint8_t cppr);
>>  void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
>> diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
>> index 7c4a1f50b38b..24a5af95cb45 100644
>> --- a/target/ppc/cpu.h
>> +++ b/target/ppc/cpu.h
>> @@ -2518,5 +2518,15 @@ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu);
>>   */
>>  PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id);
>>  
>> +/**
>> + * ppc_get_vcpu_by_pir_id:
>> + * @pir: Processor Identifier Register (SPR_PIR)
>> + *
>> + * Searches for a CPU by @pir.
>> + *
>> + * Returns: a PowerPCCPU struct
>> + */
>> +PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
>> +
>>  void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len);
>>  #endif /* PPC_CPU_H */
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric
  2017-03-14  5:40   ` David Gibson
@ 2017-03-14  8:12     ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-14  8:12 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/14/2017 06:40 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:45AM +0100, Cédric Le Goater wrote:
>> This handler will be required by PowerPC machines using multiple ICS
>> objects, like this is the case for PowerNV. Also update the sPAPR
>> machine to use the new handler.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> I don't see why you need a new callback for this.  In this and the
> subsequent patches, it looks like the callback is always just use the
> fabric's ics_get() to find the right ICS state, then call ics_eoi() on
> it.

yes. I have been dragging this uselessly from an old patchset. I will
remove it.

Thanks,

C. 

> 
>> ---
>>  hw/intc/xics.c        |  9 +++------
>>  hw/ppc/spapr.c        | 11 +++++++++++
>>  include/hw/ppc/xics.h |  2 ++
>>  3 files changed, 16 insertions(+), 6 deletions(-)
>>
>> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
>> index 209e1a75ecb9..e6fecd6e1a89 100644
>> --- a/hw/intc/xics.c
>> +++ b/hw/intc/xics.c
>> @@ -169,7 +169,7 @@ void ics_resend(ICSState *ics)
>>      }
>>  }
>>  
>> -static void ics_eoi(ICSState *ics, int nr)
>> +void ics_eoi(ICSState *ics, int nr)
>>  {
>>      ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
>>  
>> @@ -268,7 +268,6 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
>>  {
>>      XICSFabric *xi = icp->xics;
>>      XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi);
>> -    ICSState *ics;
>>      uint32_t irq;
>>  
>>      /* Send EOI -> ICS */
>> @@ -276,10 +275,8 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
>>      trace_xics_icp_eoi(icp->cs->cpu_index, xirr, icp->xirr);
>>      irq = xirr & XISR_MASK;
>>  
>> -    ics = xic->ics_get(xi, irq);
>> -    if (ics) {
>> -        ics_eoi(ics, irq);
>> -    }
>> +    xic->ics_eoi(xi, irq);
>> +
>>      if (!XISR(icp)) {
>>          icp_resend(icp);
>>      }
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index c3bb99160545..043629cc5c54 100644
>> --- a/hw/ppc/spapr.c
>> +++ b/hw/ppc/spapr.c
>> @@ -3024,6 +3024,16 @@ static void spapr_ics_resend(XICSFabric *dev)
>>      ics_resend(spapr->ics);
>>  }
>>  
>> +static void spapr_ics_eoi(XICSFabric *xi, int irq)
>> +{
>> +    ICSState *ics;
>> +
>> +    ics = spapr_ics_get(xi, irq);
>> +    if (ics) {
>> +        ics_eoi(ics, irq);
>> +    }
>> +}
>> +
>>  static ICPState *spapr_icp_get(XICSFabric *xi, int server)
>>  {
>>      sPAPRMachineState *spapr = SPAPR_MACHINE(xi);
>> @@ -3094,6 +3104,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
>>      vhc->get_patbe = spapr_get_patbe;
>>      xic->ics_get = spapr_ics_get;
>>      xic->ics_resend = spapr_ics_resend;
>> +    xic->ics_eoi = spapr_ics_eoi;
>>      xic->icp_get = spapr_icp_get;
>>      ispc->print_info = spapr_pic_print_info;
>>  }
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index 42bd24e975cb..00b003b2392d 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -155,6 +155,7 @@ typedef struct XICSFabricClass {
>>      InterfaceClass parent;
>>      ICSState *(*ics_get)(XICSFabric *xi, int irq);
>>      void (*ics_resend)(XICSFabric *xi);
>> +    void (*ics_eoi)(XICSFabric *xi, int irq);
>>      ICPState *(*icp_get)(XICSFabric *xi, int server);
>>  } XICSFabricClass;
>>  
>> @@ -189,6 +190,7 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon);
>>  void ics_pic_print_info(ICSState *ics, Monitor *mon);
>>  
>>  void ics_resend(ICSState *ics);
>> +void ics_eoi(ICSState *ics, int irq);
>>  void icp_resend(ICPState *ss);
>>  
>>  typedef struct sPAPRMachineState sPAPRMachineState;
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine
  2017-03-14  5:45   ` David Gibson
@ 2017-03-14  9:47     ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-14  9:47 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/14/2017 06:45 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:46AM +0100, Cédric Le Goater wrote:
>> Like this is done for the sPAPR machine, we use a simple array under
>> the PowerNV machine to store the Interrupt Control Presenters (ICP)
>> objects, one for each vCPU. This array is indexed by 'cpu_index' of
>> the CPUState.
>>
>> We use a list to hold the different Interrupt Control Sources (ICS)
>> objects, as PowerNV needs to handle multiple sources: for PCI-E and
>> for the Processor Service Interface (PSI).
>>
>> Finally, to interface with the XICS layer which manipulates the ICP
>> and ICS objects, we extend the PowerNV machine with an XICSFabric
>> interface and its associated handlers.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c          | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h  |  4 +++
>>  include/hw/ppc/xics.h |  1 +
>>  3 files changed, 94 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 09f0d22defb8..461d3535e99c 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -32,6 +32,8 @@
>>  #include "exec/address-spaces.h"
>>  #include "qemu/cutils.h"
>>  #include "qapi/visitor.h"
>> +#include "monitor/monitor.h"
>> +#include "hw/intc/intc.h"
>>  
>>  #include "hw/ppc/pnv_xscom.h"
>>  
>> @@ -416,6 +418,23 @@ static void ppc_powernv_init(MachineState *machine)
>>          machine->cpu_model = "POWER8";
>>      }
>>  
>> +    /* Create the Interrupt Control Presenters before the vCPUs */
>> +    pnv->nr_servers = pnv->num_chips * smp_cores * smp_threads;
>> +    pnv->icps = g_new0(ICPState, pnv->nr_servers);
>> +    for (i = 0; i < pnv->nr_servers; i++) {
>> +        ICPState *icp = &pnv->icps[i];
>> +        object_initialize(icp, sizeof(*icp), TYPE_ICP);
>> +        qdev_set_parent_bus(DEVICE(icp), sysbus_get_default());
> 
> Your fixup for the PAPR case suggests this is not the right approach
> to the device initialization.

yes. unless we introduce a new ICP type object with MMIOs.
 
>> +        object_property_add_child(OBJECT(pnv), "icp[*]", OBJECT(icp),
>> +                                  &error_fatal);
> 
> This isn't an urgent problem, but I dislike using the icp[*]
> behaviour as a rule (it's fixed now, but it just to be you could
> easily get O(n^3) behaviour using it).  In this case you already have
> a handle on a specific id for the object.
> 
> As well as being a bit less expensive, using an explicit index means
> that the exposed QOM address will definitely match an otherwise
> meaningful id, rather than just lining up by accident if all the
> orders remain the same.

I agree. The ICP should be indexed with the HW core ids and these
are not necessarily contiguous. I will change that.

>> +        object_property_add_const_link(OBJECT(icp), "xics", OBJECT(pnv),
>> +                                       &error_fatal);
>> +        object_property_set_bool(OBJECT(icp), true, "realized", &error_fatal);
>> +    }
>> +
>> +    /* and the list of Interrupt Control Sources */
>> +    QLIST_INIT(&pnv->ics);
>> +
>>      /* Create the processor chips */
>>      chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
>>      if (!object_class_by_name(chip_typename)) {
>> @@ -742,6 +761,48 @@ static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
>>      visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp);
>>  }
>>  
>> +static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
>> +    ICSState *ics;
>> +
>> +    QLIST_FOREACH(ics, &pnv->ics, list) {
>> +        if (ics_valid_irq(ics, irq)) {
>> +            return ics;
>> +        }
>> +    }
>> +    return NULL;
>> +}
>> +
>> +static void pnv_ics_resend(XICSFabric *xi)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
>> +    ICSState *ics;
>> +
>> +    QLIST_FOREACH(ics, &pnv->ics, list) {
>> +        ics_resend(ics);
>> +    }
>> +}
>> +
>> +static void pnv_ics_eoi(XICSFabric *xi, int irq)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
>> +    ICSState *ics;
>> +
>> +    QLIST_FOREACH(ics, &pnv->ics, list) {
>> +        if (ics_valid_irq(ics, irq)) {
> 
> Unless something is badly wrong, this should only return true for
> exactly one iteration through the loop.  Which makes this equivalent
> to ics_get() followed by ics_eoi().

yes.

Thanks,

C. 

>> +            ics_eoi(ics, irq);
>> +        }
>> +    }
>> +}
>> +
>> +static ICPState *pnv_icp_get(XICSFabric *xi, int server)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(xi);
>> +
>> +    return (server < pnv->nr_servers) ? &pnv->icps[server] : NULL;
>> +}
>> +
>>  static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name,
>>                                void *opaque, Error **errp)
>>  {
>> @@ -783,9 +844,27 @@ static void powernv_machine_class_props_init(ObjectClass *oc)
>>                                NULL);
>>  }
>>  
>> +static void pnv_pic_print_info(InterruptStatsProvider *obj,
>> +                               Monitor *mon)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
>> +    ICSState *ics;
>> +    int i;
>> +
>> +    for (i = 0; i < pnv->nr_servers; i++) {
>> +        icp_pic_print_info(&pnv->icps[i], mon);
>> +    }
>> +
>> +    QLIST_FOREACH(ics, &pnv->ics, list) {
>> +        ics_pic_print_info(ics, mon);
>> +    }
>> +}
>> +
>>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
>>  {
>>      MachineClass *mc = MACHINE_CLASS(oc);
>> +    XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
>> +    InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
>>  
>>      mc->desc = "IBM PowerNV (Non-Virtualized)";
>>      mc->init = ppc_powernv_init;
>> @@ -796,6 +875,11 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data)
>>      mc->no_parallel = 1;
>>      mc->default_boot_order = NULL;
>>      mc->default_ram_size = 1 * G_BYTE;
>> +    xic->icp_get = pnv_icp_get;
>> +    xic->ics_get = pnv_ics_get;
>> +    xic->ics_eoi = pnv_ics_eoi;
>> +    xic->ics_resend = pnv_ics_resend;
>> +    ispc->print_info = pnv_pic_print_info;
>>  
>>      powernv_machine_class_props_init(oc);
>>  }
>> @@ -806,6 +890,11 @@ static const TypeInfo powernv_machine_info = {
>>      .instance_size = sizeof(PnvMachineState),
>>      .instance_init = powernv_machine_initfn,
>>      .class_init    = powernv_machine_class_init,
>> +    .interfaces = (InterfaceInfo[]) {
>> +        { TYPE_XICS_FABRIC },
>> +        { TYPE_INTERRUPT_STATS_PROVIDER },
>> +        { },
>> +    },
>>  };
>>  
>>  static void powernv_machine_register_types(void)
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index df98a72006e4..6a0b004cea93 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -22,6 +22,7 @@
>>  #include "hw/boards.h"
>>  #include "hw/sysbus.h"
>>  #include "hw/ppc/pnv_lpc.h"
>> +#include "hw/ppc/xics.h"
>>  
>>  #define TYPE_PNV_CHIP "powernv-chip"
>>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>> @@ -114,6 +115,9 @@ typedef struct PnvMachineState {
>>      PnvChip      **chips;
>>  
>>      ISABus       *isa_bus;
>> +    ICPState     *icps;
>> +    uint32_t     nr_servers;
>> +    QLIST_HEAD(, ICSState) ics;
>>  } PnvMachineState;
>>  
>>  #define PNV_FDT_ADDR          0x01000000
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index 00b003b2392d..c2032cac55f6 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -115,6 +115,7 @@ struct ICSState {
>>      qemu_irq *qirqs;
>>      ICSIRQState *irqs;
>>      XICSFabric *xics;
>> +    QLIST_ENTRY(ICSState) list;
>>  };
>>  
>>  static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions
  2017-03-14  5:52   ` David Gibson
@ 2017-03-14 10:02     ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-14 10:02 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/14/2017 06:52 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:48AM +0100, Cédric Le Goater wrote:
>> and populate the device tree accordingly for the guest to start using
>> interrupts. This also links the ICP object to its associated CPUState
>> (only used by KVM to control the kernel vCPU).
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c      | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ppc/pnv_core.c | 12 ++++++++----
>>  2 files changed, 63 insertions(+), 4 deletions(-)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 7b13b08deadf..0ae11cc3a2ca 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -35,6 +35,7 @@
>>  #include "monitor/monitor.h"
>>  #include "hw/intc/intc.h"
>>  
>> +#include "hw/ppc/xics.h"
>>  #include "hw/ppc/pnv_xscom.h"
>>  
>>  #include "hw/isa/isa.h"
>> @@ -216,6 +217,47 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
>>                         servers_prop, sizeof(servers_prop))));
>>  }
>>  
>> +static void powernv_populate_icp(PnvChip *chip, void *fdt, int offset,
>> +                          uint32_t pir, uint32_t count)
>> +{
>> +    uint64_t addr;
>> +    char *name;
>> +    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
>> +    uint32_t irange[2], i, rsize;
>> +    uint64_t *reg;
>> +
>> +    /*
>> +     * TODO: add multichip ICP BAR
>> +     */
>> +    addr = PNV_ICP_BASE(chip) | (pir << 12);
>> +
>> +    irange[0] = cpu_to_be32(pir);
>> +    irange[1] = cpu_to_be32(count);
>> +
>> +    rsize = sizeof(uint64_t) * 2 * count;
>> +    reg = g_malloc(rsize);
>> +    for (i = 0; i < count; i++) {
>> +        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
>> +        reg[i * 2 + 1] = cpu_to_be64(0x1000);
>> +    }
>> +
>> +    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
>> +    offset = fdt_add_subnode(fdt, offset, name);
>> +    _FDT(offset);
>> +    g_free(name);
>> +
>> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
>> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
>> +    _FDT((fdt_setprop_string(fdt, offset, "device_type",
>> +                              "PowerPC-External-Interrupt-Presentation")));
>> +    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
>> +    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
>> +                       irange, sizeof(irange))));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
>> +    g_free(reg);
>> +}
>> +
>>  static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>  {
>>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> @@ -229,6 +271,10 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>>  
>>          powernv_create_core_node(chip, pnv_core, fdt);
>> +
>> +        /* Interrupt Control Presenters (ICP). One per thread. */
>> +        powernv_populate_icp(chip, fdt, 0, pnv_core->pir,
> 
> All the xicp nodes under the root bus, is that correct?

yes. The '0' parameter feels useless there.

C. 


> 
>> +                             CPU_CORE(pnv_core)->nr_threads);
>>      }
>>  
>>      if (chip->ram_size) {
>> @@ -697,6 +743,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          error_propagate(errp, error);
>>          return;
>>      }
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
>>  
>>      /* Cores */
>>      pnv_chip_core_sanitize(chip, &error);
>> @@ -711,6 +758,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>               && (i < chip->nr_cores); core_hwid++) {
>>          char core_name[32];
>>          void *pnv_core = chip->cores + i * typesize;
>> +        int j;
>>  
>>          if (!(chip->cores_mask & (1ull << core_hwid))) {
>>              continue;
>> @@ -738,6 +786,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>                                  PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
>>                                                         core_hwid),
>>                                  &PNV_CORE(pnv_core)->xscom_regs);
>> +
>> +        /* Map the ICP registers for each thread */
>> +        for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
>> +            memory_region_add_subregion(&chip->icp_mmio,
>> +                                 (pcc->core_pir(chip, core_hwid) + j) << 12,
>> +                                  &PNV_CORE(pnv_core)->icp_mmios[j]);
>> +        }
>>          i++;
>>      }
>>      g_free(typename);
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> index 8633afbff795..d28fa445b11b 100644
>> --- a/hw/ppc/pnv_core.c
>> +++ b/hw/ppc/pnv_core.c
>> @@ -25,6 +25,7 @@
>>  #include "hw/ppc/pnv.h"
>>  #include "hw/ppc/pnv_core.h"
>>  #include "hw/ppc/pnv_xscom.h"
>> +#include "hw/ppc/xics.h"
>>  
>>  static uint64_t pnv_core_icp_read(void *opaque, hwaddr addr, unsigned width)
>>  {
>> @@ -165,7 +166,7 @@ static void powernv_cpu_reset(void *opaque)
>>      env->msr |= MSR_HVB; /* Hypervisor mode */
>>  }
>>  
>> -static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>> +static void powernv_cpu_init(PowerPCCPU *cpu, XICSFabric *xi, Error **errp)
>>  {
>>      CPUPPCState *env = &cpu->env;
>>      int core_pir;
>> @@ -185,6 +186,9 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>>      cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
>>  
>>      qemu_register_reset(powernv_cpu_reset, cpu);
>> +
>> +    /* xics_cpu_setup() assigns the CPU to the ICPState */
>> +    xics_cpu_setup(xi, cpu);
>>  }
>>  
>>  /*
>> @@ -232,7 +236,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
>>      .endianness = DEVICE_BIG_ENDIAN,
>>  };
>>  
>> -static void pnv_core_realize_child(Object *child, Error **errp)
>> +static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
>>  {
>>      Error *local_err = NULL;
>>      CPUState *cs = CPU(child);
>> @@ -244,7 +248,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
>>          return;
>>      }
>>  
>> -    powernv_cpu_init(cpu, &local_err);
>> +    powernv_cpu_init(cpu, xi, &local_err);
>>      if (local_err) {
>>          error_propagate(errp, local_err);
>>          return;
>> @@ -298,7 +302,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>>      for (j = 0; j < cc->nr_threads; j++) {
>>          obj = pc->threads + j * size;
>>  
>> -        pnv_core_realize_child(obj, &local_err);
>> +        pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err);
>>          if (local_err) {
>>              goto err;
>>          }
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-14  5:38   ` David Gibson
  2017-03-14  8:11     ` Cédric Le Goater
@ 2017-03-14 17:00     ` Cédric Le Goater
  2017-03-15  4:53       ` David Gibson
  1 sibling, 1 reply; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-14 17:00 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/14/2017 06:38 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:44AM +0100, Cédric Le Goater wrote:
>> This helper will be used to translate the server number of the XIVE
>> (which is a PIR) into an ICPState index number (which is a cpu index).
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> This seems a slightly roundabout way of doing things.  Why not just
> have the vcpu_by_pir() interface, then have the XICSFabric implementor
> go directly from PIR to xics server state.

So what you are saying is that we should try to move the "nature"
of the 'server' parameter of the xics framework in the icp_get() 
handler of the XICSFabric. Correct ? Because at the end, it all 
boils down to use a 'server' to look for an ICPState.

Each machine would do its conversion : 

	xics_get_cpu_index_by_dt_id() for spapr
	xics_get_cpu_index_by_pir() for powernv

C.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-14 17:00     ` Cédric Le Goater
@ 2017-03-15  4:53       ` David Gibson
  2017-03-15 10:04         ` Cédric Le Goater
  0 siblings, 1 reply; 29+ messages in thread
From: David Gibson @ 2017-03-15  4:53 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1375 bytes --]

On Tue, Mar 14, 2017 at 06:00:43PM +0100, Cédric Le Goater wrote:
> On 03/14/2017 06:38 AM, David Gibson wrote:
> > On Wed, Mar 08, 2017 at 11:52:44AM +0100, Cédric Le Goater wrote:
> >> This helper will be used to translate the server number of the XIVE
> >> (which is a PIR) into an ICPState index number (which is a cpu index).
> >>
> >> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> > 
> > This seems a slightly roundabout way of doing things.  Why not just
> > have the vcpu_by_pir() interface, then have the XICSFabric implementor
> > go directly from PIR to xics server state.
> 
> So what you are saying is that we should try to move the "nature"
> of the 'server' parameter of the xics framework in the icp_get() 
> handler of the XICSFabric. Correct ? Because at the end, it all 
> boils down to use a 'server' to look for an ICPState.
> 
> Each machine would do its conversion : 
> 
> 	xics_get_cpu_index_by_dt_id() for spapr
> 	xics_get_cpu_index_by_pir() for powernv

Yes, that's exactly right.  I think it makes sense for the XICSFabric
to be the thing that defines the mapping from XICS server numbers to
whatever other ID is relevant.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
@ 2017-03-15  6:16   ` David Gibson
  2017-03-15  9:38     ` Benjamin Herrenschmidt
  2017-03-16 13:52     ` Cédric Le Goater
  0 siblings, 2 replies; 29+ messages in thread
From: David Gibson @ 2017-03-15  6:16 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 31598 bytes --]

On Wed, Mar 08, 2017 at 11:52:49AM +0100, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> The PSI (Processor Service Interface) Controller is one of the engines
> of the "Bridge" unit which connects the different interfaces to the
> Power Processor.
> 
> This adds just enough of the PSI bridge to handle various on-chip and
> the one external interrupt. The rest of PSI has to do with the link to
> the IBM FSP service processor which we don't plan to emulate (not used
> on OpenPower machines).
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.9
>       - changed the XSCOM interface to fit new model
>       - QOMified the model
>       - reworked set_xive and worked around a skiboot bug
>       - removed the 'psi_mmio_to_xscom' mapping array ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |  35 ++-
>  hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   8 +
>  include/hw/ppc/pnv_psi.h   |  61 +++++
>  include/hw/ppc/pnv_xscom.h |   3 +
>  6 files changed, 685 insertions(+), 7 deletions(-)
>  create mode 100644 hw/ppc/pnv_psi.c
>  create mode 100644 include/hw/ppc/pnv_psi.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index 001293423c8d..dc19ee17fa57 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 0ae11cc3a2ca..85b00bf235c6 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -356,15 +356,22 @@ static void ppc_powernv_reset(void)
>   * have a CPLD that will collect the SerIRQ and shoot them as a
>   * single level interrupt to the P8 chip. So let's setup a hook
>   * for doing just that.
> - *
> - * Note: The actual interrupt input isn't emulated yet, this will
> - * come with the PSI bridge model.
>   */
>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>  {
> -    /* We don't yet emulate the PSI bridge which provides the external
> -     * interrupt, so just drop interrupts on the floor
> -     */
> +    PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> +    uint32_t old_state = pnv->cpld_irqstate;
> +    PnvChip *chip = opaque;
> +
> +    if (level) {
> +        pnv->cpld_irqstate |= 1u << n;
> +    } else {
> +        pnv->cpld_irqstate &= ~(1u << n);
> +    }
> +    if (pnv->cpld_irqstate != old_state) {
> +        pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
> +                        pnv->cpld_irqstate != 0);
> +    }
>  }
>  
>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> @@ -702,6 +709,11 @@ static void pnv_chip_init(Object *obj)
>  
>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
> +
> +    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
> +    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
> +    object_property_add_const_link(OBJECT(&chip->psi), "xics",
> +                                   OBJECT(qdev_get_machine()), &error_abort);
>  }
>  
>  static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
> @@ -722,6 +734,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      char *typename = pnv_core_typename(pcc->cpu_model);
>      size_t typesize = object_type_get_instance_size(typename);
>      int i, core_hwid;
> +    MachineState *machine = MACHINE(qdev_get_machine());
> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
>  
>      if (!object_class_by_name(typename)) {
>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
> @@ -797,6 +811,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      }
>      g_free(typename);
>  
> +
> +    /* Processor Service Interface (PSI) Host Bridge */
> +    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
> +                             &error_fatal);
> +    pnv_xscom_add_subregion(chip, PNV_XSCOM_PSI_BASE, &chip->psi.xscom_regs);
> +
> +    /* link in the PSI ICS */
> +    QLIST_INSERT_HEAD(&pnv->ics, &chip->psi.ics, list);
> +
>      /* Create LPC controller */
>      object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
>                               &error_fatal);
> diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
> new file mode 100644
> index 000000000000..6ba688aac075
> --- /dev/null
> +++ b/hw/ppc/pnv_psi.c
> @@ -0,0 +1,583 @@
> +/*
> + * QEMU PowerNV PowerPC PSI interface
> + *
> + * Copyright (c) 2016, IBM Corporation
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "target/ppc/cpu.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +
> +#include "exec/address-spaces.h"
> +
> +#include "hw/ppc/fdt.h"
> +#include "hw/ppc/pnv.h"
> +#include "hw/ppc/pnv_xscom.h"
> +#include "hw/ppc/pnv_psi.h"
> +
> +#include <libfdt.h>
> +
> +#define PSIHB_XSCOM_FIR_RW      0x00
> +#define PSIHB_XSCOM_FIR_AND     0x01
> +#define PSIHB_XSCOM_FIR_OR      0x02
> +#define PSIHB_XSCOM_FIRMASK_RW  0x03
> +#define PSIHB_XSCOM_FIRMASK_AND 0x04
> +#define PSIHB_XSCOM_FIRMASK_OR  0x05
> +#define PSIHB_XSCOM_FIRACT0     0x06
> +#define PSIHB_XSCOM_FIRACT1     0x07
> +#define PSIHB_XSCOM_BAR         0x0a
> +#define   PSIHB_BAR_EN                  0x0000000000000001ull
> +#define PSIHB_XSCOM_FSPBAR      0x0b
> +#define PSIHB_XSCOM_CR          0x0e
> +#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
> +#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
> +#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
> +#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
> +#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
> +#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
> +#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
> +#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
> +#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
> +#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
> +          /* and more ... */
> +#define PSIHB_XSCOM_SEMR        0x0f
> +#define PSIHB_XSCOM_XIVR_PSI    0x10
> +#define   PSIHB_XIVR_SERVER_SH  40
> +#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
> +#define   PSIHB_XIVR_PRIO_SH    32
> +#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
> +#define   PSIHB_XIVR_SRC_SH             29
> +#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
> +#define   PSIHB_XIVR_PENDING    0x01000000ull
> +#define PSIHB_XSCOM_SCR         0x12
> +#define PSIHB_XSCOM_CCR         0x13
> +#define PSIHB_XSCOM_DMA_UPADD   0x14
> +#define PSIHB_XSCOM_IRQ_STAT    0x15
> +#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
> +#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
> +#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
> +#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
> +#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
> +#define PSIHB_XSCOM_XIVR_OCC    0x16
> +#define PSIHB_XSCOM_XIVR_FSI    0x17
> +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
> +#define PSIHB_XSCOM_XIVR_LOCERR 0x19
> +#define PSIHB_XSCOM_XIVR_EXT    0x1a
> +#define PSIHB_XSCOM_IRSN        0x1b
> +#define   PSIHB_IRSN_COMP_SH            45
> +#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
> +#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
> +#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
> +#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
> +#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
> +#define   PSIHB_IRSN_COMPMASK_SH        13
> +#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
> +
> +/*
> + * These are the values of the registers when accessed through the
> + * MMIO region. The relation is xscom = (mmio + 0x50) >> 3
> + */
> +#define PSIHB_MMIO_BAR          0x00
> +#define PSIHB_MMIO_FSPBAR       0x08
> +#define PSIHB_MMIO_CR           0x20
> +#define PSIHB_MMIO_SEMR         0x28
> +#define PSIHB_MMIO_XIVR_PSI     0x30
> +#define PSIHB_MMIO_SCR          0x40
> +#define PSIHB_MMIO_CCR          0x48
> +#define PSIHB_MMIO_DMA_UPADD    0x50
> +#define PSIHB_MMIO_IRQ_STAT     0x58
> +#define PSIHB_MMIO_XIVR_OCC     0x60
> +#define PSIHB_MMIO_XIVR_FSI     0x68
> +#define PSIHB_MMIO_XIVR_LPCI2C  0x70
> +#define PSIHB_MMIO_XIVR_LOCERR  0x78
> +#define PSIHB_MMIO_XIVR_EXT     0x80
> +#define PSIHB_MMIO_IRSN         0x88
> +#define PSIHB_MMIO_MAX          0x100
> +
> +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
> +{
> +    MemoryRegion *sysmem = get_system_memory();
> +    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
> +
> +    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
> +
> +    /* Update MR, always remove it first */
> +    if (old & PSIHB_BAR_EN) {
> +        memory_region_del_subregion(sysmem, &psi->regs_mr);
> +    }
> +    /* Then add it back if needed */
> +    if (bar & PSIHB_BAR_EN) {
> +        uint64_t addr = bar & 0x0003fffffff00000;
> +        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
> +    }
> +}
> +
> +static void pnv_psi_update_fsp_mr(PnvPsi *psi)
> +{
> +    /* XXX Update FSP MR if/when we support FSP BAR */
> +}
> +
> +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr)
> +{
> +    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
> +
> +    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
> +
> +    /* Check some bit changes */
> +    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
> +        pnv_psi_update_fsp_mr(psi);
> +    }
> +}
> +
> +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val)
> +{
> +    uint32_t offset;
> +    ICSState *ics = &psi->ics;
> +
> +    /* In this model we ignore the up/down enable bits for now
> +     * as SW doesn't use them (other than setting them at boot).
> +     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
> +     * it and finally we ignore reset (XXX fix that ?)
> +     */
> +    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
> +                                         PSIHB_IRSN_IRQ_MUX |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN);
> +
> +    /* We ignore the compare mask as well, our ICS emulation is too
> +     * simplistic to make any use if it, and we extract the offset
> +     * from the compare value
> +     */
> +    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
> +    ics->offset = offset;
> +}
> +
> +static bool pnv_psi_irq_bits(PnvPsi *psi, PnvPsiIrq irq,
> +                             uint32_t *out_xivr_reg,
> +                             uint32_t *out_stat_reg,
> +                             uint64_t *out_stat_bit)

Your PnvPsiIrq values are arbitrary and contiguous AFACT.  Why not
just have a lookup table for this info, instead of a giant switch
statement?

> +{
> +    switch (irq) {
> +    case PSIHB_IRQ_PSI:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> +        *out_stat_reg = PSIHB_XSCOM_CR;
> +        *out_stat_bit = PSIHB_CR_PSI_IRQ;
> +        break;
> +    case PSIHB_IRQ_FSP:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> +        *out_stat_reg = PSIHB_XSCOM_CR;
> +        *out_stat_bit = PSIHB_CR_FSP_IRQ;
> +        break;
> +    case PSIHB_IRQ_OCC:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
> +        break;
> +    case PSIHB_IRQ_FSI:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
> +        break;
> +    case PSIHB_IRQ_LPC_I2C:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
> +        break;
> +    case PSIHB_IRQ_LOCAL_ERR:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
> +        break;
> +    case PSIHB_IRQ_EXTERNAL:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
> +        break;
> +    default:
> +        return false;
> +    }
> +    return true;
> +}
> +
> +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
> +{
> +    ICSState *ics = &psi->ics;
> +    uint32_t xivr_reg;
> +    uint32_t stat_reg;
> +    uint64_t stat_bit;
> +    uint32_t src;
> +    bool masked;
> +
> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
> +        return;
> +    }
> +
> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
> +    if (state) {
> +        psi->regs[stat_reg] |= stat_bit;
> +        /* XXX optimization: check mask here. That means re-evaluating
> +         * when unmasking, thus TODO
> +         */
> +        qemu_irq_raise(ics->qirqs[src]);
> +    } else {
> +        psi->regs[stat_reg] &= ~stat_bit;
> +
> +        /* FSP and PSI are muxed so don't lower if either still set */
> +        if (stat_reg != PSIHB_XSCOM_CR ||
> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
> +            qemu_irq_lower(ics->qirqs[src]);
> +        } else {
> +            state = true;
> +        }
> +    }

It might be cleaner to just revaluate the irq level from scratch here,
and set the level, rather than doing this complicated dance to work
out if it has changed.

> +
> +    /* XXX Note about the emulation of the pending bit: This isn't
> +     * entirely correct. The pending bit should be cleared when the
> +     * EOI has been received. However, we don't have callbacks on
> +     * EOI (especially not under KVM) so no way to emulate that
> +     * properly, so instead we just set that bit as the logical
> +     * "output" of the XIVR (ie pending & !masked)
> +     * XXX TODO: Also update it on set_xivr
> +     */
> +    if (state && !masked) {
> +        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
> +    } else {
> +        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
> +    }
> +}
> +
> +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val)
> +{
> +    ICSState *ics = &psi->ics;
> +    uint16_t server;
> +    uint8_t prio;
> +    uint8_t src;
> +    int icp_index;
> +
> +    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
> +            (val & (PSIHB_XIVR_SERVER_MSK |
> +                    PSIHB_XIVR_PRIO_MSK |
> +                    PSIHB_XIVR_SRC_MSK));
> +    val = psi->regs[reg];
> +    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
> +    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
> +    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> +    if (src > PSIHB_IRQ_EXTERNAL) {
> +        /* XXX Generate error ? */
> +        return;
> +    }
> +
> +    /*
> +     * Linux fills the irq xivr with the hw processor id plus the
> +     * link bits. shift back to get something valid.
> +     */
> +    server >>= 2;
> +
> +    /*
> +     * When skiboot initializes PSIHB, it fills the xives with
> +     * server=0, prio=0xff, but we don't have a CPU with a pir=0. So
> +     * skip that case.
> +     */
> +    if (prio != 0xff) {
> +        icp_index = xics_get_cpu_index_by_pir(server);
> +        assert(icp_index != -1);
> +    } else {
> +        if (server) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "PSI: bogus server %d for IRQ %d\n",
> +                          server, src);
> +        }
> +        icp_index = server;
> +    }

This logic doesn't seem like it belongs here.  You've received a
server number, seems like you should just pass it on to the xics code,
and if there can be an error, have that detect it.

> +
> +    /* Now because of source remapping, weird things can happen
> +     * if you change the source number dynamically, our simple ICS
> +     * doesn't deal with remapping. So we just poke a different
> +     * ICS entry based on what source number was written. This will
> +     * do for now but a more accurate implementation would instead
> +     * use a fixed server/prio and a remapper of the generated irq.
> +     */
> +    ics_simple_write_xive(ics, src, icp_index, prio, prio);
> +}
> +
> +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio)
> +{
> +    uint64_t val = 0xffffffffffffffffull;
> +
> +    switch (offset) {
> +    case PSIHB_XSCOM_FIR_RW:
> +    case PSIHB_XSCOM_FIRACT0:
> +    case PSIHB_XSCOM_FIRACT1:
> +    case PSIHB_XSCOM_BAR:
> +    case PSIHB_XSCOM_FSPBAR:
> +    case PSIHB_XSCOM_CR:
> +    case PSIHB_XSCOM_XIVR_PSI:
> +    case PSIHB_XSCOM_XIVR_OCC:
> +    case PSIHB_XSCOM_XIVR_FSI:
> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> +    case PSIHB_XSCOM_XIVR_LOCERR:
> +    case PSIHB_XSCOM_XIVR_EXT:
> +    case PSIHB_XSCOM_IRQ_STAT:
> +    case PSIHB_XSCOM_SEMR:
> +    case PSIHB_XSCOM_DMA_UPADD:
> +    case PSIHB_XSCOM_IRSN:
> +        val = psi->regs[offset];
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
> +                      offset);

As noted elsewhere, tracepoints are more usual than qemu_log() these
days.  But either way, this really should have a distinguishable
message from the one in the write path.

> +    }
> +    return val;
> +}
> +
> +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val,
> +                              bool mmio)
> +{
> +    switch (offset) {
> +    case PSIHB_XSCOM_FIR_RW:
> +    case PSIHB_XSCOM_FIRACT0:
> +    case PSIHB_XSCOM_FIRACT1:
> +    case PSIHB_XSCOM_SEMR:
> +    case PSIHB_XSCOM_DMA_UPADD:
> +        psi->regs[offset] = val;
> +        break;
> +    case PSIHB_XSCOM_FIR_OR:
> +        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
> +        break;
> +    case PSIHB_XSCOM_FIR_AND:
> +        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
> +        break;
> +    case PSIHB_XSCOM_BAR:
> +        /* Only XSCOM can write this one */
> +        if (!mmio) {
> +            pnv_psi_set_bar(psi, val);
> +        }
> +        break;
> +    case PSIHB_XSCOM_FSPBAR:
> +        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;

Should that be PSIHB_XSCOM_FSPBAR?

> +        pnv_psi_update_fsp_mr(psi);
> +        break;
> +    case PSIHB_XSCOM_CR:
> +        pnv_psi_set_cr(psi, val);
> +        break;
> +    case PSIHB_XSCOM_SCR:
> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
> +        break;
> +    case PSIHB_XSCOM_CCR:
> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
> +        break;
> +    case PSIHB_XSCOM_XIVR_PSI:
> +    case PSIHB_XSCOM_XIVR_OCC:
> +    case PSIHB_XSCOM_XIVR_FSI:
> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> +    case PSIHB_XSCOM_XIVR_LOCERR:
> +    case PSIHB_XSCOM_XIVR_EXT:
> +        pnv_psi_set_xivr(psi, offset, val);
> +        break;
> +    case PSIHB_XSCOM_IRQ_STAT:
> +        /* Read only, should we generate an error ? */
> +        break;
> +    case PSIHB_XSCOM_IRSN:
> +        pnv_psi_set_irsn(psi, val);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
> +                      offset);
> +    }
> +}
> +
> +static inline uint32_t psi_mmio_to_xscom(hwaddr addr)
> +{
> +    return (addr >> 3) + PSIHB_XSCOM_BAR;
> +}
> +
> +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    return pnv_psi_reg_read(opaque, psi_mmio_to_xscom(addr), true);
> +}
> +
> +static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
> +                              uint64_t val, unsigned size)
> +{
> +    pnv_psi_reg_write(opaque, psi_mmio_to_xscom(addr), val, true);
> +}
> +
> +static const MemoryRegionOps psi_mmio_ops = {
> +    .read = pnv_psi_mmio_read,
> +    .write = pnv_psi_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_xscom_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvPsi *psi = PNV_PSI(opaque);
> +    uint32_t offset = addr >> 3;
> +
> +    return pnv_psi_reg_read(psi, offset, false);
> +}
> +
> +static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
> +                                uint64_t val, unsigned size)
> +{
> +    PnvPsi *psi = PNV_PSI(opaque);
> +    uint32_t offset = addr >> 3;
> +
> +    pnv_psi_reg_write(psi, offset, val, false);
> +}
> +
> +static const MemoryRegionOps pnv_psi_xscom_ops = {
> +    .read = pnv_psi_xscom_read,
> +    .write = pnv_psi_xscom_write,
> +    .valid.min_access_size = 8,
> +    .valid.max_access_size = 8,
> +    .impl.min_access_size = 8,
> +    .impl.max_access_size = 8,

Consistent nesting format of the two MemoryRegionOps would be good.

> +    .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static void pnv_psi_init(Object *obj)
> +{
> +    PnvPsi *psi = PNV_PSI(obj);
> +
> +    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
> +    qdev_set_parent_bus(DEVICE(&psi->ics), sysbus_get_default());
> +    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
> +}
> +
> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvPsi *psi = PNV_PSI(dev);
> +    ICSState *ics = &psi->ics;
> +    Object *obj;
> +    Error *err = NULL, *local_err = NULL;
> +    unsigned int i;
> +
> +    /* Initialize MMIO region */
> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
> +                          "psihb", PNV_PSIHB_BAR_SIZE);
> +
> +    /* Default BAR. Use object properties ? */
> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
> +
> +    /* Default sources in XIVR */
> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
> +            (0ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
> +            (1ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
> +            (2ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
> +            (3ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
> +            (4ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
> +            (5ull << PSIHB_XIVR_SRC_SH);
> +
> +    /* get XICSFabric from chip */
> +    obj = object_property_get_link(OBJECT(dev), "xics", &err);
> +    if (!obj) {
> +        error_setg(errp, "%s: required link 'xics' not found: %s",
> +                   __func__, error_get_pretty(err));
> +        return;
> +    }
> +
> +    /*
> +     * PSI interrupt control source
> +     */
> +    object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
> +    object_property_add_const_link(OBJECT(ics), "xics", obj, &err);
> +    object_property_set_bool(OBJECT(ics), true, "realized",  &local_err);
> +    error_propagate(&err, local_err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +
> +    for (i = 0; i < ics->nr_irqs; i++) {
> +        ics_set_irq_type(ics, i, true);
> +    }
> +
> +    /* XScom region for PSI registers */
> +    pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
> +                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE);
> +}
> +
> +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> +{
> +    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
> +    char *name;
> +    int offset;
> +    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
> +    uint32_t reg[] = {
> +        cpu_to_be32(lpc_pcba),
> +        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
> +    };
> +
> +    name = g_strdup_printf("psihb@%x", lpc_pcba);
> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    _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,
> +                      sizeof(compat))));
> +    return 0;
> +}
> +
> +
> +static void pnv_psi_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> +
> +    xdc->populate = pnv_psi_populate;
> +
> +    dc->realize = pnv_psi_realize;
> +}
> +
> +static const TypeInfo pnv_psi_info = {
> +    .name          = TYPE_PNV_PSI,
> +    .parent        = TYPE_DEVICE,

Since the PSI has an MMIO presence, it probably should be a
SysBusDevice, rather than a raw descendent of TYPE_DEVICE.

> +    .instance_size = sizeof(PnvPsi),
> +    .instance_init = pnv_psi_init,
> +    .class_init    = pnv_psi_class_init,
> +    .interfaces    = (InterfaceInfo[]) {
> +        { TYPE_PNV_XSCOM_INTERFACE },
> +        { }
> +    }
> +};
> +
> +static void pnv_psi_register_types(void)
> +{
> +    type_register_static(&pnv_psi_info);
> +}
> +
> +type_init(pnv_psi_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index f11215ea31f2..f93ec32603b7 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -23,6 +23,7 @@
>  #include "hw/sysbus.h"
>  #include "hw/ppc/pnv_lpc.h"
>  #include "hw/ppc/xics.h"
> +#include "hw/ppc/pnv_psi.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -58,6 +59,7 @@ typedef struct PnvChip {
>      MemoryRegion icp_mmio;
>  
>      PnvLpcController lpc;
> +    PnvPsi       psi;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> @@ -119,6 +121,8 @@ typedef struct PnvMachineState {
>      ICPState     *icps;
>      uint32_t     nr_servers;
>      QLIST_HEAD(, ICSState) ics;
> +
> +    uint32_t     cpld_irqstate;
>  } PnvMachineState;
>  
>  #define PNV_FDT_ADDR          0x01000000
> @@ -150,4 +154,8 @@ typedef struct PnvMachineState {
>  #define PNV_ICP_BASE(chip)   0x0003ffff80000000ull
>  #define PNV_ICP_SIZE         0x0000000000100000ull
>  
> +#define PNV_PSIHB_BAR         0x0003fffe80000000ull
> +#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
> +
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
> new file mode 100644
> index 000000000000..ac3c5f8362e3
> --- /dev/null
> +++ b/include/hw/ppc/pnv_psi.h
> @@ -0,0 +1,61 @@
> +/*
> + * QEMU PowerPC PowerNV PSI controller
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_PSI_H
> +#define _PPC_PNV_PSI_H
> +
> +#define TYPE_PNV_PSI "pnv-psi"
> +#define PNV_PSI(obj) \
> +     OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI)
> +
> +#define PSIHB_XSCOM_MAX         0x20
> +
> +typedef struct XICSState XICSState;
> +
> +typedef struct PnvPsi {
> +    DeviceState parent;
> +
> +    MemoryRegion regs_mr;
> +
> +    /* FSP region not supported */
> +    /* MemoryRegion fsp_mr; */
> +
> +    /* Interrupt generation */
> +    ICSState ics;
> +
> +    /* Registers */
> +    uint64_t regs[PSIHB_XSCOM_MAX];
> +
> +    MemoryRegion xscom_regs;
> +} PnvPsi;
> +
> +typedef enum PnvPsiIrq {
> +    PSIHB_IRQ_PSI, /* internal use only */
> +    PSIHB_IRQ_FSP, /* internal use only */
> +    PSIHB_IRQ_OCC,
> +    PSIHB_IRQ_FSI,
> +    PSIHB_IRQ_LPC_I2C,
> +    PSIHB_IRQ_LOCAL_ERR,
> +    PSIHB_IRQ_EXTERNAL,
> +} PnvPsiIrq;
> +
> +#define PSI_NUM_INTERRUPTS 6
> +
> +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state);
> +
> +#endif /* _PPC_PNV_PSI_H */
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index 0faa1847bf13..2938abd74955 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
>  #define PNV_XSCOM_LPC_BASE        0xb0020
>  #define PNV_XSCOM_LPC_SIZE        0x4
>  
> +#define PNV_XSCOM_PSI_BASE        0x2010900
> +#define PNV_XSCOM_PSI_SIZE        0x20
> +
>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-15  6:16   ` David Gibson
@ 2017-03-15  9:38     ` Benjamin Herrenschmidt
  2017-03-16 13:52     ` Cédric Le Goater
  1 sibling, 0 replies; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2017-03-15  9:38 UTC (permalink / raw)
  To: David Gibson, Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

On Wed, 2017-03-15 at 17:16 +1100, David Gibson wrote:
> It might be cleaner to just revaluate the irq level from scratch
> here,
> and set the level, rather than doing this complicated dance to work
> out if it has changed.

Hrm... there was a reason I did it this way but I can't quite remember
why...

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper
  2017-03-15  4:53       ` David Gibson
@ 2017-03-15 10:04         ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-15 10:04 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel

On 03/15/2017 05:53 AM, David Gibson wrote:
> On Tue, Mar 14, 2017 at 06:00:43PM +0100, Cédric Le Goater wrote:
>> On 03/14/2017 06:38 AM, David Gibson wrote:
>>> On Wed, Mar 08, 2017 at 11:52:44AM +0100, Cédric Le Goater wrote:
>>>> This helper will be used to translate the server number of the XIVE
>>>> (which is a PIR) into an ICPState index number (which is a cpu index).
>>>>
>>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>>
>>> This seems a slightly roundabout way of doing things.  Why not just
>>> have the vcpu_by_pir() interface, then have the XICSFabric implementor
>>> go directly from PIR to xics server state.
>>
>> So what you are saying is that we should try to move the "nature"
>> of the 'server' parameter of the xics framework in the icp_get() 
>> handler of the XICSFabric. Correct ? Because at the end, it all 
>> boils down to use a 'server' to look for an ICPState.
>>
>> Each machine would do its conversion : 
>>
>> 	xics_get_cpu_index_by_dt_id() for spapr
>> 	xics_get_cpu_index_by_pir() for powernv
> 
> Yes, that's exactly right.  I think it makes sense for the XICSFabric
> to be the thing that defines the mapping from XICS server numbers to
> whatever other ID is relevant.
> 

OK. So we need some more cleanups in the current code because 
'cpu_index' is still being used in a couple of places. Below
is a patch adding an ICPState backlink to ease the transition. 
After this one, moving the XICS server mapping under the 
icp_get() handler is trivial. Tell me if you like the idea and 
I will send both patches. 

If not, we can still do a few thing but we will need to move 
the lookup out of xics_cpu_setup() *and* xics_cpu_destoy().


Thanks,

C. 











>From dad67b9fdcafa7f9e84ce27e6ae5fd2c2bbd6b3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org>
Date: Wed, 15 Mar 2017 09:17:11 +0100
Subject: [PATCH] ppc/xics: introduce a ICPState backlink under PowerPCCPU
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently, 'cpu_index' is used to lookup for ICPState objects. But,
this can be a problem for platforms having a different index, the
PowerNV uses a core PIR number for instance.

One way to abstract the IRQ 'server' number of the XICS layer is to
let the core init routine of the platform do the lookup of the
ICPState and store the resulting ICPState object under PowerPCCPU.
This enables us to limit the use of 'cpu_index' in XICS making it more
generic for other platforms.

It also has the benefit of simplifying the sPAPR hcalls which do not
need to do any ICPState lookup anymore.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/xics.c          |  4 ++--
 hw/intc/xics_spapr.c    | 20 +++++---------------
 hw/ppc/spapr_cpu_core.c |  4 +++-
 target/ppc/cpu.h        |  2 ++
 4 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index e740989a1162..5cde86ceb3bc 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -52,7 +52,7 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
 void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(xi, cs->cpu_index);
+    ICPState *icp = cpu->icp;
 
     assert(icp);
     assert(cs == icp->cs);
@@ -65,7 +65,7 @@ void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    ICPState *icp = xics_icp_get(xi, cs->cpu_index);
+    ICPState *icp = cpu->icp;
     ICPStateClass *icpc;
 
     assert(icp);
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index 84d24b2837a7..178b3adc8af7 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -43,11 +43,9 @@
 static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                            target_ulong opcode, target_ulong *args)
 {
-    CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
     target_ulong cppr = args[0];
 
-    icp_set_cppr(icp, cppr);
+    icp_set_cppr(cpu->icp, cppr);
     return H_SUCCESS;
 }
 
@@ -69,9 +67,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                            target_ulong opcode, target_ulong *args)
 {
-    CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
-    uint32_t xirr = icp_accept(icp);
+    uint32_t xirr = icp_accept(cpu->icp);
 
     args[0] = xirr;
     return H_SUCCESS;
@@ -80,9 +76,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                              target_ulong opcode, target_ulong *args)
 {
-    CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
-    uint32_t xirr = icp_accept(icp);
+    uint32_t xirr = icp_accept(cpu->icp);
 
     args[0] = xirr;
     args[1] = cpu_get_host_ticks();
@@ -92,21 +86,17 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                           target_ulong opcode, target_ulong *args)
 {
-    CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
     target_ulong xirr = args[0];
 
-    icp_eoi(icp, xirr);
+    icp_eoi(cpu->icp, xirr);
     return H_SUCCESS;
 }
 
 static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                             target_ulong opcode, target_ulong *args)
 {
-    CPUState *cs = CPU(cpu);
-    ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
     uint32_t mfrr;
-    uint32_t xirr = icp_ipoll(icp, &mfrr);
+    uint32_t xirr = icp_ipoll(cpu->icp, &mfrr);
 
     args[0] = xirr;
     args[1] = mfrr;
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 6883f0991ae9..59f1cba6fba5 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -63,6 +63,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
                            Error **errp)
 {
     CPUPPCState *env = &cpu->env;
+    XICSFabric *xi = XICS_FABRIC(spapr);
 
     /* Set time-base frequency to 512 MHz */
     cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
@@ -80,7 +81,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
         }
     }
 
-    xics_cpu_setup(XICS_FABRIC(spapr), cpu);
+    cpu->icp = xics_icp_get(xi, CPU(cpu)->cpu_index);
+    xics_cpu_setup(xi, cpu);
 
     qemu_register_reset(spapr_cpu_reset, cpu);
     spapr_cpu_reset(cpu);
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 5ee33b3fd315..b1626d0a6607 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1176,6 +1176,7 @@ do {                                            \
 
 typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
 typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
+typedef struct ICPState ICPState;
 
 /**
  * PowerPCCPU:
@@ -1196,6 +1197,7 @@ struct PowerPCCPU {
     uint32_t max_compat;
     uint32_t compat_pvr;
     PPCVirtualHypervisor *vhyp;
+    ICPState *icp;
 
     /* Fields related to migration compatibility hacks */
     bool pre_2_8_migration;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-15  6:16   ` David Gibson
  2017-03-15  9:38     ` Benjamin Herrenschmidt
@ 2017-03-16 13:52     ` Cédric Le Goater
  2017-03-17  2:00       ` David Gibson
  2017-03-21 13:36       ` Cédric Le Goater
  1 sibling, 2 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-16 13:52 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt

On 03/15/2017 07:16 AM, David Gibson wrote:
> On Wed, Mar 08, 2017 at 11:52:49AM +0100, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> The PSI (Processor Service Interface) Controller is one of the engines
>> of the "Bridge" unit which connects the different interfaces to the
>> Power Processor.
>>
>> This adds just enough of the PSI bridge to handle various on-chip and
>> the one external interrupt. The rest of PSI has to do with the link to
>> the IBM FSP service processor which we don't plan to emulate (not used
>> on OpenPower machines).
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - updated for qemu-2.9
>>       - changed the XSCOM interface to fit new model
>>       - QOMified the model
>>       - reworked set_xive and worked around a skiboot bug
>>       - removed the 'psi_mmio_to_xscom' mapping array ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/Makefile.objs       |   2 +-
>>  hw/ppc/pnv.c               |  35 ++-
>>  hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h       |   8 +
>>  include/hw/ppc/pnv_psi.h   |  61 +++++
>>  include/hw/ppc/pnv_xscom.h |   3 +
>>  6 files changed, 685 insertions(+), 7 deletions(-)
>>  create mode 100644 hw/ppc/pnv_psi.c
>>  create mode 100644 include/hw/ppc/pnv_psi.h
>>
>> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
>> index 001293423c8d..dc19ee17fa57 100644
>> --- a/hw/ppc/Makefile.objs
>> +++ b/hw/ppc/Makefile.objs
>> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
>>  # IBM PowerNV
>> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
>> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
>>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>>  obj-y += spapr_pci_vfio.o
>>  endif
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 0ae11cc3a2ca..85b00bf235c6 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -356,15 +356,22 @@ static void ppc_powernv_reset(void)
>>   * have a CPLD that will collect the SerIRQ and shoot them as a
>>   * single level interrupt to the P8 chip. So let's setup a hook
>>   * for doing just that.
>> - *
>> - * Note: The actual interrupt input isn't emulated yet, this will
>> - * come with the PSI bridge model.
>>   */
>>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>>  {
>> -    /* We don't yet emulate the PSI bridge which provides the external
>> -     * interrupt, so just drop interrupts on the floor
>> -     */
>> +    PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
>> +    uint32_t old_state = pnv->cpld_irqstate;
>> +    PnvChip *chip = opaque;
>> +
>> +    if (level) {
>> +        pnv->cpld_irqstate |= 1u << n;
>> +    } else {
>> +        pnv->cpld_irqstate &= ~(1u << n);
>> +    }
>> +    if (pnv->cpld_irqstate != old_state) {
>> +        pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
>> +                        pnv->cpld_irqstate != 0);
>> +    }
>>  }
>>  
>>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
>> @@ -702,6 +709,11 @@ static void pnv_chip_init(Object *obj)
>>  
>>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
>>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
>> +
>> +    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
>> +    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
>> +    object_property_add_const_link(OBJECT(&chip->psi), "xics",
>> +                                   OBJECT(qdev_get_machine()), &error_abort);
>>  }
>>  
>>  static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
>> @@ -722,6 +734,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>      char *typename = pnv_core_typename(pcc->cpu_model);
>>      size_t typesize = object_type_get_instance_size(typename);
>>      int i, core_hwid;
>> +    MachineState *machine = MACHINE(qdev_get_machine());
>> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
>>  
>>      if (!object_class_by_name(typename)) {
>>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
>> @@ -797,6 +811,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>      }
>>      g_free(typename);
>>  
>> +
>> +    /* Processor Service Interface (PSI) Host Bridge */
>> +    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
>> +                             &error_fatal);
>> +    pnv_xscom_add_subregion(chip, PNV_XSCOM_PSI_BASE, &chip->psi.xscom_regs);
>> +
>> +    /* link in the PSI ICS */
>> +    QLIST_INSERT_HEAD(&pnv->ics, &chip->psi.ics, list);
>> +
>>      /* Create LPC controller */
>>      object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
>>                               &error_fatal);
>> diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
>> new file mode 100644
>> index 000000000000..6ba688aac075
>> --- /dev/null
>> +++ b/hw/ppc/pnv_psi.c
>> @@ -0,0 +1,583 @@
>> +/*
>> + * QEMU PowerNV PowerPC PSI interface
>> + *
>> + * Copyright (c) 2016, IBM Corporation
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "hw/hw.h"
>> +#include "target/ppc/cpu.h"
>> +#include "qemu/log.h"
>> +#include "qapi/error.h"
>> +
>> +#include "exec/address-spaces.h"
>> +
>> +#include "hw/ppc/fdt.h"
>> +#include "hw/ppc/pnv.h"
>> +#include "hw/ppc/pnv_xscom.h"
>> +#include "hw/ppc/pnv_psi.h"
>> +
>> +#include <libfdt.h>
>> +
>> +#define PSIHB_XSCOM_FIR_RW      0x00
>> +#define PSIHB_XSCOM_FIR_AND     0x01
>> +#define PSIHB_XSCOM_FIR_OR      0x02
>> +#define PSIHB_XSCOM_FIRMASK_RW  0x03
>> +#define PSIHB_XSCOM_FIRMASK_AND 0x04
>> +#define PSIHB_XSCOM_FIRMASK_OR  0x05
>> +#define PSIHB_XSCOM_FIRACT0     0x06
>> +#define PSIHB_XSCOM_FIRACT1     0x07
>> +#define PSIHB_XSCOM_BAR         0x0a
>> +#define   PSIHB_BAR_EN                  0x0000000000000001ull
>> +#define PSIHB_XSCOM_FSPBAR      0x0b
>> +#define PSIHB_XSCOM_CR          0x0e
>> +#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
>> +#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
>> +#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
>> +#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
>> +#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
>> +#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
>> +#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
>> +#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
>> +#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
>> +#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
>> +          /* and more ... */
>> +#define PSIHB_XSCOM_SEMR        0x0f
>> +#define PSIHB_XSCOM_XIVR_PSI    0x10
>> +#define   PSIHB_XIVR_SERVER_SH  40
>> +#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
>> +#define   PSIHB_XIVR_PRIO_SH    32
>> +#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
>> +#define   PSIHB_XIVR_SRC_SH             29
>> +#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
>> +#define   PSIHB_XIVR_PENDING    0x01000000ull
>> +#define PSIHB_XSCOM_SCR         0x12
>> +#define PSIHB_XSCOM_CCR         0x13
>> +#define PSIHB_XSCOM_DMA_UPADD   0x14
>> +#define PSIHB_XSCOM_IRQ_STAT    0x15
>> +#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
>> +#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
>> +#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
>> +#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
>> +#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
>> +#define PSIHB_XSCOM_XIVR_OCC    0x16
>> +#define PSIHB_XSCOM_XIVR_FSI    0x17
>> +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
>> +#define PSIHB_XSCOM_XIVR_LOCERR 0x19
>> +#define PSIHB_XSCOM_XIVR_EXT    0x1a
>> +#define PSIHB_XSCOM_IRSN        0x1b
>> +#define   PSIHB_IRSN_COMP_SH            45
>> +#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
>> +#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
>> +#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
>> +#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
>> +#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
>> +#define   PSIHB_IRSN_COMPMASK_SH        13
>> +#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
>> +
>> +/*
>> + * These are the values of the registers when accessed through the
>> + * MMIO region. The relation is xscom = (mmio + 0x50) >> 3
>> + */
>> +#define PSIHB_MMIO_BAR          0x00
>> +#define PSIHB_MMIO_FSPBAR       0x08
>> +#define PSIHB_MMIO_CR           0x20
>> +#define PSIHB_MMIO_SEMR         0x28
>> +#define PSIHB_MMIO_XIVR_PSI     0x30
>> +#define PSIHB_MMIO_SCR          0x40
>> +#define PSIHB_MMIO_CCR          0x48
>> +#define PSIHB_MMIO_DMA_UPADD    0x50
>> +#define PSIHB_MMIO_IRQ_STAT     0x58
>> +#define PSIHB_MMIO_XIVR_OCC     0x60
>> +#define PSIHB_MMIO_XIVR_FSI     0x68
>> +#define PSIHB_MMIO_XIVR_LPCI2C  0x70
>> +#define PSIHB_MMIO_XIVR_LOCERR  0x78
>> +#define PSIHB_MMIO_XIVR_EXT     0x80
>> +#define PSIHB_MMIO_IRSN         0x88
>> +#define PSIHB_MMIO_MAX          0x100
>> +
>> +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
>> +{
>> +    MemoryRegion *sysmem = get_system_memory();
>> +    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
>> +
>> +    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
>> +
>> +    /* Update MR, always remove it first */
>> +    if (old & PSIHB_BAR_EN) {
>> +        memory_region_del_subregion(sysmem, &psi->regs_mr);
>> +    }
>> +    /* Then add it back if needed */
>> +    if (bar & PSIHB_BAR_EN) {
>> +        uint64_t addr = bar & 0x0003fffffff00000;
>> +        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
>> +    }
>> +}
>> +
>> +static void pnv_psi_update_fsp_mr(PnvPsi *psi)
>> +{
>> +    /* XXX Update FSP MR if/when we support FSP BAR */
>> +}
>> +
>> +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr)
>> +{
>> +    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
>> +
>> +    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
>> +
>> +    /* Check some bit changes */
>> +    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
>> +        pnv_psi_update_fsp_mr(psi);
>> +    }
>> +}
>> +
>> +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val)
>> +{
>> +    uint32_t offset;
>> +    ICSState *ics = &psi->ics;
>> +
>> +    /* In this model we ignore the up/down enable bits for now
>> +     * as SW doesn't use them (other than setting them at boot).
>> +     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
>> +     * it and finally we ignore reset (XXX fix that ?)
>> +     */
>> +    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
>> +                                         PSIHB_IRSN_IRQ_MUX |
>> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
>> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
>> +                                         PSIHB_IRSN_DOWNSTREAM_EN);
>> +
>> +    /* We ignore the compare mask as well, our ICS emulation is too
>> +     * simplistic to make any use if it, and we extract the offset
>> +     * from the compare value
>> +     */
>> +    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
>> +    ics->offset = offset;
>> +}
>> +
>> +static bool pnv_psi_irq_bits(PnvPsi *psi, PnvPsiIrq irq,
>> +                             uint32_t *out_xivr_reg,
>> +                             uint32_t *out_stat_reg,
>> +                             uint64_t *out_stat_bit)
> 
> Your PnvPsiIrq values are arbitrary and contiguous AFACT.  Why not
> just have a lookup table for this info, instead of a giant switch
> statement?

Well, I agree but at the same time, we are not gaining much in terms
of lines by using an array, and we have to check for boundaries which
the switch provide. The question would be different if we had more 
parameters. So let's keep it that way. 
 
>> +{
>> +    switch (irq) {
>> +    case PSIHB_IRQ_PSI:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
>> +        *out_stat_reg = PSIHB_XSCOM_CR;
>> +        *out_stat_bit = PSIHB_CR_PSI_IRQ;
>> +        break;
>> +    case PSIHB_IRQ_FSP:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
>> +        *out_stat_reg = PSIHB_XSCOM_CR;
>> +        *out_stat_bit = PSIHB_CR_FSP_IRQ;
>> +        break;
>> +    case PSIHB_IRQ_OCC:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>> +        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
>> +        break;
>> +    case PSIHB_IRQ_FSI:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>> +        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
>> +        break;
>> +    case PSIHB_IRQ_LPC_I2C:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>> +        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
>> +        break;
>> +    case PSIHB_IRQ_LOCAL_ERR:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>> +        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
>> +        break;
>> +    case PSIHB_IRQ_EXTERNAL:
>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>> +        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
>> +        break;
>> +    default:
>> +        return false;
>> +    }
>> +    return true;
>> +}
>> +
>> +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
>> +{
>> +    ICSState *ics = &psi->ics;
>> +    uint32_t xivr_reg;
>> +    uint32_t stat_reg;
>> +    uint64_t stat_bit;
>> +    uint32_t src;
>> +    bool masked;
>> +
>> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
>> +        return;
>> +    }
>> +
>> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
>> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
>> +    if (state) {
>> +        psi->regs[stat_reg] |= stat_bit;
>> +        /* XXX optimization: check mask here. That means re-evaluating
>> +         * when unmasking, thus TODO
>> +         */
>> +        qemu_irq_raise(ics->qirqs[src]);
>> +    } else {
>> +        psi->regs[stat_reg] &= ~stat_bit;
>> +
>> +        /* FSP and PSI are muxed so don't lower if either still set */
>> +        if (stat_reg != PSIHB_XSCOM_CR ||
>> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
>> +            qemu_irq_lower(ics->qirqs[src]);
>> +        } else {
>> +            state = true;
>> +        }
>> +    }
> 
> It might be cleaner to just revaluate the irq level from scratch here,
> and set the level, rather than doing this complicated dance to work
> out if it has changed.

OK. I need to take a closer look at that.

>> +
>> +    /* XXX Note about the emulation of the pending bit: This isn't
>> +     * entirely correct. The pending bit should be cleared when the
>> +     * EOI has been received. However, we don't have callbacks on
>> +     * EOI (especially not under KVM) so no way to emulate that
>> +     * properly, so instead we just set that bit as the logical
>> +     * "output" of the XIVR (ie pending & !masked)
>> +     * XXX TODO: Also update it on set_xivr
>> +     */
>> +    if (state && !masked) {
>> +        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
>> +    } else {
>> +        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
>> +    }
>> +}
>> +
>> +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val)
>> +{
>> +    ICSState *ics = &psi->ics;
>> +    uint16_t server;
>> +    uint8_t prio;
>> +    uint8_t src;
>> +    int icp_index;
>> +
>> +    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
>> +            (val & (PSIHB_XIVR_SERVER_MSK |
>> +                    PSIHB_XIVR_PRIO_MSK |
>> +                    PSIHB_XIVR_SRC_MSK));
>> +    val = psi->regs[reg];
>> +    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
>> +    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
>> +    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
>> +    if (src > PSIHB_IRQ_EXTERNAL) {
>> +        /* XXX Generate error ? */
>> +        return;
>> +    }
>> +
>> +    /*
>> +     * Linux fills the irq xivr with the hw processor id plus the
>> +     * link bits. shift back to get something valid.
>> +     */
>> +    server >>= 2;
>> +
>> +    /*
>> +     * When skiboot initializes PSIHB, it fills the xives with
>> +     * server=0, prio=0xff, but we don't have a CPU with a pir=0. So
>> +     * skip that case.
>> +     */
>> +    if (prio != 0xff) {
>> +        icp_index = xics_get_cpu_index_by_pir(server);
>> +        assert(icp_index != -1);
>> +    } else {
>> +        if (server) {
>> +            qemu_log_mask(LOG_GUEST_ERROR, "PSI: bogus server %d for IRQ %d\n",
>> +                          server, src);
>> +        }
>> +        icp_index = server;
>> +    }
> 
> This logic doesn't seem like it belongs here.  You've received a
> server number, seems like you should just pass it on to the xics code,
> and if there can be an error, have that detect it.

I have removed all of that in a private patch. This is tracking 
an issue in skiboot which did not clear correctly the PSI xive. 
It is partially resolved in recent version but I still see some 
warnings whe the guest reboots.

>> +
>> +    /* Now because of source remapping, weird things can happen
>> +     * if you change the source number dynamically, our simple ICS
>> +     * doesn't deal with remapping. So we just poke a different
>> +     * ICS entry based on what source number was written. This will
>> +     * do for now but a more accurate implementation would instead
>> +     * use a fixed server/prio and a remapper of the generated irq.
>> +     */
>> +    ics_simple_write_xive(ics, src, icp_index, prio, prio);
>> +}
>> +
>> +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio)
>> +{
>> +    uint64_t val = 0xffffffffffffffffull;
>> +
>> +    switch (offset) {
>> +    case PSIHB_XSCOM_FIR_RW:
>> +    case PSIHB_XSCOM_FIRACT0:
>> +    case PSIHB_XSCOM_FIRACT1:
>> +    case PSIHB_XSCOM_BAR:
>> +    case PSIHB_XSCOM_FSPBAR:
>> +    case PSIHB_XSCOM_CR:
>> +    case PSIHB_XSCOM_XIVR_PSI:
>> +    case PSIHB_XSCOM_XIVR_OCC:
>> +    case PSIHB_XSCOM_XIVR_FSI:
>> +    case PSIHB_XSCOM_XIVR_LPCI2C:
>> +    case PSIHB_XSCOM_XIVR_LOCERR:
>> +    case PSIHB_XSCOM_XIVR_EXT:
>> +    case PSIHB_XSCOM_IRQ_STAT:
>> +    case PSIHB_XSCOM_SEMR:
>> +    case PSIHB_XSCOM_DMA_UPADD:
>> +    case PSIHB_XSCOM_IRSN:
>> +        val = psi->regs[offset];
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
>> +                      offset);
> 
> As noted elsewhere, tracepoints are more usual than qemu_log() these
> days.  But either way, this really should have a distinguishable
> message from the one in the write path.

OK. Will add a read/write statement. 

>> +    }
>> +    return val;
>> +}
>> +
>> +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val,
>> +                              bool mmio)
>> +{
>> +    switch (offset) {
>> +    case PSIHB_XSCOM_FIR_RW:
>> +    case PSIHB_XSCOM_FIRACT0:
>> +    case PSIHB_XSCOM_FIRACT1:
>> +    case PSIHB_XSCOM_SEMR:
>> +    case PSIHB_XSCOM_DMA_UPADD:
>> +        psi->regs[offset] = val;
>> +        break;
>> +    case PSIHB_XSCOM_FIR_OR:
>> +        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
>> +        break;
>> +    case PSIHB_XSCOM_FIR_AND:
>> +        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
>> +        break;
>> +    case PSIHB_XSCOM_BAR:
>> +        /* Only XSCOM can write this one */
>> +        if (!mmio) {
>> +            pnv_psi_set_bar(psi, val);
>> +        }
>> +        break;
>> +    case PSIHB_XSCOM_FSPBAR:
>> +        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
> 
> Should that be PSIHB_XSCOM_FSPBAR?

yes ...

>> +        pnv_psi_update_fsp_mr(psi);
>> +        break;
>> +    case PSIHB_XSCOM_CR:
>> +        pnv_psi_set_cr(psi, val);
>> +        break;
>> +    case PSIHB_XSCOM_SCR:
>> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
>> +        break;
>> +    case PSIHB_XSCOM_CCR:
>> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
>> +        break;
>> +    case PSIHB_XSCOM_XIVR_PSI:
>> +    case PSIHB_XSCOM_XIVR_OCC:
>> +    case PSIHB_XSCOM_XIVR_FSI:
>> +    case PSIHB_XSCOM_XIVR_LPCI2C:
>> +    case PSIHB_XSCOM_XIVR_LOCERR:
>> +    case PSIHB_XSCOM_XIVR_EXT:
>> +        pnv_psi_set_xivr(psi, offset, val);
>> +        break;
>> +    case PSIHB_XSCOM_IRQ_STAT:
>> +        /* Read only, should we generate an error ? */
>> +        break;
>> +    case PSIHB_XSCOM_IRSN:
>> +        pnv_psi_set_irsn(psi, val);
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
>> +                      offset);
>> +    }
>> +}
>> +
>> +static inline uint32_t psi_mmio_to_xscom(hwaddr addr)
>> +{
>> +    return (addr >> 3) + PSIHB_XSCOM_BAR;
>> +}
>> +
>> +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    return pnv_psi_reg_read(opaque, psi_mmio_to_xscom(addr), true);
>> +}
>> +
>> +static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
>> +                              uint64_t val, unsigned size)
>> +{
>> +    pnv_psi_reg_write(opaque, psi_mmio_to_xscom(addr), val, true);
>> +}
>> +
>> +static const MemoryRegionOps psi_mmio_ops = {
>> +    .read = pnv_psi_mmio_read,
>> +    .write = pnv_psi_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_xscom_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    PnvPsi *psi = PNV_PSI(opaque);
>> +    uint32_t offset = addr >> 3;
>> +
>> +    return pnv_psi_reg_read(psi, offset, false);
>> +}
>> +
>> +static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
>> +                                uint64_t val, unsigned size)
>> +{
>> +    PnvPsi *psi = PNV_PSI(opaque);
>> +    uint32_t offset = addr >> 3;
>> +
>> +    pnv_psi_reg_write(psi, offset, val, false);
>> +}
>> +
>> +static const MemoryRegionOps pnv_psi_xscom_ops = {
>> +    .read = pnv_psi_xscom_read,
>> +    .write = pnv_psi_xscom_write,
>> +    .valid.min_access_size = 8,
>> +    .valid.max_access_size = 8,
>> +    .impl.min_access_size = 8,
>> +    .impl.max_access_size = 8,
> 
> Consistent nesting format of the two MemoryRegionOps would be good.

OK.
 
>> +    .endianness = DEVICE_BIG_ENDIAN,
>> +};
>> +
>> +static void pnv_psi_init(Object *obj)
>> +{
>> +    PnvPsi *psi = PNV_PSI(obj);
>> +
>> +    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
>> +    qdev_set_parent_bus(DEVICE(&psi->ics), sysbus_get_default());
>> +    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
>> +}
>> +
>> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
>> +{
>> +    PnvPsi *psi = PNV_PSI(dev);
>> +    ICSState *ics = &psi->ics;
>> +    Object *obj;
>> +    Error *err = NULL, *local_err = NULL;
>> +    unsigned int i;
>> +
>> +    /* Initialize MMIO region */
>> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
>> +                          "psihb", PNV_PSIHB_BAR_SIZE);
>> +
>> +    /* Default BAR. Use object properties ? */
>> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
>> +
>> +    /* Default sources in XIVR */
>> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
>> +            (0ull << PSIHB_XIVR_SRC_SH);
>> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
>> +            (1ull << PSIHB_XIVR_SRC_SH);
>> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
>> +            (2ull << PSIHB_XIVR_SRC_SH);
>> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
>> +            (3ull << PSIHB_XIVR_SRC_SH);
>> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
>> +            (4ull << PSIHB_XIVR_SRC_SH);
>> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
>> +            (5ull << PSIHB_XIVR_SRC_SH);
>> +
>> +    /* get XICSFabric from chip */
>> +    obj = object_property_get_link(OBJECT(dev), "xics", &err);
>> +    if (!obj) {
>> +        error_setg(errp, "%s: required link 'xics' not found: %s",
>> +                   __func__, error_get_pretty(err));
>> +        return;
>> +    }
>> +
>> +    /*
>> +     * PSI interrupt control source
>> +     */
>> +    object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
>> +    object_property_add_const_link(OBJECT(ics), "xics", obj, &err);
>> +    object_property_set_bool(OBJECT(ics), true, "realized",  &local_err);
>> +    error_propagate(&err, local_err);
>> +    if (err) {
>> +        error_propagate(errp, err);
>> +        return;
>> +    }
>> +
>> +    for (i = 0; i < ics->nr_irqs; i++) {
>> +        ics_set_irq_type(ics, i, true);
>> +    }
>> +
>> +    /* XScom region for PSI registers */
>> +    pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
>> +                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE);
>> +}
>> +
>> +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
>> +{
>> +    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
>> +    char *name;
>> +    int offset;
>> +    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
>> +    uint32_t reg[] = {
>> +        cpu_to_be32(lpc_pcba),
>> +        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
>> +    };
>> +
>> +    name = g_strdup_printf("psihb@%x", lpc_pcba);
>> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
>> +    _FDT(offset);
>> +    g_free(name);
>> +
>> +    _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,
>> +                      sizeof(compat))));
>> +    return 0;
>> +}
>> +
>> +
>> +static void pnv_psi_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
>> +
>> +    xdc->populate = pnv_psi_populate;
>> +
>> +    dc->realize = pnv_psi_realize;
>> +}
>> +
>> +static const TypeInfo pnv_psi_info = {
>> +    .name          = TYPE_PNV_PSI,
>> +    .parent        = TYPE_DEVICE,
> 
> Since the PSI has an MMIO presence, it probably should be a
> SysBusDevice, rather than a raw descendent of TYPE_DEVICE.

Yes indeed. 

So I will resend the patchset with just the XICS part. I want to 
take a look at pnv_psi_irq_set() first.

Thanks,

C. 

>> +    .instance_size = sizeof(PnvPsi),
>> +    .instance_init = pnv_psi_init,
>> +    .class_init    = pnv_psi_class_init,
>> +    .interfaces    = (InterfaceInfo[]) {
>> +        { TYPE_PNV_XSCOM_INTERFACE },
>> +        { }
>> +    }
>> +};
>> +
>> +static void pnv_psi_register_types(void)
>> +{
>> +    type_register_static(&pnv_psi_info);
>> +}
>> +
>> +type_init(pnv_psi_register_types)
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index f11215ea31f2..f93ec32603b7 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -23,6 +23,7 @@
>>  #include "hw/sysbus.h"
>>  #include "hw/ppc/pnv_lpc.h"
>>  #include "hw/ppc/xics.h"
>> +#include "hw/ppc/pnv_psi.h"
>>  
>>  #define TYPE_PNV_CHIP "powernv-chip"
>>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>> @@ -58,6 +59,7 @@ typedef struct PnvChip {
>>      MemoryRegion icp_mmio;
>>  
>>      PnvLpcController lpc;
>> +    PnvPsi       psi;
>>  } PnvChip;
>>  
>>  typedef struct PnvChipClass {
>> @@ -119,6 +121,8 @@ typedef struct PnvMachineState {
>>      ICPState     *icps;
>>      uint32_t     nr_servers;
>>      QLIST_HEAD(, ICSState) ics;
>> +
>> +    uint32_t     cpld_irqstate;
>>  } PnvMachineState;
>>  
>>  #define PNV_FDT_ADDR          0x01000000
>> @@ -150,4 +154,8 @@ typedef struct PnvMachineState {
>>  #define PNV_ICP_BASE(chip)   0x0003ffff80000000ull
>>  #define PNV_ICP_SIZE         0x0000000000100000ull
>>  
>> +#define PNV_PSIHB_BAR         0x0003fffe80000000ull
>> +#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
>> +
>> +
>>  #endif /* _PPC_PNV_H */
>> diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
>> new file mode 100644
>> index 000000000000..ac3c5f8362e3
>> --- /dev/null
>> +++ b/include/hw/ppc/pnv_psi.h
>> @@ -0,0 +1,61 @@
>> +/*
>> + * QEMU PowerPC PowerNV PSI controller
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef _PPC_PNV_PSI_H
>> +#define _PPC_PNV_PSI_H
>> +
>> +#define TYPE_PNV_PSI "pnv-psi"
>> +#define PNV_PSI(obj) \
>> +     OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI)
>> +
>> +#define PSIHB_XSCOM_MAX         0x20
>> +
>> +typedef struct XICSState XICSState;
>> +
>> +typedef struct PnvPsi {
>> +    DeviceState parent;
>> +
>> +    MemoryRegion regs_mr;
>> +
>> +    /* FSP region not supported */
>> +    /* MemoryRegion fsp_mr; */
>> +
>> +    /* Interrupt generation */
>> +    ICSState ics;
>> +
>> +    /* Registers */
>> +    uint64_t regs[PSIHB_XSCOM_MAX];
>> +
>> +    MemoryRegion xscom_regs;
>> +} PnvPsi;
>> +
>> +typedef enum PnvPsiIrq {
>> +    PSIHB_IRQ_PSI, /* internal use only */
>> +    PSIHB_IRQ_FSP, /* internal use only */
>> +    PSIHB_IRQ_OCC,
>> +    PSIHB_IRQ_FSI,
>> +    PSIHB_IRQ_LPC_I2C,
>> +    PSIHB_IRQ_LOCAL_ERR,
>> +    PSIHB_IRQ_EXTERNAL,
>> +} PnvPsiIrq;
>> +
>> +#define PSI_NUM_INTERRUPTS 6
>> +
>> +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state);
>> +
>> +#endif /* _PPC_PNV_PSI_H */
>> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
>> index 0faa1847bf13..2938abd74955 100644
>> --- a/include/hw/ppc/pnv_xscom.h
>> +++ b/include/hw/ppc/pnv_xscom.h
>> @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
>>  #define PNV_XSCOM_LPC_BASE        0xb0020
>>  #define PNV_XSCOM_LPC_SIZE        0x4
>>  
>> +#define PNV_XSCOM_PSI_BASE        0x2010900
>> +#define PNV_XSCOM_PSI_SIZE        0x20
>> +
>>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>>  
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-16 13:52     ` Cédric Le Goater
@ 2017-03-17  2:00       ` David Gibson
  2017-03-17  8:27         ` Cédric Le Goater
  2017-03-21 13:36       ` Cédric Le Goater
  1 sibling, 1 reply; 29+ messages in thread
From: David Gibson @ 2017-03-17  2:00 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 35412 bytes --]

On Thu, Mar 16, 2017 at 02:52:17PM +0100, Cédric Le Goater wrote:
> On 03/15/2017 07:16 AM, David Gibson wrote:
> > On Wed, Mar 08, 2017 at 11:52:49AM +0100, Cédric Le Goater wrote:
> >> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >>
> >> The PSI (Processor Service Interface) Controller is one of the engines
> >> of the "Bridge" unit which connects the different interfaces to the
> >> Power Processor.
> >>
> >> This adds just enough of the PSI bridge to handle various on-chip and
> >> the one external interrupt. The rest of PSI has to do with the link to
> >> the IBM FSP service processor which we don't plan to emulate (not used
> >> on OpenPower machines).
> >>
> >> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >> [clg: - updated for qemu-2.9
> >>       - changed the XSCOM interface to fit new model
> >>       - QOMified the model
> >>       - reworked set_xive and worked around a skiboot bug
> >>       - removed the 'psi_mmio_to_xscom' mapping array ]
> >> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> >> ---
> >>  hw/ppc/Makefile.objs       |   2 +-
> >>  hw/ppc/pnv.c               |  35 ++-
> >>  hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
> >>  include/hw/ppc/pnv.h       |   8 +
> >>  include/hw/ppc/pnv_psi.h   |  61 +++++
> >>  include/hw/ppc/pnv_xscom.h |   3 +
> >>  6 files changed, 685 insertions(+), 7 deletions(-)
> >>  create mode 100644 hw/ppc/pnv_psi.c
> >>  create mode 100644 include/hw/ppc/pnv_psi.h
> >>
> >> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> >> index 001293423c8d..dc19ee17fa57 100644
> >> --- a/hw/ppc/Makefile.objs
> >> +++ b/hw/ppc/Makefile.objs
> >> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
> >>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
> >>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
> >>  # IBM PowerNV
> >> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
> >> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
> >>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
> >>  obj-y += spapr_pci_vfio.o
> >>  endif
> >> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> >> index 0ae11cc3a2ca..85b00bf235c6 100644
> >> --- a/hw/ppc/pnv.c
> >> +++ b/hw/ppc/pnv.c
> >> @@ -356,15 +356,22 @@ static void ppc_powernv_reset(void)
> >>   * have a CPLD that will collect the SerIRQ and shoot them as a
> >>   * single level interrupt to the P8 chip. So let's setup a hook
> >>   * for doing just that.
> >> - *
> >> - * Note: The actual interrupt input isn't emulated yet, this will
> >> - * come with the PSI bridge model.
> >>   */
> >>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> >>  {
> >> -    /* We don't yet emulate the PSI bridge which provides the external
> >> -     * interrupt, so just drop interrupts on the floor
> >> -     */
> >> +    PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> >> +    uint32_t old_state = pnv->cpld_irqstate;
> >> +    PnvChip *chip = opaque;
> >> +
> >> +    if (level) {
> >> +        pnv->cpld_irqstate |= 1u << n;
> >> +    } else {
> >> +        pnv->cpld_irqstate &= ~(1u << n);
> >> +    }
> >> +    if (pnv->cpld_irqstate != old_state) {
> >> +        pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
> >> +                        pnv->cpld_irqstate != 0);
> >> +    }
> >>  }
> >>  
> >>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> >> @@ -702,6 +709,11 @@ static void pnv_chip_init(Object *obj)
> >>  
> >>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
> >>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
> >> +
> >> +    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
> >> +    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
> >> +    object_property_add_const_link(OBJECT(&chip->psi), "xics",
> >> +                                   OBJECT(qdev_get_machine()), &error_abort);
> >>  }
> >>  
> >>  static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
> >> @@ -722,6 +734,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>      char *typename = pnv_core_typename(pcc->cpu_model);
> >>      size_t typesize = object_type_get_instance_size(typename);
> >>      int i, core_hwid;
> >> +    MachineState *machine = MACHINE(qdev_get_machine());
> >> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
> >>  
> >>      if (!object_class_by_name(typename)) {
> >>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
> >> @@ -797,6 +811,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>      }
> >>      g_free(typename);
> >>  
> >> +
> >> +    /* Processor Service Interface (PSI) Host Bridge */
> >> +    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
> >> +                             &error_fatal);
> >> +    pnv_xscom_add_subregion(chip, PNV_XSCOM_PSI_BASE, &chip->psi.xscom_regs);
> >> +
> >> +    /* link in the PSI ICS */
> >> +    QLIST_INSERT_HEAD(&pnv->ics, &chip->psi.ics, list);
> >> +
> >>      /* Create LPC controller */
> >>      object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
> >>                               &error_fatal);
> >> diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
> >> new file mode 100644
> >> index 000000000000..6ba688aac075
> >> --- /dev/null
> >> +++ b/hw/ppc/pnv_psi.c
> >> @@ -0,0 +1,583 @@
> >> +/*
> >> + * QEMU PowerNV PowerPC PSI interface
> >> + *
> >> + * Copyright (c) 2016, IBM Corporation
> >> + *
> >> + * This library is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2 of the License, or (at your option) any later version.
> >> + *
> >> + * This library is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> >> + */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "hw/hw.h"
> >> +#include "target/ppc/cpu.h"
> >> +#include "qemu/log.h"
> >> +#include "qapi/error.h"
> >> +
> >> +#include "exec/address-spaces.h"
> >> +
> >> +#include "hw/ppc/fdt.h"
> >> +#include "hw/ppc/pnv.h"
> >> +#include "hw/ppc/pnv_xscom.h"
> >> +#include "hw/ppc/pnv_psi.h"
> >> +
> >> +#include <libfdt.h>
> >> +
> >> +#define PSIHB_XSCOM_FIR_RW      0x00
> >> +#define PSIHB_XSCOM_FIR_AND     0x01
> >> +#define PSIHB_XSCOM_FIR_OR      0x02
> >> +#define PSIHB_XSCOM_FIRMASK_RW  0x03
> >> +#define PSIHB_XSCOM_FIRMASK_AND 0x04
> >> +#define PSIHB_XSCOM_FIRMASK_OR  0x05
> >> +#define PSIHB_XSCOM_FIRACT0     0x06
> >> +#define PSIHB_XSCOM_FIRACT1     0x07
> >> +#define PSIHB_XSCOM_BAR         0x0a
> >> +#define   PSIHB_BAR_EN                  0x0000000000000001ull
> >> +#define PSIHB_XSCOM_FSPBAR      0x0b
> >> +#define PSIHB_XSCOM_CR          0x0e
> >> +#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
> >> +#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
> >> +#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
> >> +#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
> >> +#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
> >> +#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
> >> +#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
> >> +#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
> >> +#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
> >> +#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
> >> +          /* and more ... */
> >> +#define PSIHB_XSCOM_SEMR        0x0f
> >> +#define PSIHB_XSCOM_XIVR_PSI    0x10
> >> +#define   PSIHB_XIVR_SERVER_SH  40
> >> +#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
> >> +#define   PSIHB_XIVR_PRIO_SH    32
> >> +#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
> >> +#define   PSIHB_XIVR_SRC_SH             29
> >> +#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
> >> +#define   PSIHB_XIVR_PENDING    0x01000000ull
> >> +#define PSIHB_XSCOM_SCR         0x12
> >> +#define PSIHB_XSCOM_CCR         0x13
> >> +#define PSIHB_XSCOM_DMA_UPADD   0x14
> >> +#define PSIHB_XSCOM_IRQ_STAT    0x15
> >> +#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
> >> +#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
> >> +#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
> >> +#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
> >> +#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
> >> +#define PSIHB_XSCOM_XIVR_OCC    0x16
> >> +#define PSIHB_XSCOM_XIVR_FSI    0x17
> >> +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
> >> +#define PSIHB_XSCOM_XIVR_LOCERR 0x19
> >> +#define PSIHB_XSCOM_XIVR_EXT    0x1a
> >> +#define PSIHB_XSCOM_IRSN        0x1b
> >> +#define   PSIHB_IRSN_COMP_SH            45
> >> +#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
> >> +#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
> >> +#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
> >> +#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
> >> +#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
> >> +#define   PSIHB_IRSN_COMPMASK_SH        13
> >> +#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
> >> +
> >> +/*
> >> + * These are the values of the registers when accessed through the
> >> + * MMIO region. The relation is xscom = (mmio + 0x50) >> 3
> >> + */
> >> +#define PSIHB_MMIO_BAR          0x00
> >> +#define PSIHB_MMIO_FSPBAR       0x08
> >> +#define PSIHB_MMIO_CR           0x20
> >> +#define PSIHB_MMIO_SEMR         0x28
> >> +#define PSIHB_MMIO_XIVR_PSI     0x30
> >> +#define PSIHB_MMIO_SCR          0x40
> >> +#define PSIHB_MMIO_CCR          0x48
> >> +#define PSIHB_MMIO_DMA_UPADD    0x50
> >> +#define PSIHB_MMIO_IRQ_STAT     0x58
> >> +#define PSIHB_MMIO_XIVR_OCC     0x60
> >> +#define PSIHB_MMIO_XIVR_FSI     0x68
> >> +#define PSIHB_MMIO_XIVR_LPCI2C  0x70
> >> +#define PSIHB_MMIO_XIVR_LOCERR  0x78
> >> +#define PSIHB_MMIO_XIVR_EXT     0x80
> >> +#define PSIHB_MMIO_IRSN         0x88
> >> +#define PSIHB_MMIO_MAX          0x100
> >> +
> >> +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
> >> +{
> >> +    MemoryRegion *sysmem = get_system_memory();
> >> +    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
> >> +
> >> +    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
> >> +
> >> +    /* Update MR, always remove it first */
> >> +    if (old & PSIHB_BAR_EN) {
> >> +        memory_region_del_subregion(sysmem, &psi->regs_mr);
> >> +    }
> >> +    /* Then add it back if needed */
> >> +    if (bar & PSIHB_BAR_EN) {
> >> +        uint64_t addr = bar & 0x0003fffffff00000;
> >> +        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
> >> +    }
> >> +}
> >> +
> >> +static void pnv_psi_update_fsp_mr(PnvPsi *psi)
> >> +{
> >> +    /* XXX Update FSP MR if/when we support FSP BAR */
> >> +}
> >> +
> >> +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr)
> >> +{
> >> +    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
> >> +
> >> +    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
> >> +
> >> +    /* Check some bit changes */
> >> +    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
> >> +        pnv_psi_update_fsp_mr(psi);
> >> +    }
> >> +}
> >> +
> >> +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val)
> >> +{
> >> +    uint32_t offset;
> >> +    ICSState *ics = &psi->ics;
> >> +
> >> +    /* In this model we ignore the up/down enable bits for now
> >> +     * as SW doesn't use them (other than setting them at boot).
> >> +     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
> >> +     * it and finally we ignore reset (XXX fix that ?)
> >> +     */
> >> +    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
> >> +                                         PSIHB_IRSN_IRQ_MUX |
> >> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> >> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> >> +                                         PSIHB_IRSN_DOWNSTREAM_EN);
> >> +
> >> +    /* We ignore the compare mask as well, our ICS emulation is too
> >> +     * simplistic to make any use if it, and we extract the offset
> >> +     * from the compare value
> >> +     */
> >> +    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
> >> +    ics->offset = offset;
> >> +}
> >> +
> >> +static bool pnv_psi_irq_bits(PnvPsi *psi, PnvPsiIrq irq,
> >> +                             uint32_t *out_xivr_reg,
> >> +                             uint32_t *out_stat_reg,
> >> +                             uint64_t *out_stat_bit)
> > 
> > Your PnvPsiIrq values are arbitrary and contiguous AFACT.  Why not
> > just have a lookup table for this info, instead of a giant switch
> > statement?
> 
> Well, I agree but at the same time, we are not gaining much in terms
> of lines by using an array,

Hmm.. seems like an ~ x5 line reduction to me...

And, IMO, easier to read.

> and we have to check for boundaries which
> the switch provide. The question would be different if we had more 
> parameters. So let's keep it that way. 
>  
> >> +{
> >> +    switch (irq) {
> >> +    case PSIHB_IRQ_PSI:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> >> +        *out_stat_reg = PSIHB_XSCOM_CR;
> >> +        *out_stat_bit = PSIHB_CR_PSI_IRQ;
> >> +        break;
> >> +    case PSIHB_IRQ_FSP:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> >> +        *out_stat_reg = PSIHB_XSCOM_CR;
> >> +        *out_stat_bit = PSIHB_CR_FSP_IRQ;
> >> +        break;
> >> +    case PSIHB_IRQ_OCC:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
> >> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> >> +        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
> >> +        break;
> >> +    case PSIHB_IRQ_FSI:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
> >> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> >> +        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
> >> +        break;
> >> +    case PSIHB_IRQ_LPC_I2C:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
> >> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> >> +        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
> >> +        break;
> >> +    case PSIHB_IRQ_LOCAL_ERR:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
> >> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> >> +        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
> >> +        break;
> >> +    case PSIHB_IRQ_EXTERNAL:
> >> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
> >> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> >> +        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
> >> +        break;
> >> +    default:
> >> +        return false;
> >> +    }
> >> +    return true;
> >> +}
> >> +
> >> +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
> >> +{
> >> +    ICSState *ics = &psi->ics;
> >> +    uint32_t xivr_reg;
> >> +    uint32_t stat_reg;
> >> +    uint64_t stat_bit;
> >> +    uint32_t src;
> >> +    bool masked;
> >> +
> >> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
> >> +        return;
> >> +    }
> >> +
> >> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> >> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
> >> +    if (state) {
> >> +        psi->regs[stat_reg] |= stat_bit;
> >> +        /* XXX optimization: check mask here. That means re-evaluating
> >> +         * when unmasking, thus TODO
> >> +         */
> >> +        qemu_irq_raise(ics->qirqs[src]);
> >> +    } else {
> >> +        psi->regs[stat_reg] &= ~stat_bit;
> >> +
> >> +        /* FSP and PSI are muxed so don't lower if either still set */
> >> +        if (stat_reg != PSIHB_XSCOM_CR ||
> >> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
> >> +            qemu_irq_lower(ics->qirqs[src]);
> >> +        } else {
> >> +            state = true;
> >> +        }
> >> +    }
> > 
> > It might be cleaner to just revaluate the irq level from scratch here,
> > and set the level, rather than doing this complicated dance to work
> > out if it has changed.
> 
> OK. I need to take a closer look at that.
> 
> >> +
> >> +    /* XXX Note about the emulation of the pending bit: This isn't
> >> +     * entirely correct. The pending bit should be cleared when the
> >> +     * EOI has been received. However, we don't have callbacks on
> >> +     * EOI (especially not under KVM) so no way to emulate that
> >> +     * properly, so instead we just set that bit as the logical
> >> +     * "output" of the XIVR (ie pending & !masked)
> >> +     * XXX TODO: Also update it on set_xivr
> >> +     */
> >> +    if (state && !masked) {
> >> +        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
> >> +    } else {
> >> +        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
> >> +    }
> >> +}
> >> +
> >> +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val)
> >> +{
> >> +    ICSState *ics = &psi->ics;
> >> +    uint16_t server;
> >> +    uint8_t prio;
> >> +    uint8_t src;
> >> +    int icp_index;
> >> +
> >> +    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
> >> +            (val & (PSIHB_XIVR_SERVER_MSK |
> >> +                    PSIHB_XIVR_PRIO_MSK |
> >> +                    PSIHB_XIVR_SRC_MSK));
> >> +    val = psi->regs[reg];
> >> +    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
> >> +    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
> >> +    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> >> +    if (src > PSIHB_IRQ_EXTERNAL) {
> >> +        /* XXX Generate error ? */
> >> +        return;
> >> +    }
> >> +
> >> +    /*
> >> +     * Linux fills the irq xivr with the hw processor id plus the
> >> +     * link bits. shift back to get something valid.
> >> +     */
> >> +    server >>= 2;
> >> +
> >> +    /*
> >> +     * When skiboot initializes PSIHB, it fills the xives with
> >> +     * server=0, prio=0xff, but we don't have a CPU with a pir=0. So
> >> +     * skip that case.
> >> +     */
> >> +    if (prio != 0xff) {
> >> +        icp_index = xics_get_cpu_index_by_pir(server);
> >> +        assert(icp_index != -1);
> >> +    } else {
> >> +        if (server) {
> >> +            qemu_log_mask(LOG_GUEST_ERROR, "PSI: bogus server %d for IRQ %d\n",
> >> +                          server, src);
> >> +        }
> >> +        icp_index = server;
> >> +    }
> > 
> > This logic doesn't seem like it belongs here.  You've received a
> > server number, seems like you should just pass it on to the xics code,
> > and if there can be an error, have that detect it.
> 
> I have removed all of that in a private patch. This is tracking 
> an issue in skiboot which did not clear correctly the PSI xive. 
> It is partially resolved in recent version but I still see some 
> warnings whe the guest reboots.

Ok.  Workarounds for firmware bugs are fine, but they need a detailed
comment so other people have a hope of understanding why they're
there.  Including stating outright that it is a bug workaround, rather
than expected firmware behaviour.

> 
> >> +
> >> +    /* Now because of source remapping, weird things can happen
> >> +     * if you change the source number dynamically, our simple ICS
> >> +     * doesn't deal with remapping. So we just poke a different
> >> +     * ICS entry based on what source number was written. This will
> >> +     * do for now but a more accurate implementation would instead
> >> +     * use a fixed server/prio and a remapper of the generated irq.
> >> +     */
> >> +    ics_simple_write_xive(ics, src, icp_index, prio, prio);
> >> +}
> >> +
> >> +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio)
> >> +{
> >> +    uint64_t val = 0xffffffffffffffffull;
> >> +
> >> +    switch (offset) {
> >> +    case PSIHB_XSCOM_FIR_RW:
> >> +    case PSIHB_XSCOM_FIRACT0:
> >> +    case PSIHB_XSCOM_FIRACT1:
> >> +    case PSIHB_XSCOM_BAR:
> >> +    case PSIHB_XSCOM_FSPBAR:
> >> +    case PSIHB_XSCOM_CR:
> >> +    case PSIHB_XSCOM_XIVR_PSI:
> >> +    case PSIHB_XSCOM_XIVR_OCC:
> >> +    case PSIHB_XSCOM_XIVR_FSI:
> >> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> >> +    case PSIHB_XSCOM_XIVR_LOCERR:
> >> +    case PSIHB_XSCOM_XIVR_EXT:
> >> +    case PSIHB_XSCOM_IRQ_STAT:
> >> +    case PSIHB_XSCOM_SEMR:
> >> +    case PSIHB_XSCOM_DMA_UPADD:
> >> +    case PSIHB_XSCOM_IRSN:
> >> +        val = psi->regs[offset];
> >> +        break;
> >> +    default:
> >> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
> >> +                      offset);
> > 
> > As noted elsewhere, tracepoints are more usual than qemu_log() these
> > days.  But either way, this really should have a distinguishable
> > message from the one in the write path.
> 
> OK. Will add a read/write statement. 
> 
> >> +    }
> >> +    return val;
> >> +}
> >> +
> >> +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val,
> >> +                              bool mmio)
> >> +{
> >> +    switch (offset) {
> >> +    case PSIHB_XSCOM_FIR_RW:
> >> +    case PSIHB_XSCOM_FIRACT0:
> >> +    case PSIHB_XSCOM_FIRACT1:
> >> +    case PSIHB_XSCOM_SEMR:
> >> +    case PSIHB_XSCOM_DMA_UPADD:
> >> +        psi->regs[offset] = val;
> >> +        break;
> >> +    case PSIHB_XSCOM_FIR_OR:
> >> +        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
> >> +        break;
> >> +    case PSIHB_XSCOM_FIR_AND:
> >> +        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
> >> +        break;
> >> +    case PSIHB_XSCOM_BAR:
> >> +        /* Only XSCOM can write this one */
> >> +        if (!mmio) {
> >> +            pnv_psi_set_bar(psi, val);
> >> +        }
> >> +        break;
> >> +    case PSIHB_XSCOM_FSPBAR:
> >> +        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
> > 
> > Should that be PSIHB_XSCOM_FSPBAR?
> 
> yes ...
> 
> >> +        pnv_psi_update_fsp_mr(psi);
> >> +        break;
> >> +    case PSIHB_XSCOM_CR:
> >> +        pnv_psi_set_cr(psi, val);
> >> +        break;
> >> +    case PSIHB_XSCOM_SCR:
> >> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
> >> +        break;
> >> +    case PSIHB_XSCOM_CCR:
> >> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
> >> +        break;
> >> +    case PSIHB_XSCOM_XIVR_PSI:
> >> +    case PSIHB_XSCOM_XIVR_OCC:
> >> +    case PSIHB_XSCOM_XIVR_FSI:
> >> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> >> +    case PSIHB_XSCOM_XIVR_LOCERR:
> >> +    case PSIHB_XSCOM_XIVR_EXT:
> >> +        pnv_psi_set_xivr(psi, offset, val);
> >> +        break;
> >> +    case PSIHB_XSCOM_IRQ_STAT:
> >> +        /* Read only, should we generate an error ? */
> >> +        break;
> >> +    case PSIHB_XSCOM_IRSN:
> >> +        pnv_psi_set_irsn(psi, val);
> >> +        break;
> >> +    default:
> >> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
> >> +                      offset);
> >> +    }
> >> +}
> >> +
> >> +static inline uint32_t psi_mmio_to_xscom(hwaddr addr)
> >> +{
> >> +    return (addr >> 3) + PSIHB_XSCOM_BAR;
> >> +}
> >> +
> >> +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
> >> +{
> >> +    return pnv_psi_reg_read(opaque, psi_mmio_to_xscom(addr), true);
> >> +}
> >> +
> >> +static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
> >> +                              uint64_t val, unsigned size)
> >> +{
> >> +    pnv_psi_reg_write(opaque, psi_mmio_to_xscom(addr), val, true);
> >> +}
> >> +
> >> +static const MemoryRegionOps psi_mmio_ops = {
> >> +    .read = pnv_psi_mmio_read,
> >> +    .write = pnv_psi_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_xscom_read(void *opaque, hwaddr addr, unsigned size)
> >> +{
> >> +    PnvPsi *psi = PNV_PSI(opaque);
> >> +    uint32_t offset = addr >> 3;
> >> +
> >> +    return pnv_psi_reg_read(psi, offset, false);
> >> +}
> >> +
> >> +static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
> >> +                                uint64_t val, unsigned size)
> >> +{
> >> +    PnvPsi *psi = PNV_PSI(opaque);
> >> +    uint32_t offset = addr >> 3;
> >> +
> >> +    pnv_psi_reg_write(psi, offset, val, false);
> >> +}
> >> +
> >> +static const MemoryRegionOps pnv_psi_xscom_ops = {
> >> +    .read = pnv_psi_xscom_read,
> >> +    .write = pnv_psi_xscom_write,
> >> +    .valid.min_access_size = 8,
> >> +    .valid.max_access_size = 8,
> >> +    .impl.min_access_size = 8,
> >> +    .impl.max_access_size = 8,
> > 
> > Consistent nesting format of the two MemoryRegionOps would be good.
> 
> OK.
>  
> >> +    .endianness = DEVICE_BIG_ENDIAN,
> >> +};
> >> +
> >> +static void pnv_psi_init(Object *obj)
> >> +{
> >> +    PnvPsi *psi = PNV_PSI(obj);
> >> +
> >> +    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
> >> +    qdev_set_parent_bus(DEVICE(&psi->ics), sysbus_get_default());
> >> +    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
> >> +}
> >> +
> >> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
> >> +{
> >> +    PnvPsi *psi = PNV_PSI(dev);
> >> +    ICSState *ics = &psi->ics;
> >> +    Object *obj;
> >> +    Error *err = NULL, *local_err = NULL;
> >> +    unsigned int i;
> >> +
> >> +    /* Initialize MMIO region */
> >> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
> >> +                          "psihb", PNV_PSIHB_BAR_SIZE);
> >> +
> >> +    /* Default BAR. Use object properties ? */
> >> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
> >> +
> >> +    /* Default sources in XIVR */
> >> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
> >> +            (0ull << PSIHB_XIVR_SRC_SH);
> >> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
> >> +            (1ull << PSIHB_XIVR_SRC_SH);
> >> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
> >> +            (2ull << PSIHB_XIVR_SRC_SH);
> >> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
> >> +            (3ull << PSIHB_XIVR_SRC_SH);
> >> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
> >> +            (4ull << PSIHB_XIVR_SRC_SH);
> >> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
> >> +            (5ull << PSIHB_XIVR_SRC_SH);
> >> +
> >> +    /* get XICSFabric from chip */
> >> +    obj = object_property_get_link(OBJECT(dev), "xics", &err);
> >> +    if (!obj) {
> >> +        error_setg(errp, "%s: required link 'xics' not found: %s",
> >> +                   __func__, error_get_pretty(err));
> >> +        return;
> >> +    }
> >> +
> >> +    /*
> >> +     * PSI interrupt control source
> >> +     */
> >> +    object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
> >> +    object_property_add_const_link(OBJECT(ics), "xics", obj, &err);
> >> +    object_property_set_bool(OBJECT(ics), true, "realized",  &local_err);
> >> +    error_propagate(&err, local_err);
> >> +    if (err) {
> >> +        error_propagate(errp, err);
> >> +        return;
> >> +    }
> >> +
> >> +    for (i = 0; i < ics->nr_irqs; i++) {
> >> +        ics_set_irq_type(ics, i, true);
> >> +    }
> >> +
> >> +    /* XScom region for PSI registers */
> >> +    pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
> >> +                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE);
> >> +}
> >> +
> >> +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> >> +{
> >> +    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
> >> +    char *name;
> >> +    int offset;
> >> +    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
> >> +    uint32_t reg[] = {
> >> +        cpu_to_be32(lpc_pcba),
> >> +        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
> >> +    };
> >> +
> >> +    name = g_strdup_printf("psihb@%x", lpc_pcba);
> >> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
> >> +    _FDT(offset);
> >> +    g_free(name);
> >> +
> >> +    _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,
> >> +                      sizeof(compat))));
> >> +    return 0;
> >> +}
> >> +
> >> +
> >> +static void pnv_psi_class_init(ObjectClass *klass, void *data)
> >> +{
> >> +    DeviceClass *dc = DEVICE_CLASS(klass);
> >> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> >> +
> >> +    xdc->populate = pnv_psi_populate;
> >> +
> >> +    dc->realize = pnv_psi_realize;
> >> +}
> >> +
> >> +static const TypeInfo pnv_psi_info = {
> >> +    .name          = TYPE_PNV_PSI,
> >> +    .parent        = TYPE_DEVICE,
> > 
> > Since the PSI has an MMIO presence, it probably should be a
> > SysBusDevice, rather than a raw descendent of TYPE_DEVICE.
> 
> Yes indeed. 
> 
> So I will resend the patchset with just the XICS part. I want to 
> take a look at pnv_psi_irq_set() first.
> 
> Thanks,
> 
> C. 
> 
> >> +    .instance_size = sizeof(PnvPsi),
> >> +    .instance_init = pnv_psi_init,
> >> +    .class_init    = pnv_psi_class_init,
> >> +    .interfaces    = (InterfaceInfo[]) {
> >> +        { TYPE_PNV_XSCOM_INTERFACE },
> >> +        { }
> >> +    }
> >> +};
> >> +
> >> +static void pnv_psi_register_types(void)
> >> +{
> >> +    type_register_static(&pnv_psi_info);
> >> +}
> >> +
> >> +type_init(pnv_psi_register_types)
> >> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> >> index f11215ea31f2..f93ec32603b7 100644
> >> --- a/include/hw/ppc/pnv.h
> >> +++ b/include/hw/ppc/pnv.h
> >> @@ -23,6 +23,7 @@
> >>  #include "hw/sysbus.h"
> >>  #include "hw/ppc/pnv_lpc.h"
> >>  #include "hw/ppc/xics.h"
> >> +#include "hw/ppc/pnv_psi.h"
> >>  
> >>  #define TYPE_PNV_CHIP "powernv-chip"
> >>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> >> @@ -58,6 +59,7 @@ typedef struct PnvChip {
> >>      MemoryRegion icp_mmio;
> >>  
> >>      PnvLpcController lpc;
> >> +    PnvPsi       psi;
> >>  } PnvChip;
> >>  
> >>  typedef struct PnvChipClass {
> >> @@ -119,6 +121,8 @@ typedef struct PnvMachineState {
> >>      ICPState     *icps;
> >>      uint32_t     nr_servers;
> >>      QLIST_HEAD(, ICSState) ics;
> >> +
> >> +    uint32_t     cpld_irqstate;
> >>  } PnvMachineState;
> >>  
> >>  #define PNV_FDT_ADDR          0x01000000
> >> @@ -150,4 +154,8 @@ typedef struct PnvMachineState {
> >>  #define PNV_ICP_BASE(chip)   0x0003ffff80000000ull
> >>  #define PNV_ICP_SIZE         0x0000000000100000ull
> >>  
> >> +#define PNV_PSIHB_BAR         0x0003fffe80000000ull
> >> +#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
> >> +
> >> +
> >>  #endif /* _PPC_PNV_H */
> >> diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
> >> new file mode 100644
> >> index 000000000000..ac3c5f8362e3
> >> --- /dev/null
> >> +++ b/include/hw/ppc/pnv_psi.h
> >> @@ -0,0 +1,61 @@
> >> +/*
> >> + * QEMU PowerPC PowerNV PSI controller
> >> + *
> >> + * Copyright (c) 2016, IBM Corporation.
> >> + *
> >> + * This library is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2 of the License, or (at your option) any later version.
> >> + *
> >> + * This library is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> >> + */
> >> +#ifndef _PPC_PNV_PSI_H
> >> +#define _PPC_PNV_PSI_H
> >> +
> >> +#define TYPE_PNV_PSI "pnv-psi"
> >> +#define PNV_PSI(obj) \
> >> +     OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI)
> >> +
> >> +#define PSIHB_XSCOM_MAX         0x20
> >> +
> >> +typedef struct XICSState XICSState;
> >> +
> >> +typedef struct PnvPsi {
> >> +    DeviceState parent;
> >> +
> >> +    MemoryRegion regs_mr;
> >> +
> >> +    /* FSP region not supported */
> >> +    /* MemoryRegion fsp_mr; */
> >> +
> >> +    /* Interrupt generation */
> >> +    ICSState ics;
> >> +
> >> +    /* Registers */
> >> +    uint64_t regs[PSIHB_XSCOM_MAX];
> >> +
> >> +    MemoryRegion xscom_regs;
> >> +} PnvPsi;
> >> +
> >> +typedef enum PnvPsiIrq {
> >> +    PSIHB_IRQ_PSI, /* internal use only */
> >> +    PSIHB_IRQ_FSP, /* internal use only */
> >> +    PSIHB_IRQ_OCC,
> >> +    PSIHB_IRQ_FSI,
> >> +    PSIHB_IRQ_LPC_I2C,
> >> +    PSIHB_IRQ_LOCAL_ERR,
> >> +    PSIHB_IRQ_EXTERNAL,
> >> +} PnvPsiIrq;
> >> +
> >> +#define PSI_NUM_INTERRUPTS 6
> >> +
> >> +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state);
> >> +
> >> +#endif /* _PPC_PNV_PSI_H */
> >> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> >> index 0faa1847bf13..2938abd74955 100644
> >> --- a/include/hw/ppc/pnv_xscom.h
> >> +++ b/include/hw/ppc/pnv_xscom.h
> >> @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
> >>  #define PNV_XSCOM_LPC_BASE        0xb0020
> >>  #define PNV_XSCOM_LPC_SIZE        0x4
> >>  
> >> +#define PNV_XSCOM_PSI_BASE        0x2010900
> >> +#define PNV_XSCOM_PSI_SIZE        0x20
> >> +
> >>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
> >>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
> >>  
> > 
> 

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-17  2:00       ` David Gibson
@ 2017-03-17  8:27         ` Cédric Le Goater
  0 siblings, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-17  8:27 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt

On 03/17/2017 03:00 AM, David Gibson wrote:
> On Thu, Mar 16, 2017 at 02:52:17PM +0100, Cédric Le Goater wrote:
>> On 03/15/2017 07:16 AM, David Gibson wrote:
>>> On Wed, Mar 08, 2017 at 11:52:49AM +0100, Cédric Le Goater wrote:
>>>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>>>
>>>> The PSI (Processor Service Interface) Controller is one of the engines
>>>> of the "Bridge" unit which connects the different interfaces to the
>>>> Power Processor.
>>>>
>>>> This adds just enough of the PSI bridge to handle various on-chip and
>>>> the one external interrupt. The rest of PSI has to do with the link to
>>>> the IBM FSP service processor which we don't plan to emulate (not used
>>>> on OpenPower machines).
>>>>
>>>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>>> [clg: - updated for qemu-2.9
>>>>       - changed the XSCOM interface to fit new model
>>>>       - QOMified the model
>>>>       - reworked set_xive and worked around a skiboot bug
>>>>       - removed the 'psi_mmio_to_xscom' mapping array ]
>>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>>> ---
>>>>  hw/ppc/Makefile.objs       |   2 +-
>>>>  hw/ppc/pnv.c               |  35 ++-
>>>>  hw/ppc/pnv_psi.c           | 583 +++++++++++++++++++++++++++++++++++++++++++++
>>>>  include/hw/ppc/pnv.h       |   8 +
>>>>  include/hw/ppc/pnv_psi.h   |  61 +++++
>>>>  include/hw/ppc/pnv_xscom.h |   3 +
>>>>  6 files changed, 685 insertions(+), 7 deletions(-)
>>>>  create mode 100644 hw/ppc/pnv_psi.c
>>>>  create mode 100644 include/hw/ppc/pnv_psi.h
>>>>
>>>> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
>>>> index 001293423c8d..dc19ee17fa57 100644
>>>> --- a/hw/ppc/Makefile.objs
>>>> +++ b/hw/ppc/Makefile.objs
>>>> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>>>>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>>>>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
>>>>  # IBM PowerNV
>>>> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
>>>> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
>>>>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>>>>  obj-y += spapr_pci_vfio.o
>>>>  endif
>>>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>>>> index 0ae11cc3a2ca..85b00bf235c6 100644
>>>> --- a/hw/ppc/pnv.c
>>>> +++ b/hw/ppc/pnv.c
>>>> @@ -356,15 +356,22 @@ static void ppc_powernv_reset(void)
>>>>   * have a CPLD that will collect the SerIRQ and shoot them as a
>>>>   * single level interrupt to the P8 chip. So let's setup a hook
>>>>   * for doing just that.
>>>> - *
>>>> - * Note: The actual interrupt input isn't emulated yet, this will
>>>> - * come with the PSI bridge model.
>>>>   */
>>>>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>>>>  {
>>>> -    /* We don't yet emulate the PSI bridge which provides the external
>>>> -     * interrupt, so just drop interrupts on the floor
>>>> -     */
>>>> +    PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
>>>> +    uint32_t old_state = pnv->cpld_irqstate;
>>>> +    PnvChip *chip = opaque;
>>>> +
>>>> +    if (level) {
>>>> +        pnv->cpld_irqstate |= 1u << n;
>>>> +    } else {
>>>> +        pnv->cpld_irqstate &= ~(1u << n);
>>>> +    }
>>>> +    if (pnv->cpld_irqstate != old_state) {
>>>> +        pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
>>>> +                        pnv->cpld_irqstate != 0);
>>>> +    }
>>>>  }
>>>>  
>>>>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
>>>> @@ -702,6 +709,11 @@ static void pnv_chip_init(Object *obj)
>>>>  
>>>>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
>>>>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
>>>> +
>>>> +    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
>>>> +    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
>>>> +    object_property_add_const_link(OBJECT(&chip->psi), "xics",
>>>> +                                   OBJECT(qdev_get_machine()), &error_abort);
>>>>  }
>>>>  
>>>>  static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
>>>> @@ -722,6 +734,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>>>      char *typename = pnv_core_typename(pcc->cpu_model);
>>>>      size_t typesize = object_type_get_instance_size(typename);
>>>>      int i, core_hwid;
>>>> +    MachineState *machine = MACHINE(qdev_get_machine());
>>>> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
>>>>  
>>>>      if (!object_class_by_name(typename)) {
>>>>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
>>>> @@ -797,6 +811,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>>>      }
>>>>      g_free(typename);
>>>>  
>>>> +
>>>> +    /* Processor Service Interface (PSI) Host Bridge */
>>>> +    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
>>>> +                             &error_fatal);
>>>> +    pnv_xscom_add_subregion(chip, PNV_XSCOM_PSI_BASE, &chip->psi.xscom_regs);
>>>> +
>>>> +    /* link in the PSI ICS */
>>>> +    QLIST_INSERT_HEAD(&pnv->ics, &chip->psi.ics, list);
>>>> +
>>>>      /* Create LPC controller */
>>>>      object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
>>>>                               &error_fatal);
>>>> diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
>>>> new file mode 100644
>>>> index 000000000000..6ba688aac075
>>>> --- /dev/null
>>>> +++ b/hw/ppc/pnv_psi.c
>>>> @@ -0,0 +1,583 @@
>>>> +/*
>>>> + * QEMU PowerNV PowerPC PSI interface
>>>> + *
>>>> + * Copyright (c) 2016, IBM Corporation
>>>> + *
>>>> + * This library is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2 of the License, or (at your option) any later version.
>>>> + *
>>>> + * This library is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "hw/hw.h"
>>>> +#include "target/ppc/cpu.h"
>>>> +#include "qemu/log.h"
>>>> +#include "qapi/error.h"
>>>> +
>>>> +#include "exec/address-spaces.h"
>>>> +
>>>> +#include "hw/ppc/fdt.h"
>>>> +#include "hw/ppc/pnv.h"
>>>> +#include "hw/ppc/pnv_xscom.h"
>>>> +#include "hw/ppc/pnv_psi.h"
>>>> +
>>>> +#include <libfdt.h>
>>>> +
>>>> +#define PSIHB_XSCOM_FIR_RW      0x00
>>>> +#define PSIHB_XSCOM_FIR_AND     0x01
>>>> +#define PSIHB_XSCOM_FIR_OR      0x02
>>>> +#define PSIHB_XSCOM_FIRMASK_RW  0x03
>>>> +#define PSIHB_XSCOM_FIRMASK_AND 0x04
>>>> +#define PSIHB_XSCOM_FIRMASK_OR  0x05
>>>> +#define PSIHB_XSCOM_FIRACT0     0x06
>>>> +#define PSIHB_XSCOM_FIRACT1     0x07
>>>> +#define PSIHB_XSCOM_BAR         0x0a
>>>> +#define   PSIHB_BAR_EN                  0x0000000000000001ull
>>>> +#define PSIHB_XSCOM_FSPBAR      0x0b
>>>> +#define PSIHB_XSCOM_CR          0x0e
>>>> +#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
>>>> +#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
>>>> +#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
>>>> +#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
>>>> +#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
>>>> +#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
>>>> +#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
>>>> +#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
>>>> +#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
>>>> +#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
>>>> +          /* and more ... */
>>>> +#define PSIHB_XSCOM_SEMR        0x0f
>>>> +#define PSIHB_XSCOM_XIVR_PSI    0x10
>>>> +#define   PSIHB_XIVR_SERVER_SH  40
>>>> +#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
>>>> +#define   PSIHB_XIVR_PRIO_SH    32
>>>> +#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
>>>> +#define   PSIHB_XIVR_SRC_SH             29
>>>> +#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
>>>> +#define   PSIHB_XIVR_PENDING    0x01000000ull
>>>> +#define PSIHB_XSCOM_SCR         0x12
>>>> +#define PSIHB_XSCOM_CCR         0x13
>>>> +#define PSIHB_XSCOM_DMA_UPADD   0x14
>>>> +#define PSIHB_XSCOM_IRQ_STAT    0x15
>>>> +#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
>>>> +#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
>>>> +#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
>>>> +#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
>>>> +#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
>>>> +#define PSIHB_XSCOM_XIVR_OCC    0x16
>>>> +#define PSIHB_XSCOM_XIVR_FSI    0x17
>>>> +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
>>>> +#define PSIHB_XSCOM_XIVR_LOCERR 0x19
>>>> +#define PSIHB_XSCOM_XIVR_EXT    0x1a
>>>> +#define PSIHB_XSCOM_IRSN        0x1b
>>>> +#define   PSIHB_IRSN_COMP_SH            45
>>>> +#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
>>>> +#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
>>>> +#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
>>>> +#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
>>>> +#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
>>>> +#define   PSIHB_IRSN_COMPMASK_SH        13
>>>> +#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
>>>> +
>>>> +/*
>>>> + * These are the values of the registers when accessed through the
>>>> + * MMIO region. The relation is xscom = (mmio + 0x50) >> 3
>>>> + */
>>>> +#define PSIHB_MMIO_BAR          0x00
>>>> +#define PSIHB_MMIO_FSPBAR       0x08
>>>> +#define PSIHB_MMIO_CR           0x20
>>>> +#define PSIHB_MMIO_SEMR         0x28
>>>> +#define PSIHB_MMIO_XIVR_PSI     0x30
>>>> +#define PSIHB_MMIO_SCR          0x40
>>>> +#define PSIHB_MMIO_CCR          0x48
>>>> +#define PSIHB_MMIO_DMA_UPADD    0x50
>>>> +#define PSIHB_MMIO_IRQ_STAT     0x58
>>>> +#define PSIHB_MMIO_XIVR_OCC     0x60
>>>> +#define PSIHB_MMIO_XIVR_FSI     0x68
>>>> +#define PSIHB_MMIO_XIVR_LPCI2C  0x70
>>>> +#define PSIHB_MMIO_XIVR_LOCERR  0x78
>>>> +#define PSIHB_MMIO_XIVR_EXT     0x80
>>>> +#define PSIHB_MMIO_IRSN         0x88
>>>> +#define PSIHB_MMIO_MAX          0x100
>>>> +
>>>> +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
>>>> +{
>>>> +    MemoryRegion *sysmem = get_system_memory();
>>>> +    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
>>>> +
>>>> +    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
>>>> +
>>>> +    /* Update MR, always remove it first */
>>>> +    if (old & PSIHB_BAR_EN) {
>>>> +        memory_region_del_subregion(sysmem, &psi->regs_mr);
>>>> +    }
>>>> +    /* Then add it back if needed */
>>>> +    if (bar & PSIHB_BAR_EN) {
>>>> +        uint64_t addr = bar & 0x0003fffffff00000;
>>>> +        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void pnv_psi_update_fsp_mr(PnvPsi *psi)
>>>> +{
>>>> +    /* XXX Update FSP MR if/when we support FSP BAR */
>>>> +}
>>>> +
>>>> +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr)
>>>> +{
>>>> +    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
>>>> +
>>>> +    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
>>>> +
>>>> +    /* Check some bit changes */
>>>> +    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
>>>> +        pnv_psi_update_fsp_mr(psi);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val)
>>>> +{
>>>> +    uint32_t offset;
>>>> +    ICSState *ics = &psi->ics;
>>>> +
>>>> +    /* In this model we ignore the up/down enable bits for now
>>>> +     * as SW doesn't use them (other than setting them at boot).
>>>> +     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
>>>> +     * it and finally we ignore reset (XXX fix that ?)
>>>> +     */
>>>> +    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
>>>> +                                         PSIHB_IRSN_IRQ_MUX |
>>>> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
>>>> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
>>>> +                                         PSIHB_IRSN_DOWNSTREAM_EN);
>>>> +
>>>> +    /* We ignore the compare mask as well, our ICS emulation is too
>>>> +     * simplistic to make any use if it, and we extract the offset
>>>> +     * from the compare value
>>>> +     */
>>>> +    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
>>>> +    ics->offset = offset;
>>>> +}
>>>> +
>>>> +static bool pnv_psi_irq_bits(PnvPsi *psi, PnvPsiIrq irq,
>>>> +                             uint32_t *out_xivr_reg,
>>>> +                             uint32_t *out_stat_reg,
>>>> +                             uint64_t *out_stat_bit)
>>>
>>> Your PnvPsiIrq values are arbitrary and contiguous AFACT.  Why not
>>> just have a lookup table for this info, instead of a giant switch
>>> statement?
>>
>> Well, I agree but at the same time, we are not gaining much in terms
>> of lines by using an array,
> 
> Hmm.. seems like an ~ x5 line reduction to me...
> 
> And, IMO, easier to read.

OK. Will do. Hope you like it :)

>> and we have to check for boundaries which
>> the switch provide. The question would be different if we had more 
>> parameters. So let's keep it that way. 
>>  
>>>> +{
>>>> +    switch (irq) {
>>>> +    case PSIHB_IRQ_PSI:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
>>>> +        *out_stat_reg = PSIHB_XSCOM_CR;
>>>> +        *out_stat_bit = PSIHB_CR_PSI_IRQ;
>>>> +        break;
>>>> +    case PSIHB_IRQ_FSP:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
>>>> +        *out_stat_reg = PSIHB_XSCOM_CR;
>>>> +        *out_stat_bit = PSIHB_CR_FSP_IRQ;
>>>> +        break;
>>>> +    case PSIHB_IRQ_OCC:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
>>>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>>>> +        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
>>>> +        break;
>>>> +    case PSIHB_IRQ_FSI:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
>>>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>>>> +        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
>>>> +        break;
>>>> +    case PSIHB_IRQ_LPC_I2C:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
>>>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>>>> +        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
>>>> +        break;
>>>> +    case PSIHB_IRQ_LOCAL_ERR:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
>>>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>>>> +        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
>>>> +        break;
>>>> +    case PSIHB_IRQ_EXTERNAL:
>>>> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
>>>> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
>>>> +        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
>>>> +        break;
>>>> +    default:
>>>> +        return false;
>>>> +    }
>>>> +    return true;
>>>> +}
>>>> +
>>>> +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
>>>> +{
>>>> +    ICSState *ics = &psi->ics;
>>>> +    uint32_t xivr_reg;
>>>> +    uint32_t stat_reg;
>>>> +    uint64_t stat_bit;
>>>> +    uint32_t src;
>>>> +    bool masked;
>>>> +
>>>> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
>>>> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
>>>> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
>>>> +    if (state) {
>>>> +        psi->regs[stat_reg] |= stat_bit;
>>>> +        /* XXX optimization: check mask here. That means re-evaluating
>>>> +         * when unmasking, thus TODO
>>>> +         */
>>>> +        qemu_irq_raise(ics->qirqs[src]);
>>>> +    } else {
>>>> +        psi->regs[stat_reg] &= ~stat_bit;
>>>> +
>>>> +        /* FSP and PSI are muxed so don't lower if either still set */
>>>> +        if (stat_reg != PSIHB_XSCOM_CR ||
>>>> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
>>>> +            qemu_irq_lower(ics->qirqs[src]);
>>>> +        } else {
>>>> +            state = true;
>>>> +        }
>>>> +    }
>>>
>>> It might be cleaner to just revaluate the irq level from scratch here,
>>> and set the level, rather than doing this complicated dance to work
>>> out if it has changed.
>>
>> OK. I need to take a closer look at that.
>>
>>>> +
>>>> +    /* XXX Note about the emulation of the pending bit: This isn't
>>>> +     * entirely correct. The pending bit should be cleared when the
>>>> +     * EOI has been received. However, we don't have callbacks on
>>>> +     * EOI (especially not under KVM) so no way to emulate that
>>>> +     * properly, so instead we just set that bit as the logical
>>>> +     * "output" of the XIVR (ie pending & !masked)
>>>> +     * XXX TODO: Also update it on set_xivr
>>>> +     */
>>>> +    if (state && !masked) {
>>>> +        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
>>>> +    } else {
>>>> +        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val)
>>>> +{
>>>> +    ICSState *ics = &psi->ics;
>>>> +    uint16_t server;
>>>> +    uint8_t prio;
>>>> +    uint8_t src;
>>>> +    int icp_index;
>>>> +
>>>> +    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
>>>> +            (val & (PSIHB_XIVR_SERVER_MSK |
>>>> +                    PSIHB_XIVR_PRIO_MSK |
>>>> +                    PSIHB_XIVR_SRC_MSK));
>>>> +    val = psi->regs[reg];
>>>> +    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
>>>> +    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
>>>> +    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
>>>> +    if (src > PSIHB_IRQ_EXTERNAL) {
>>>> +        /* XXX Generate error ? */
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * Linux fills the irq xivr with the hw processor id plus the
>>>> +     * link bits. shift back to get something valid.
>>>> +     */
>>>> +    server >>= 2;
>>>> +
>>>> +    /*
>>>> +     * When skiboot initializes PSIHB, it fills the xives with
>>>> +     * server=0, prio=0xff, but we don't have a CPU with a pir=0. So
>>>> +     * skip that case.
>>>> +     */
>>>> +    if (prio != 0xff) {
>>>> +        icp_index = xics_get_cpu_index_by_pir(server);
>>>> +        assert(icp_index != -1);
>>>> +    } else {
>>>> +        if (server) {
>>>> +            qemu_log_mask(LOG_GUEST_ERROR, "PSI: bogus server %d for IRQ %d\n",
>>>> +                          server, src);
>>>> +        }
>>>> +        icp_index = server;
>>>> +    }
>>>
>>> This logic doesn't seem like it belongs here.  You've received a
>>> server number, seems like you should just pass it on to the xics code,
>>> and if there can be an error, have that detect it.
>>
>> I have removed all of that in a private patch. This is tracking 
>> an issue in skiboot which did not clear correctly the PSI xive. 
>> It is partially resolved in recent version but I still see some 
>> warnings whe the guest reboots.
> 
> Ok.  Workarounds for firmware bugs are fine, but they need a detailed
> comment so other people have a hope of understanding why they're
> there.  Including stating outright that it is a bug workaround, rather
> than expected firmware behaviour.

Yes. I will included a detailed comment in an extra patch if this is 
still needed. 

Thanks,

C. 


>>
>>>> +
>>>> +    /* Now because of source remapping, weird things can happen
>>>> +     * if you change the source number dynamically, our simple ICS
>>>> +     * doesn't deal with remapping. So we just poke a different
>>>> +     * ICS entry based on what source number was written. This will
>>>> +     * do for now but a more accurate implementation would instead
>>>> +     * use a fixed server/prio and a remapper of the generated irq.
>>>> +     */
>>>> +    ics_simple_write_xive(ics, src, icp_index, prio, prio);
>>>> +}
>>>> +
>>>> +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio)
>>>> +{
>>>> +    uint64_t val = 0xffffffffffffffffull;
>>>> +
>>>> +    switch (offset) {
>>>> +    case PSIHB_XSCOM_FIR_RW:
>>>> +    case PSIHB_XSCOM_FIRACT0:
>>>> +    case PSIHB_XSCOM_FIRACT1:
>>>> +    case PSIHB_XSCOM_BAR:
>>>> +    case PSIHB_XSCOM_FSPBAR:
>>>> +    case PSIHB_XSCOM_CR:
>>>> +    case PSIHB_XSCOM_XIVR_PSI:
>>>> +    case PSIHB_XSCOM_XIVR_OCC:
>>>> +    case PSIHB_XSCOM_XIVR_FSI:
>>>> +    case PSIHB_XSCOM_XIVR_LPCI2C:
>>>> +    case PSIHB_XSCOM_XIVR_LOCERR:
>>>> +    case PSIHB_XSCOM_XIVR_EXT:
>>>> +    case PSIHB_XSCOM_IRQ_STAT:
>>>> +    case PSIHB_XSCOM_SEMR:
>>>> +    case PSIHB_XSCOM_DMA_UPADD:
>>>> +    case PSIHB_XSCOM_IRSN:
>>>> +        val = psi->regs[offset];
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
>>>> +                      offset);
>>>
>>> As noted elsewhere, tracepoints are more usual than qemu_log() these
>>> days.  But either way, this really should have a distinguishable
>>> message from the one in the write path.
>>
>> OK. Will add a read/write statement. 
>>
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val,
>>>> +                              bool mmio)
>>>> +{
>>>> +    switch (offset) {
>>>> +    case PSIHB_XSCOM_FIR_RW:
>>>> +    case PSIHB_XSCOM_FIRACT0:
>>>> +    case PSIHB_XSCOM_FIRACT1:
>>>> +    case PSIHB_XSCOM_SEMR:
>>>> +    case PSIHB_XSCOM_DMA_UPADD:
>>>> +        psi->regs[offset] = val;
>>>> +        break;
>>>> +    case PSIHB_XSCOM_FIR_OR:
>>>> +        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
>>>> +        break;
>>>> +    case PSIHB_XSCOM_FIR_AND:
>>>> +        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
>>>> +        break;
>>>> +    case PSIHB_XSCOM_BAR:
>>>> +        /* Only XSCOM can write this one */
>>>> +        if (!mmio) {
>>>> +            pnv_psi_set_bar(psi, val);
>>>> +        }
>>>> +        break;
>>>> +    case PSIHB_XSCOM_FSPBAR:
>>>> +        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
>>>
>>> Should that be PSIHB_XSCOM_FSPBAR?
>>
>> yes ...
>>
>>>> +        pnv_psi_update_fsp_mr(psi);
>>>> +        break;
>>>> +    case PSIHB_XSCOM_CR:
>>>> +        pnv_psi_set_cr(psi, val);
>>>> +        break;
>>>> +    case PSIHB_XSCOM_SCR:
>>>> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
>>>> +        break;
>>>> +    case PSIHB_XSCOM_CCR:
>>>> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
>>>> +        break;
>>>> +    case PSIHB_XSCOM_XIVR_PSI:
>>>> +    case PSIHB_XSCOM_XIVR_OCC:
>>>> +    case PSIHB_XSCOM_XIVR_FSI:
>>>> +    case PSIHB_XSCOM_XIVR_LPCI2C:
>>>> +    case PSIHB_XSCOM_XIVR_LOCERR:
>>>> +    case PSIHB_XSCOM_XIVR_EXT:
>>>> +        pnv_psi_set_xivr(psi, offset, val);
>>>> +        break;
>>>> +    case PSIHB_XSCOM_IRQ_STAT:
>>>> +        /* Read only, should we generate an error ? */
>>>> +        break;
>>>> +    case PSIHB_XSCOM_IRSN:
>>>> +        pnv_psi_set_irsn(psi, val);
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_UNIMP, "PSI Unimplemented register: Ox%" PRIx32 "\n",
>>>> +                      offset);
>>>> +    }
>>>> +}
>>>> +
>>>> +static inline uint32_t psi_mmio_to_xscom(hwaddr addr)
>>>> +{
>>>> +    return (addr >> 3) + PSIHB_XSCOM_BAR;
>>>> +}
>>>> +
>>>> +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
>>>> +{
>>>> +    return pnv_psi_reg_read(opaque, psi_mmio_to_xscom(addr), true);
>>>> +}
>>>> +
>>>> +static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
>>>> +                              uint64_t val, unsigned size)
>>>> +{
>>>> +    pnv_psi_reg_write(opaque, psi_mmio_to_xscom(addr), val, true);
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps psi_mmio_ops = {
>>>> +    .read = pnv_psi_mmio_read,
>>>> +    .write = pnv_psi_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_xscom_read(void *opaque, hwaddr addr, unsigned size)
>>>> +{
>>>> +    PnvPsi *psi = PNV_PSI(opaque);
>>>> +    uint32_t offset = addr >> 3;
>>>> +
>>>> +    return pnv_psi_reg_read(psi, offset, false);
>>>> +}
>>>> +
>>>> +static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
>>>> +                                uint64_t val, unsigned size)
>>>> +{
>>>> +    PnvPsi *psi = PNV_PSI(opaque);
>>>> +    uint32_t offset = addr >> 3;
>>>> +
>>>> +    pnv_psi_reg_write(psi, offset, val, false);
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps pnv_psi_xscom_ops = {
>>>> +    .read = pnv_psi_xscom_read,
>>>> +    .write = pnv_psi_xscom_write,
>>>> +    .valid.min_access_size = 8,
>>>> +    .valid.max_access_size = 8,
>>>> +    .impl.min_access_size = 8,
>>>> +    .impl.max_access_size = 8,
>>>
>>> Consistent nesting format of the two MemoryRegionOps would be good.
>>
>> OK.
>>  
>>>> +    .endianness = DEVICE_BIG_ENDIAN,
>>>> +};
>>>> +
>>>> +static void pnv_psi_init(Object *obj)
>>>> +{
>>>> +    PnvPsi *psi = PNV_PSI(obj);
>>>> +
>>>> +    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
>>>> +    qdev_set_parent_bus(DEVICE(&psi->ics), sysbus_get_default());
>>>> +    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
>>>> +}
>>>> +
>>>> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
>>>> +{
>>>> +    PnvPsi *psi = PNV_PSI(dev);
>>>> +    ICSState *ics = &psi->ics;
>>>> +    Object *obj;
>>>> +    Error *err = NULL, *local_err = NULL;
>>>> +    unsigned int i;
>>>> +
>>>> +    /* Initialize MMIO region */
>>>> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
>>>> +                          "psihb", PNV_PSIHB_BAR_SIZE);
>>>> +
>>>> +    /* Default BAR. Use object properties ? */
>>>> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
>>>> +
>>>> +    /* Default sources in XIVR */
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (0ull << PSIHB_XIVR_SRC_SH);
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (1ull << PSIHB_XIVR_SRC_SH);
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (2ull << PSIHB_XIVR_SRC_SH);
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (3ull << PSIHB_XIVR_SRC_SH);
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (4ull << PSIHB_XIVR_SRC_SH);
>>>> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
>>>> +            (5ull << PSIHB_XIVR_SRC_SH);
>>>> +
>>>> +    /* get XICSFabric from chip */
>>>> +    obj = object_property_get_link(OBJECT(dev), "xics", &err);
>>>> +    if (!obj) {
>>>> +        error_setg(errp, "%s: required link 'xics' not found: %s",
>>>> +                   __func__, error_get_pretty(err));
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * PSI interrupt control source
>>>> +     */
>>>> +    object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
>>>> +    object_property_add_const_link(OBJECT(ics), "xics", obj, &err);
>>>> +    object_property_set_bool(OBJECT(ics), true, "realized",  &local_err);
>>>> +    error_propagate(&err, local_err);
>>>> +    if (err) {
>>>> +        error_propagate(errp, err);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    for (i = 0; i < ics->nr_irqs; i++) {
>>>> +        ics_set_irq_type(ics, i, true);
>>>> +    }
>>>> +
>>>> +    /* XScom region for PSI registers */
>>>> +    pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
>>>> +                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE);
>>>> +}
>>>> +
>>>> +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
>>>> +{
>>>> +    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
>>>> +    char *name;
>>>> +    int offset;
>>>> +    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
>>>> +    uint32_t reg[] = {
>>>> +        cpu_to_be32(lpc_pcba),
>>>> +        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
>>>> +    };
>>>> +
>>>> +    name = g_strdup_printf("psihb@%x", lpc_pcba);
>>>> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
>>>> +    _FDT(offset);
>>>> +    g_free(name);
>>>> +
>>>> +    _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,
>>>> +                      sizeof(compat))));
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +
>>>> +static void pnv_psi_class_init(ObjectClass *klass, void *data)
>>>> +{
>>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>>> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
>>>> +
>>>> +    xdc->populate = pnv_psi_populate;
>>>> +
>>>> +    dc->realize = pnv_psi_realize;
>>>> +}
>>>> +
>>>> +static const TypeInfo pnv_psi_info = {
>>>> +    .name          = TYPE_PNV_PSI,
>>>> +    .parent        = TYPE_DEVICE,
>>>
>>> Since the PSI has an MMIO presence, it probably should be a
>>> SysBusDevice, rather than a raw descendent of TYPE_DEVICE.
>>
>> Yes indeed. 
>>
>> So I will resend the patchset with just the XICS part. I want to 
>> take a look at pnv_psi_irq_set() first.
>>
>> Thanks,
>>
>> C. 
>>
>>>> +    .instance_size = sizeof(PnvPsi),
>>>> +    .instance_init = pnv_psi_init,
>>>> +    .class_init    = pnv_psi_class_init,
>>>> +    .interfaces    = (InterfaceInfo[]) {
>>>> +        { TYPE_PNV_XSCOM_INTERFACE },
>>>> +        { }
>>>> +    }
>>>> +};
>>>> +
>>>> +static void pnv_psi_register_types(void)
>>>> +{
>>>> +    type_register_static(&pnv_psi_info);
>>>> +}
>>>> +
>>>> +type_init(pnv_psi_register_types)
>>>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>>>> index f11215ea31f2..f93ec32603b7 100644
>>>> --- a/include/hw/ppc/pnv.h
>>>> +++ b/include/hw/ppc/pnv.h
>>>> @@ -23,6 +23,7 @@
>>>>  #include "hw/sysbus.h"
>>>>  #include "hw/ppc/pnv_lpc.h"
>>>>  #include "hw/ppc/xics.h"
>>>> +#include "hw/ppc/pnv_psi.h"
>>>>  
>>>>  #define TYPE_PNV_CHIP "powernv-chip"
>>>>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>>>> @@ -58,6 +59,7 @@ typedef struct PnvChip {
>>>>      MemoryRegion icp_mmio;
>>>>  
>>>>      PnvLpcController lpc;
>>>> +    PnvPsi       psi;
>>>>  } PnvChip;
>>>>  
>>>>  typedef struct PnvChipClass {
>>>> @@ -119,6 +121,8 @@ typedef struct PnvMachineState {
>>>>      ICPState     *icps;
>>>>      uint32_t     nr_servers;
>>>>      QLIST_HEAD(, ICSState) ics;
>>>> +
>>>> +    uint32_t     cpld_irqstate;
>>>>  } PnvMachineState;
>>>>  
>>>>  #define PNV_FDT_ADDR          0x01000000
>>>> @@ -150,4 +154,8 @@ typedef struct PnvMachineState {
>>>>  #define PNV_ICP_BASE(chip)   0x0003ffff80000000ull
>>>>  #define PNV_ICP_SIZE         0x0000000000100000ull
>>>>  
>>>> +#define PNV_PSIHB_BAR         0x0003fffe80000000ull
>>>> +#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
>>>> +
>>>> +
>>>>  #endif /* _PPC_PNV_H */
>>>> diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
>>>> new file mode 100644
>>>> index 000000000000..ac3c5f8362e3
>>>> --- /dev/null
>>>> +++ b/include/hw/ppc/pnv_psi.h
>>>> @@ -0,0 +1,61 @@
>>>> +/*
>>>> + * QEMU PowerPC PowerNV PSI controller
>>>> + *
>>>> + * Copyright (c) 2016, IBM Corporation.
>>>> + *
>>>> + * This library is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2 of the License, or (at your option) any later version.
>>>> + *
>>>> + * This library is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +#ifndef _PPC_PNV_PSI_H
>>>> +#define _PPC_PNV_PSI_H
>>>> +
>>>> +#define TYPE_PNV_PSI "pnv-psi"
>>>> +#define PNV_PSI(obj) \
>>>> +     OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI)
>>>> +
>>>> +#define PSIHB_XSCOM_MAX         0x20
>>>> +
>>>> +typedef struct XICSState XICSState;
>>>> +
>>>> +typedef struct PnvPsi {
>>>> +    DeviceState parent;
>>>> +
>>>> +    MemoryRegion regs_mr;
>>>> +
>>>> +    /* FSP region not supported */
>>>> +    /* MemoryRegion fsp_mr; */
>>>> +
>>>> +    /* Interrupt generation */
>>>> +    ICSState ics;
>>>> +
>>>> +    /* Registers */
>>>> +    uint64_t regs[PSIHB_XSCOM_MAX];
>>>> +
>>>> +    MemoryRegion xscom_regs;
>>>> +} PnvPsi;
>>>> +
>>>> +typedef enum PnvPsiIrq {
>>>> +    PSIHB_IRQ_PSI, /* internal use only */
>>>> +    PSIHB_IRQ_FSP, /* internal use only */
>>>> +    PSIHB_IRQ_OCC,
>>>> +    PSIHB_IRQ_FSI,
>>>> +    PSIHB_IRQ_LPC_I2C,
>>>> +    PSIHB_IRQ_LOCAL_ERR,
>>>> +    PSIHB_IRQ_EXTERNAL,
>>>> +} PnvPsiIrq;
>>>> +
>>>> +#define PSI_NUM_INTERRUPTS 6
>>>> +
>>>> +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state);
>>>> +
>>>> +#endif /* _PPC_PNV_PSI_H */
>>>> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
>>>> index 0faa1847bf13..2938abd74955 100644
>>>> --- a/include/hw/ppc/pnv_xscom.h
>>>> +++ b/include/hw/ppc/pnv_xscom.h
>>>> @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
>>>>  #define PNV_XSCOM_LPC_BASE        0xb0020
>>>>  #define PNV_XSCOM_LPC_SIZE        0x4
>>>>  
>>>> +#define PNV_XSCOM_PSI_BASE        0x2010900
>>>> +#define PNV_XSCOM_PSI_SIZE        0x20
>>>> +
>>>>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>>>>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>>>>  
>>>
>>
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2017-03-16 13:52     ` Cédric Le Goater
  2017-03-17  2:00       ` David Gibson
@ 2017-03-21 13:36       ` Cédric Le Goater
  1 sibling, 0 replies; 29+ messages in thread
From: Cédric Le Goater @ 2017-03-21 13:36 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, qemu-devel, Benjamin Herrenschmidt

[ ... ]

>>> +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state)
>>> +{
>>> +    ICSState *ics = &psi->ics;
>>> +    uint32_t xivr_reg;
>>> +    uint32_t stat_reg;
>>> +    uint64_t stat_bit;
>>> +    uint32_t src;
>>> +    bool masked;
>>> +
>>> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
>>> +        return;
>>> +    }
>>> +
>>> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
>>> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
>>> +    if (state) {
>>> +        psi->regs[stat_reg] |= stat_bit;
>>> +        /* XXX optimization: check mask here. That means re-evaluating
>>> +         * when unmasking, thus TODO
>>> +         */
>>> +        qemu_irq_raise(ics->qirqs[src]);
>>> +    } else {
>>> +        psi->regs[stat_reg] &= ~stat_bit;
>>> +
>>> +        /* FSP and PSI are muxed so don't lower if either still set */
>>> +        if (stat_reg != PSIHB_XSCOM_CR ||
>>> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
>>> +            qemu_irq_lower(ics->qirqs[src]);
>>> +        } else {
>>> +            state = true;

ugly.

>>> +        }
>>> +    }
>>
>> It might be cleaner to just revaluate the irq level from scratch here,
>> and set the level, rather than doing this complicated dance to work
>> out if it has changed.
> 
> OK. I need to take a closer look at that.

So I took a closer a look and some parts are not clear, even if 
correct. The FSP and PSI interrupts are muxed and the above code 
tries to re-conciliate the IRQ triggering in a single routine. 
But we ended up (I think) using some hacks. See below PnvPsiIrq.

[ ... ]

>>> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    PnvPsi *psi = PNV_PSI(dev);
>>> +    ICSState *ics = &psi->ics;
>>> +    Object *obj;
>>> +    Error *err = NULL, *local_err = NULL;
>>> +    unsigned int i;
>>> +
>>> +    /* Initialize MMIO region */
>>> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
>>> +                          "psihb", PNV_PSIHB_BAR_SIZE);
>>> +
>>> +    /* Default BAR. Use object properties ? */
>>> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
>>> +
>>> +    /* Default sources in XIVR */
>>> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
>>> +            (0ull << PSIHB_XIVR_SRC_SH);
>>> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
>>> +            (1ull << PSIHB_XIVR_SRC_SH);
>>> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
>>> +            (2ull << PSIHB_XIVR_SRC_SH);
>>> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
>>> +            (3ull << PSIHB_XIVR_SRC_SH);
>>> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
>>> +            (4ull << PSIHB_XIVR_SRC_SH);
>>> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
>>> +            (5ull << PSIHB_XIVR_SRC_SH);
>>> +

The above is not using a loop on PnvPsiIrq because the numbers
do not match the PSI IRQ definitions.

[ ... ]

>>> +
>>> +typedef enum PnvPsiIrq {
>>> +    PSIHB_IRQ_PSI, /* internal use only */
>>> +    PSIHB_IRQ_FSP, /* internal use only */
>>> +    PSIHB_IRQ_OCC,
>>> +    PSIHB_IRQ_FSI,
>>> +    PSIHB_IRQ_LPC_I2C,
>>> +    PSIHB_IRQ_LOCAL_ERR,
>>> +    PSIHB_IRQ_EXTERNAL,
>>> +} PnvPsiIrq;
>>> +
>>> +#define PSI_NUM_INTERRUPTS 6

a maximum of 6 interrupts for an enum containing 7 entries. It is a
little ugly.

I am going to rewrite this part in a more straight forward way.

C.


 

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2017-03-21 13:36 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-08 10:52 [Qemu-devel] [PATCH for-2.10 0/8] ppc/pnv: interrupt controller (POWER8) Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 1/8] ppc/xics: add a xics_get_cpu_index_by_pir() helper Cédric Le Goater
2017-03-14  5:38   ` David Gibson
2017-03-14  8:11     ` Cédric Le Goater
2017-03-14 17:00     ` Cédric Le Goater
2017-03-15  4:53       ` David Gibson
2017-03-15 10:04         ` Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 2/8] ppc/xics: add an ics_eoi() handler to XICSFabric Cédric Le Goater
2017-03-14  5:40   ` David Gibson
2017-03-14  8:12     ` Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 3/8] ppc/pnv: create the ICP and ICS objects under the machine Cédric Le Goater
2017-03-14  5:45   ` David Gibson
2017-03-14  9:47     ` Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 4/8] ppc/pnv: add memory regions for the ICP registers Cédric Le Goater
2017-03-08 11:24   ` Philippe Mathieu-Daudé
2017-03-08 13:33     ` Cédric Le Goater
2017-03-14  5:49   ` David Gibson
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 5/8] ppc/pnv: map the ICP memory regions Cédric Le Goater
2017-03-14  5:52   ` David Gibson
2017-03-14 10:02     ` Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 6/8] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
2017-03-15  6:16   ` David Gibson
2017-03-15  9:38     ` Benjamin Herrenschmidt
2017-03-16 13:52     ` Cédric Le Goater
2017-03-17  2:00       ` David Gibson
2017-03-17  8:27         ` Cédric Le Goater
2017-03-21 13:36       ` Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 7/8] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
2017-03-08 10:52 ` [Qemu-devel] [PATCH for-2.10 8/8] ppc/pnv: Add support for POWER8+ LPC Controller Cédric Le Goater

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.