qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] ppc/pnv: LPC interrupt fixes
@ 2024-05-28  6:20 Nicholas Piggin
  2024-05-28  6:20 ` [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts Nicholas Piggin
  2024-05-28  6:20 ` [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function Nicholas Piggin
  0 siblings, 2 replies; 5+ messages in thread
From: Nicholas Piggin @ 2024-05-28  6:20 UTC (permalink / raw)
  To: qemu-ppc
  Cc: Nicholas Piggin, Glenn Miles, Cédric Le Goater,
	Frédéric Barrat, qemu-devel

Here is v2 of the POWER9 PSI serirq patch with changes suggested by
Cedric and some other things. But also in front of that we have a fix
from Glenn for a lost interrupt problem.

I rebased Glenn's patch and also changed some comments and changelog
a bit so any bugs or silly comments are probably my fault, but debug
and fix is credit to him.

Thanks,
Nick

Glenn Miles (1):
  ppc/pnv: Fix loss of LPC SERIRQ interrupts

Nicholas Piggin (1):
  ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear
    function

 include/hw/ppc/pnv_lpc.h |  17 ++++-
 hw/ppc/pnv.c             |  36 ++++++++--
 hw/ppc/pnv_lpc.c         | 150 ++++++++++++++++++++++++++++++++-------
 3 files changed, 170 insertions(+), 33 deletions(-)

-- 
2.43.0



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

* [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts
  2024-05-28  6:20 [PATCH 0/2] ppc/pnv: LPC interrupt fixes Nicholas Piggin
@ 2024-05-28  6:20 ` Nicholas Piggin
  2024-05-28 14:23   ` Miles Glenn
  2024-05-28  6:20 ` [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function Nicholas Piggin
  1 sibling, 1 reply; 5+ messages in thread
From: Nicholas Piggin @ 2024-05-28  6:20 UTC (permalink / raw)
  To: qemu-ppc
  Cc: Nicholas Piggin, Glenn Miles, Cédric Le Goater,
	Frédéric Barrat, qemu-devel, Glenn Miles

From: Glenn Miles <milesg@linux.vnet.ibm.com>

The LPC HC irq status register bits are set when an LPC IRQSER input is
asserted. These irq status bits drive the PSI irq to the CPU interrupt
controller. The LPC HC irq status bits are cleared by software writing
to the register with 1's for the bits to clear.

Existing register write was clearing the irq status bits even when the
input was asserted, this results in interrupts being lost.

This fix changes the behavior to keep track of the device IRQ status
in internal state that is separate from the irq status register, and
only allowing the irq status bits to be cleared if the associated
input is not asserted.

[np: rebased before P9 PSI SERIRQ patch, adjust changelog/comments]
Signed-off-by: Glenn Miles <milesg@linux.ibm.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 include/hw/ppc/pnv_lpc.h |  3 +++
 hw/ppc/pnv_lpc.c         | 22 +++++++++++++++++++---
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index 5d22c45570..97c6872c3f 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -73,6 +73,9 @@ struct PnvLpcController {
     uint32_t opb_irq_pol;
     uint32_t opb_irq_input;
 
+    /* LPC device IRQ state */
+    uint32_t lpc_hc_irq_inputs;
+
     /* LPC HC registers */
     uint32_t lpc_hc_fw_seg_idsel;
     uint32_t lpc_hc_fw_rd_acc_size;
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index d692858bee..252690dcaa 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -505,7 +505,14 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
         pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_IRQSTAT:
-        lpc->lpc_hc_irqstat &= ~val;
+        /*
+         * This register is write-to-clear for the IRQSER (LPC device IRQ)
+         * status. However if the device has not de-asserted its interrupt
+         * that will just raise this IRQ status bit again. Model this by
+         * keeping track of the inputs and only clearing if the inputs are
+         * deasserted.
+         */
+        lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs);
         pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_ERROR_ADDRESS:
@@ -803,11 +810,20 @@ 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)
 {
     PnvLpcController *lpc = PNV_LPC(opaque);
+    uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n;
 
-    /* The Naples HW latches the 1 levels, clearing is done by SW */
     if (level) {
-        lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
+        lpc->lpc_hc_irq_inputs |= irq_bit;
+
+        /*
+	 * The LPC HC in Naples and later latches LPC IRQ into a bit field in
+	 * the IRQSTAT register, and that drives the PSI IRQ to the IC.
+	 * Software clears this bit manually (see LPC_HC_IRQSTAT handler).
+         */
+        lpc->lpc_hc_irqstat |= irq_bit;
         pnv_lpc_eval_irqs(lpc);
+    } else {
+        lpc->lpc_hc_irq_inputs &= ~irq_bit;
     }
 }
 
-- 
2.43.0



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

* [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function
  2024-05-28  6:20 [PATCH 0/2] ppc/pnv: LPC interrupt fixes Nicholas Piggin
  2024-05-28  6:20 ` [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts Nicholas Piggin
@ 2024-05-28  6:20 ` Nicholas Piggin
  2024-05-29 14:31   ` Miles Glenn
  1 sibling, 1 reply; 5+ messages in thread
From: Nicholas Piggin @ 2024-05-28  6:20 UTC (permalink / raw)
  To: qemu-ppc
  Cc: Nicholas Piggin, Glenn Miles, Cédric Le Goater,
	Frédéric Barrat, qemu-devel

The POWER8 LPC ISA device irqs all get combined and reported to the line
connected the PSI LPCHC irq. POWER9 changed this so only internal LPC
host controller irqs use that line, and the device irqs get routed to
4 new lines connected to PSI SERIRQ0-3.

POWER9 also introduced a new feature that automatically clears the irq
status in the LPC host controller when EOI'ed, so software does not have
to.

The powernv OPAL (skiboot) firmware managed to work because the LPCHC
irq handler scanned all LPC irqs and handled those including clearing
status even on POWER9 systems. So LPC irqs worked despite OPAL thinking
it was running in POWER9 mode. After this change, UART interrupts show
up on serirq1 which is where OPAL routes them to:

 cat /proc/interrupts
 ...
 20:          0  XIVE-IRQ 1048563 Level     opal-psi#0:lpchc
 ...
 25:         34  XIVE-IRQ 1048568 Level     opal-psi#0:lpc_serirq_mux1

Whereas they previously turn up on lpchc.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
Since v1:
- Fix and test power8
- Rebase onto Glenn's fix
- Move irq_to_serirq_route from global into PnvLpcController
- Don't have SERIRQ irqs latch the OPB irq status register, docs don't
  suggest they do and skiboot does not clear that bit for SERIRQ path.
- Have the SERIRQ path use the LPCHC IRQ mask (missed in previous
  patch).

 include/hw/ppc/pnv_lpc.h |  14 ++++-
 hw/ppc/pnv.c             |  36 +++++++++--
 hw/ppc/pnv_lpc.c         | 128 ++++++++++++++++++++++++++++++++-------
 3 files changed, 148 insertions(+), 30 deletions(-)

diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index 97c6872c3f..e0fd5e4130 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -23,6 +23,7 @@
 #include "exec/memory.h"
 #include "hw/ppc/pnv.h"
 #include "hw/qdev-core.h"
+#include "hw/isa/isa.h" /* For ISA_NUM_IRQS */
 
 #define TYPE_PNV_LPC "pnv-lpc"
 typedef struct PnvLpcClass PnvLpcClass;
@@ -87,8 +88,19 @@ struct PnvLpcController {
     /* XSCOM registers */
     MemoryRegion xscom_regs;
 
+    /*
+     * In P8, ISA irqs are combined with internal sources to drive the
+     * LPCHC interrupt output. P9 ISA irqs raise one of 4 lines that
+     * drive PSI SERIRQ irqs, routing according to OPB routing registers.
+     */
+    bool psi_has_serirq;
+
     /* PSI to generate interrupts */
-    qemu_irq psi_irq;
+    qemu_irq psi_irq_lpchc;
+
+    /* P9 serirq lines and irq routing table */
+    qemu_irq psi_irq_serirq[4];
+    int irq_to_serirq_route[ISA_NUM_IRQS];
 };
 
 struct PnvLpcClass {
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 6e3a5ccdec..f6c3e91b3a 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -728,7 +728,8 @@ static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp)
     Pnv8Chip *chip8 = PNV8_CHIP(chip);
     qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_EXTERNAL);
 
-    qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq);
+    qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq);
+
     return pnv_lpc_isa_create(&chip8->lpc, true, errp);
 }
 
@@ -737,25 +738,48 @@ static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp)
     Pnv8Chip *chip8 = PNV8_CHIP(chip);
     qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_LPC_I2C);
 
-    qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq);
+    qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq);
+
     return pnv_lpc_isa_create(&chip8->lpc, false, errp);
 }
 
 static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp)
 {
     Pnv9Chip *chip9 = PNV9_CHIP(chip);
-    qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC);
+    qemu_irq irq;
+
+    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC);
+    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "LPCHC", 0, irq);
+
+    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ0);
+    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 0, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ1);
+    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 1, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ2);
+    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 2, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ3);
+    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 3, irq);
 
-    qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq);
     return pnv_lpc_isa_create(&chip9->lpc, false, errp);
 }
 
 static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp)
 {
     Pnv10Chip *chip10 = PNV10_CHIP(chip);
-    qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC);
+    qemu_irq irq;
+
+    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC);
+    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "LPCHC", 0, irq);
+
+    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ0);
+    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 0, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ1);
+    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 1, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ2);
+    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 2, irq);
+    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ3);
+    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 3, irq);
 
-    qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq);
     return pnv_lpc_isa_create(&chip10->lpc, false, errp);
 }
 
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 252690dcaa..8d0895e6e8 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -64,6 +64,7 @@ enum {
 #define   LPC_HC_IRQSER_START_4CLK      0x00000000
 #define   LPC_HC_IRQSER_START_6CLK      0x01000000
 #define   LPC_HC_IRQSER_START_8CLK      0x02000000
+#define   LPC_HC_IRQSER_AUTO_CLEAR      0x00800000
 #define LPC_HC_IRQMASK          0x34    /* same bit defs as LPC_HC_IRQSTAT */
 #define LPC_HC_IRQSTAT          0x38
 #define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
@@ -420,32 +421,90 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
+/* Program the POWER9 LPC irq to PSI serirq routing table */
+static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc)
 {
-    bool lpc_to_opb_irq = false;
+    int irq;
 
-    /* Update LPC controller to OPB line */
-    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
-        uint32_t irqs;
+    if (!lpc->psi_has_serirq) {
+        if ((lpc->opb_irq_route0 & PPC_BITMASK(8, 13)) ||
+            (lpc->opb_irq_route1 & PPC_BITMASK(4, 31))) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "OPB: setting serirq routing on POWER8 system, ignoring.\n");
+        }
+        return;
+    }
 
-        irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
-        lpc_to_opb_irq = (irqs != 0);
+    for (irq = 0; irq <= 13; irq++) {
+        int serirq = (lpc->opb_irq_route1 >> (31 - 5 - (irq * 2))) & 0x3;
+        lpc->irq_to_serirq_route[irq] = serirq;
     }
 
-    /* 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;
+    for (irq = 14; irq < ISA_NUM_IRQS; irq++) {
+        int serirq = (lpc->opb_irq_route0 >> (31 - 9 - (irq * 2))) & 0x3;
+        lpc->irq_to_serirq_route[irq] = serirq;
     }
+}
 
-    /* Update OPB internal latch */
-    lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
+static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
+{
+    uint32_t active_irqs = 0;
+
+    if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) {
+        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: "
+                                 "0x%08"PRIx32"\n", lpc->lpc_hc_irqstat);
+    }
+
+    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
+        active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
+    }
 
     /* Reflect the interrupt */
-    qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0);
+    if (!lpc->psi_has_serirq) {
+        /*
+         * POWER8 ORs all irqs together (also with LPCHC internal interrupt
+         * sources) and outputs a single line that raises the PSI LPCHC irq
+         * which then latches an OPB IRQ status register that sends the irq
+         * to PSI.
+         */
+        /* We don't honor the polarity register, it's pointless and unused
+         * anyway
+         */
+        if (active_irqs) {
+            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;
+
+        qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
+    } else {
+        /*
+         * POWER9 and POWER10 have routing fields in OPB master registers that
+         * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs.
+         * These don't appear to get latched into an OPB register like the
+         * LPCHC irqs.
+         *
+         * POWER9 LPC controller internal irqs still go via the OPB
+         * and LPCHC PSI irqs like P8, but we have no such internal sources
+         * modelled yet.
+         */
+        bool serirq_out[4] = { false, false, false, false };
+        int irq;
+
+        for (irq = 0; irq < ISA_NUM_IRQS; irq++) {
+            if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) {
+                serirq_out[lpc->irq_to_serirq_route[irq]] = true;
+            }
+        }
+
+        qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]);
+        qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]);
+        qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]);
+        qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]);
+    }
 }
 
 static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
@@ -543,10 +602,10 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
     uint64_t val = 0xfffffffffffffffful;
 
     switch (addr) {
-    case OPB_MASTER_LS_ROUTE0: /* TODO */
+    case OPB_MASTER_LS_ROUTE0:
         val = lpc->opb_irq_route0;
         break;
-    case OPB_MASTER_LS_ROUTE1: /* TODO */
+    case OPB_MASTER_LS_ROUTE1:
         val = lpc->opb_irq_route1;
         break;
     case OPB_MASTER_LS_IRQ_STAT:
@@ -575,11 +634,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
     PnvLpcController *lpc = opaque;
 
     switch (addr) {
-    case OPB_MASTER_LS_ROUTE0: /* TODO */
+    case OPB_MASTER_LS_ROUTE0:
         lpc->opb_irq_route0 = val;
+        pnv_lpc_eval_serirq_routes(lpc);
+        pnv_lpc_eval_irqs(lpc);
         break;
-    case OPB_MASTER_LS_ROUTE1: /* TODO */
+    case OPB_MASTER_LS_ROUTE1:
         lpc->opb_irq_route1 = val;
+        pnv_lpc_eval_serirq_routes(lpc);
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_STAT:
         lpc->opb_irq_stat &= ~val;
@@ -664,6 +727,8 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
     PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
     Error *local_err = NULL;
 
+    object_property_set_bool(OBJECT(lpc), "psi-serirq", true, &error_abort);
+
     plc->parent_realize(dev, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
@@ -673,6 +738,9 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
     /* P9 uses a MMIO region */
     memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops,
                           lpc, "lpcm", PNV9_LPCM_SIZE);
+
+    /* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */
+    qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4);
 }
 
 static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
@@ -751,13 +819,19 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
                                 &lpc->lpc_hc_regs);
 
-    qdev_init_gpio_out(dev, &lpc->psi_irq, 1);
+    qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1);
 }
 
+static Property pnv_lpc_properties[] = {
+    DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void pnv_lpc_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    device_class_set_props(dc, pnv_lpc_properties);
     dc->realize = pnv_lpc_realize;
     dc->desc = "PowerNV LPC Controller";
     dc->user_creatable = false;
@@ -803,7 +877,7 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
     }
 
     if (pnv->cpld_irqstate != old_state) {
-        qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0);
+        qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0);
     }
 }
 
@@ -824,6 +898,13 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
         pnv_lpc_eval_irqs(lpc);
     } else {
         lpc->lpc_hc_irq_inputs &= ~irq_bit;
+
+        /* POWER9 adds an auto-clear mode that clears IRQSTAT bits on EOI */
+        if (lpc->psi_has_serirq &&
+            (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) {
+            lpc->lpc_hc_irqstat &= ~irq_bit;
+            pnv_lpc_eval_irqs(lpc);
+        }
     }
 }
 
@@ -854,6 +935,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
         handler = pnv_lpc_isa_irq_handler;
     }
 
+    /* POWER has a 17th irq, QEMU only implements the 16 regular device irqs */
     irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS);
 
     isa_bus_register_input_irqs(isa_bus, irqs);
-- 
2.43.0



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

* Re: [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts
  2024-05-28  6:20 ` [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts Nicholas Piggin
@ 2024-05-28 14:23   ` Miles Glenn
  0 siblings, 0 replies; 5+ messages in thread
From: Miles Glenn @ 2024-05-28 14:23 UTC (permalink / raw)
  To: Nicholas Piggin, qemu-ppc
  Cc: Glenn Miles, Cédric Le Goater, Frédéric Barrat,
	qemu-devel

Reviewed-by: Glenn Miles <milesg@linux.ibm.com>

Thanks,

Glenn

On Tue, 2024-05-28 at 16:20 +1000, Nicholas Piggin wrote:
> From: Glenn Miles <milesg@linux.vnet.ibm.com>
> 
> The LPC HC irq status register bits are set when an LPC IRQSER input
> is
> asserted. These irq status bits drive the PSI irq to the CPU
> interrupt
> controller. The LPC HC irq status bits are cleared by software
> writing
> to the register with 1's for the bits to clear.
> 
> Existing register write was clearing the irq status bits even when
> the
> input was asserted, this results in interrupts being lost.
> 
> This fix changes the behavior to keep track of the device IRQ status
> in internal state that is separate from the irq status register, and
> only allowing the irq status bits to be cleared if the associated
> input is not asserted.
> 
> [np: rebased before P9 PSI SERIRQ patch, adjust changelog/comments]
> Signed-off-by: Glenn Miles <milesg@linux.ibm.com>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  include/hw/ppc/pnv_lpc.h |  3 +++
>  hw/ppc/pnv_lpc.c         | 22 +++++++++++++++++++---
>  2 files changed, 22 insertions(+), 3 deletions(-)
> 
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> index 5d22c45570..97c6872c3f 100644
> --- a/include/hw/ppc/pnv_lpc.h
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -73,6 +73,9 @@ struct PnvLpcController {
>      uint32_t opb_irq_pol;
>      uint32_t opb_irq_input;
>  
> +    /* LPC device IRQ state */
> +    uint32_t lpc_hc_irq_inputs;
> +
>      /* LPC HC registers */
>      uint32_t lpc_hc_fw_seg_idsel;
>      uint32_t lpc_hc_fw_rd_acc_size;
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> index d692858bee..252690dcaa 100644
> --- a/hw/ppc/pnv_lpc.c
> +++ b/hw/ppc/pnv_lpc.c
> @@ -505,7 +505,14 @@ static void lpc_hc_write(void *opaque, hwaddr
> addr, uint64_t val,
>          pnv_lpc_eval_irqs(lpc);
>          break;
>      case LPC_HC_IRQSTAT:
> -        lpc->lpc_hc_irqstat &= ~val;
> +        /*
> +         * This register is write-to-clear for the IRQSER (LPC
> device IRQ)
> +         * status. However if the device has not de-asserted its
> interrupt
> +         * that will just raise this IRQ status bit again. Model
> this by
> +         * keeping track of the inputs and only clearing if the
> inputs are
> +         * deasserted.
> +         */
> +        lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs);
>          pnv_lpc_eval_irqs(lpc);
>          break;
>      case LPC_HC_ERROR_ADDRESS:
> @@ -803,11 +810,20 @@ 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)
>  {
>      PnvLpcController *lpc = PNV_LPC(opaque);
> +    uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n;
>  
> -    /* The Naples HW latches the 1 levels, clearing is done by SW */
>      if (level) {
> -        lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
> +        lpc->lpc_hc_irq_inputs |= irq_bit;
> +
> +        /*
> +	 * The LPC HC in Naples and later latches LPC IRQ into a bit
> field in
> +	 * the IRQSTAT register, and that drives the PSI IRQ to the IC.
> +	 * Software clears this bit manually (see LPC_HC_IRQSTAT
> handler).
> +         */
> +        lpc->lpc_hc_irqstat |= irq_bit;
>          pnv_lpc_eval_irqs(lpc);
> +    } else {
> +        lpc->lpc_hc_irq_inputs &= ~irq_bit;
>      }
>  }
>  



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

* Re: [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function
  2024-05-28  6:20 ` [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function Nicholas Piggin
@ 2024-05-29 14:31   ` Miles Glenn
  0 siblings, 0 replies; 5+ messages in thread
From: Miles Glenn @ 2024-05-29 14:31 UTC (permalink / raw)
  To: Nicholas Piggin, qemu-ppc
  Cc: Glenn Miles, Cédric Le Goater, Frédéric Barrat,
	qemu-devel

Reviewed-by: Glenn Miles <milesg@linux.ibm.com>

Thanks,

Glenn

On Tue, 2024-05-28 at 16:20 +1000, Nicholas Piggin wrote:
> The POWER8 LPC ISA device irqs all get combined and reported to the
> line
> connected the PSI LPCHC irq. POWER9 changed this so only internal LPC
> host controller irqs use that line, and the device irqs get routed to
> 4 new lines connected to PSI SERIRQ0-3.
> 
> POWER9 also introduced a new feature that automatically clears the
> irq
> status in the LPC host controller when EOI'ed, so software does not
> have
> to.
> 
> The powernv OPAL (skiboot) firmware managed to work because the LPCHC
> irq handler scanned all LPC irqs and handled those including clearing
> status even on POWER9 systems. So LPC irqs worked despite OPAL
> thinking
> it was running in POWER9 mode. After this change, UART interrupts
> show
> up on serirq1 which is where OPAL routes them to:
> 
>  cat /proc/interrupts
>  ...
>  20:          0  XIVE-IRQ 1048563 Level     opal-psi#0:lpchc
>  ...
>  25:         34  XIVE-IRQ 1048568 Level     opal-
> psi#0:lpc_serirq_mux1
> 
> Whereas they previously turn up on lpchc.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
> Since v1:
> - Fix and test power8
> - Rebase onto Glenn's fix
> - Move irq_to_serirq_route from global into PnvLpcController
> - Don't have SERIRQ irqs latch the OPB irq status register, docs
> don't
>   suggest they do and skiboot does not clear that bit for SERIRQ
> path.
> - Have the SERIRQ path use the LPCHC IRQ mask (missed in previous
>   patch).
> 
>  include/hw/ppc/pnv_lpc.h |  14 ++++-
>  hw/ppc/pnv.c             |  36 +++++++++--
>  hw/ppc/pnv_lpc.c         | 128 ++++++++++++++++++++++++++++++++-----
> --
>  3 files changed, 148 insertions(+), 30 deletions(-)
> 
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> index 97c6872c3f..e0fd5e4130 100644
> --- a/include/hw/ppc/pnv_lpc.h
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -23,6 +23,7 @@
>  #include "exec/memory.h"
>  #include "hw/ppc/pnv.h"
>  #include "hw/qdev-core.h"
> +#include "hw/isa/isa.h" /* For ISA_NUM_IRQS */
> 
>  #define TYPE_PNV_LPC "pnv-lpc"
>  typedef struct PnvLpcClass PnvLpcClass;
> @@ -87,8 +88,19 @@ struct PnvLpcController {
>      /* XSCOM registers */
>      MemoryRegion xscom_regs;
> 
> +    /*
> +     * In P8, ISA irqs are combined with internal sources to drive
> the
> +     * LPCHC interrupt output. P9 ISA irqs raise one of 4 lines that
> +     * drive PSI SERIRQ irqs, routing according to OPB routing
> registers.
> +     */
> +    bool psi_has_serirq;
> +
>      /* PSI to generate interrupts */
> -    qemu_irq psi_irq;
> +    qemu_irq psi_irq_lpchc;
> +
> +    /* P9 serirq lines and irq routing table */
> +    qemu_irq psi_irq_serirq[4];
> +    int irq_to_serirq_route[ISA_NUM_IRQS];
>  };
> 
>  struct PnvLpcClass {
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 6e3a5ccdec..f6c3e91b3a 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -728,7 +728,8 @@ static ISABus *pnv_chip_power8_isa_create(PnvChip
> *chip, Error **errp)
>      Pnv8Chip *chip8 = PNV8_CHIP(chip);
>      qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi),
> PSIHB_IRQ_EXTERNAL);
> 
> -    qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq);
> +    qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0,
> irq);
> +
>      return pnv_lpc_isa_create(&chip8->lpc, true, errp);
>  }
> 
> @@ -737,25 +738,48 @@ static ISABus
> *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp)
>      Pnv8Chip *chip8 = PNV8_CHIP(chip);
>      qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi),
> PSIHB_IRQ_LPC_I2C);
> 
> -    qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq);
> +    qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0,
> irq);
> +
>      return pnv_lpc_isa_create(&chip8->lpc, false, errp);
>  }
> 
>  static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error
> **errp)
>  {
>      Pnv9Chip *chip9 = PNV9_CHIP(chip);
> -    qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi),
> PSIHB9_IRQ_LPCHC);
> +    qemu_irq irq;
> +
> +    irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC);
> +    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "LPCHC", 0,
> irq);
> +
> +    irq = qdev_get_gpio_in(DEVICE(&chip9->psi),
> PSIHB9_IRQ_LPC_SIRQ0);
> +    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 0,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip9->psi),
> PSIHB9_IRQ_LPC_SIRQ1);
> +    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 1,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip9->psi),
> PSIHB9_IRQ_LPC_SIRQ2);
> +    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 2,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip9->psi),
> PSIHB9_IRQ_LPC_SIRQ3);
> +    qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 3,
> irq);
> 
> -    qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq);
>      return pnv_lpc_isa_create(&chip9->lpc, false, errp);
>  }
> 
>  static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error
> **errp)
>  {
>      Pnv10Chip *chip10 = PNV10_CHIP(chip);
> -    qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi),
> PSIHB9_IRQ_LPCHC);
> +    qemu_irq irq;
> +
> +    irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC);
> +    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "LPCHC", 0,
> irq);
> +
> +    irq = qdev_get_gpio_in(DEVICE(&chip10->psi),
> PSIHB9_IRQ_LPC_SIRQ0);
> +    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 0,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip10->psi),
> PSIHB9_IRQ_LPC_SIRQ1);
> +    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 1,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip10->psi),
> PSIHB9_IRQ_LPC_SIRQ2);
> +    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 2,
> irq);
> +    irq = qdev_get_gpio_in(DEVICE(&chip10->psi),
> PSIHB9_IRQ_LPC_SIRQ3);
> +    qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 3,
> irq);
> 
> -    qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq);
>      return pnv_lpc_isa_create(&chip10->lpc, false, errp);
>  }
> 
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> index 252690dcaa..8d0895e6e8 100644
> --- a/hw/ppc/pnv_lpc.c
> +++ b/hw/ppc/pnv_lpc.c
> @@ -64,6 +64,7 @@ enum {
>  #define   LPC_HC_IRQSER_START_4CLK      0x00000000
>  #define   LPC_HC_IRQSER_START_6CLK      0x01000000
>  #define   LPC_HC_IRQSER_START_8CLK      0x02000000
> +#define   LPC_HC_IRQSER_AUTO_CLEAR      0x00800000
>  #define LPC_HC_IRQMASK          0x34    /* same bit defs as
> LPC_HC_IRQSTAT */
>  #define LPC_HC_IRQSTAT          0x38
>  #define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down
> to ... */
> @@ -420,32 +421,90 @@ static const MemoryRegionOps pnv_lpc_mmio_ops =
> {
>      .endianness = DEVICE_BIG_ENDIAN,
>  };
> 
> -static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
> +/* Program the POWER9 LPC irq to PSI serirq routing table */
> +static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc)
>  {
> -    bool lpc_to_opb_irq = false;
> +    int irq;
> 
> -    /* Update LPC controller to OPB line */
> -    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
> -        uint32_t irqs;
> +    if (!lpc->psi_has_serirq) {
> +        if ((lpc->opb_irq_route0 & PPC_BITMASK(8, 13)) ||
> +            (lpc->opb_irq_route1 & PPC_BITMASK(4, 31))) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "OPB: setting serirq routing on POWER8 system,
> ignoring.\n");
> +        }
> +        return;
> +    }
> 
> -        irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
> -        lpc_to_opb_irq = (irqs != 0);
> +    for (irq = 0; irq <= 13; irq++) {
> +        int serirq = (lpc->opb_irq_route1 >> (31 - 5 - (irq * 2))) &
> 0x3;
> +        lpc->irq_to_serirq_route[irq] = serirq;
>      }
> 
> -    /* 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;
> +    for (irq = 14; irq < ISA_NUM_IRQS; irq++) {
> +        int serirq = (lpc->opb_irq_route0 >> (31 - 9 - (irq * 2))) &
> 0x3;
> +        lpc->irq_to_serirq_route[irq] = serirq;
>      }
> +}
> 
> -    /* Update OPB internal latch */
> -    lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
> +static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
> +{
> +    uint32_t active_irqs = 0;
> +
> +    if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) {
> +        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in
> IRQSTAT: "
> +                                 "0x%08"PRIx32"\n", lpc-
> >lpc_hc_irqstat);
> +    }
> +
> +    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
> +        active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
> +    }
> 
>      /* Reflect the interrupt */
> -    qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0);
> +    if (!lpc->psi_has_serirq) {
> +        /*
> +         * POWER8 ORs all irqs together (also with LPCHC internal
> interrupt
> +         * sources) and outputs a single line that raises the PSI
> LPCHC irq
> +         * which then latches an OPB IRQ status register that sends
> the irq
> +         * to PSI.
> +         */
> +        /* We don't honor the polarity register, it's pointless and
> unused
> +         * anyway
> +         */
> +        if (active_irqs) {
> +            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;
> +
> +        qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
> +    } else {
> +        /*
> +         * POWER9 and POWER10 have routing fields in OPB master
> registers that
> +         * send LPC irqs to 4 output lines that raise the PSI SERIRQ
> irqs.
> +         * These don't appear to get latched into an OPB register
> like the
> +         * LPCHC irqs.
> +         *
> +         * POWER9 LPC controller internal irqs still go via the OPB
> +         * and LPCHC PSI irqs like P8, but we have no such internal
> sources
> +         * modelled yet.
> +         */
> +        bool serirq_out[4] = { false, false, false, false };
> +        int irq;
> +
> +        for (irq = 0; irq < ISA_NUM_IRQS; irq++) {
> +            if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) {
> +                serirq_out[lpc->irq_to_serirq_route[irq]] = true;
> +            }
> +        }
> +
> +        qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]);
> +        qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]);
> +        qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]);
> +        qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]);
> +    }
>  }
> 
>  static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned
> size)
> @@ -543,10 +602,10 @@ static uint64_t opb_master_read(void *opaque,
> hwaddr addr, unsigned size)
>      uint64_t val = 0xfffffffffffffffful;
> 
>      switch (addr) {
> -    case OPB_MASTER_LS_ROUTE0: /* TODO */
> +    case OPB_MASTER_LS_ROUTE0:
>          val = lpc->opb_irq_route0;
>          break;
> -    case OPB_MASTER_LS_ROUTE1: /* TODO */
> +    case OPB_MASTER_LS_ROUTE1:
>          val = lpc->opb_irq_route1;
>          break;
>      case OPB_MASTER_LS_IRQ_STAT:
> @@ -575,11 +634,15 @@ static void opb_master_write(void *opaque,
> hwaddr addr,
>      PnvLpcController *lpc = opaque;
> 
>      switch (addr) {
> -    case OPB_MASTER_LS_ROUTE0: /* TODO */
> +    case OPB_MASTER_LS_ROUTE0:
>          lpc->opb_irq_route0 = val;
> +        pnv_lpc_eval_serirq_routes(lpc);
> +        pnv_lpc_eval_irqs(lpc);
>          break;
> -    case OPB_MASTER_LS_ROUTE1: /* TODO */
> +    case OPB_MASTER_LS_ROUTE1:
>          lpc->opb_irq_route1 = val;
> +        pnv_lpc_eval_serirq_routes(lpc);
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case OPB_MASTER_LS_IRQ_STAT:
>          lpc->opb_irq_stat &= ~val;
> @@ -664,6 +727,8 @@ static void pnv_lpc_power9_realize(DeviceState
> *dev, Error **errp)
>      PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
>      Error *local_err = NULL;
> 
> +    object_property_set_bool(OBJECT(lpc), "psi-serirq", true,
> &error_abort);
> +
>      plc->parent_realize(dev, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);
> @@ -673,6 +738,9 @@ static void pnv_lpc_power9_realize(DeviceState
> *dev, Error **errp)
>      /* P9 uses a MMIO region */
>      memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc),
> &pnv_lpc_mmio_ops,
>                            lpc, "lpcm", PNV9_LPCM_SIZE);
> +
> +    /* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */
> +    qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4);
>  }
> 
>  static void pnv_lpc_power9_class_init(ObjectClass *klass, void
> *data)
> @@ -751,13 +819,19 @@ static void pnv_lpc_realize(DeviceState *dev,
> Error **errp)
>      memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
>                                  &lpc->lpc_hc_regs);
> 
> -    qdev_init_gpio_out(dev, &lpc->psi_irq, 1);
> +    qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1);
>  }
> 
> +static Property pnv_lpc_properties[] = {
> +    DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq,
> false),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
>  static void pnv_lpc_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> 
> +    device_class_set_props(dc, pnv_lpc_properties);
>      dc->realize = pnv_lpc_realize;
>      dc->desc = "PowerNV LPC Controller";
>      dc->user_creatable = false;
> @@ -803,7 +877,7 @@ static void pnv_lpc_isa_irq_handler_cpld(void
> *opaque, int n, int level)
>      }
> 
>      if (pnv->cpld_irqstate != old_state) {
> -        qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0);
> +        qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0);
>      }
>  }
> 
> @@ -824,6 +898,13 @@ static void pnv_lpc_isa_irq_handler(void
> *opaque, int n, int level)
>          pnv_lpc_eval_irqs(lpc);
>      } else {
>          lpc->lpc_hc_irq_inputs &= ~irq_bit;
> +
> +        /* POWER9 adds an auto-clear mode that clears IRQSTAT bits
> on EOI */
> +        if (lpc->psi_has_serirq &&
> +            (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) {
> +            lpc->lpc_hc_irqstat &= ~irq_bit;
> +            pnv_lpc_eval_irqs(lpc);
> +        }
>      }
>  }
> 
> @@ -854,6 +935,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc,
> bool use_cpld, Error **errp)
>          handler = pnv_lpc_isa_irq_handler;
>      }
> 
> +    /* POWER has a 17th irq, QEMU only implements the 16 regular
> device irqs */
>      irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS);
> 
>      isa_bus_register_input_irqs(isa_bus, irqs);



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

end of thread, other threads:[~2024-05-29 14:31 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-28  6:20 [PATCH 0/2] ppc/pnv: LPC interrupt fixes Nicholas Piggin
2024-05-28  6:20 ` [PATCH 1/2] ppc/pnv: Fix loss of LPC SERIRQ interrupts Nicholas Piggin
2024-05-28 14:23   ` Miles Glenn
2024-05-28  6:20 ` [PATCH 2/2] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function Nicholas Piggin
2024-05-29 14:31   ` Miles Glenn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).