[Skiboot] [PATCH 3/5] xive/psi/lpc: Handle proper clearing of LPC SerIRQ latch on DD1

Benjamin Herrenschmidt benh at kernel.crashing.org
Fri Feb 3 20:51:58 AEDT 2017


On DD1, the LPC SerIRQ are latched to 1 in HW but never back to 0,
we need an explicit clear after running the handler. (Not before
as they are level interrupts, they will be latched again if they
are still pending).

For now we do that in lpc_dispatch_ser_irqs() but that only works
for interrupts routed to OPAL.

In order to support routing LPC interrutps to Linux, we need a custom
EOI handler that does the clearing of the latch before we do the
EOI in the ESB.

Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---
 hw/lpc.c       | 25 +++++++++++++++++++++++--
 hw/psi.c       | 41 ++++++++++++++++++++++++++++++++++++++---
 hw/xive.c      | 12 +++++++++++-
 include/lpc.h  |  3 +++
 include/xive.h |  4 ++++
 5 files changed, 79 insertions(+), 6 deletions(-)

diff --git a/hw/lpc.c b/hw/lpc.c
index babc788..6dccf35 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -959,14 +959,35 @@ void lpc_serirq(uint32_t chip_id, uint32_t index)
 		chip_id, irqs, rmask);
 	irqs &= rmask;
 
-	/* Handle SerIRQ interrupts */
+	/*
+	 * Handle SerIRQ interrupts. Don't clear the latch,
+	 * it will be done in our special EOI callback if
+	 * necessary on DD1
+	 */
 	if (irqs)
-		lpc_dispatch_ser_irqs(lpc, irqs, true);
+		lpc_dispatch_ser_irqs(lpc, irqs, false);
 
  bail:
 	unlock(&lpc->lock);
 }
 
+void lpc_p9_sirq_eoi(uint32_t chip_id, uint32_t index)
+{
+	struct proc_chip *chip = get_chip(chip_id);
+	struct lpcm *lpc;
+	uint32_t rmask;
+
+	/* No initialized LPC controller on that chip */
+	if (!chip || !chip->lpc)
+		return;
+	lpc = chip->lpc;
+
+	lock(&lpc->lock);
+	rmask = lpc->sirq_rmasks[index];
+	opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, rmask, 4);
+	unlock(&lpc->lock);
+}
+
 void lpc_all_interrupts(uint32_t chip_id)
 {
 	struct proc_chip *chip = get_chip(chip_id);
diff --git a/hw/psi.c b/hw/psi.c
index 5d97fbb..93b5a75 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -657,6 +657,24 @@ static char *psi_p9_irq_name(struct irq_source *is, uint32_t isn)
 	return strdup(names[idx]);
 }
 
+static void psi_p9_irq_dd1_eoi(struct irq_source *is, uint32_t isn)
+{
+	struct psi *psi = is->data;
+	unsigned int idx = isn & 0xf;
+
+	if (idx >= P9_PSI_IRQ_LPC_SIRQ0 &&
+	    idx <= P9_PSI_IRQ_LPC_SIRQ3)
+		lpc_p9_sirq_eoi(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0);
+	__xive_source_eoi(is, isn);
+}
+
+static const struct irq_source_ops psi_p9_dd1_irq_ops = {
+	.interrupt = psihb_p9_interrupt,
+	.attributes = psi_p9_irq_attributes,
+	.name = psi_p9_irq_name,
+	.eoi = psi_p9_irq_dd1_eoi,
+};
+
 static const struct irq_source_ops psi_p9_irq_ops = {
 	.interrupt = psihb_p9_interrupt,
 	.attributes = psi_p9_irq_attributes,
@@ -797,6 +815,8 @@ static void psi_init_p8_interrupts(struct psi *psi)
 
 static void psi_init_p9_interrupts(struct psi *psi)
 {
+	struct proc_chip *c;
+	bool is_dd2;
 	u64 val;
 
 	/* Reset irq handling and switch to ESB mode */
@@ -829,9 +849,24 @@ static void psi_init_p9_interrupts(struct psi *psi)
 	out_be64(psi->regs + PSIHB_IVT_OFFSET, val);
 
 	/* Register sources */
-	xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
-				12, psi->esb_mmio, XIVE_SRC_LSI,
-				psi, &psi_p9_irq_ops);
+	c = next_chip(NULL);
+	is_dd2 = (c && c->ec_level >= 0x20);
+
+	if (is_dd2) {
+		prlog(PR_DEBUG,
+		      "PSI[0x%03x]: Interrupts sources registered for P9 DD2.x\n",
+		      psi->chip_id);
+		xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
+					12, psi->esb_mmio, XIVE_SRC_LSI,
+					psi, &psi_p9_irq_ops);
+	} else {
+		prlog(PR_DEBUG,
+		      "PSI[0x%03x]: Interrupts sources registered for P9 DD1.x\n",
+		      psi->chip_id);
+		xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
+					12, psi->esb_mmio, XIVE_SRC_LSI,
+					psi, &psi_p9_dd1_irq_ops);
+	}
 }
 
 static void psi_init_interrupts(struct psi *psi)
diff --git a/hw/xive.c b/hw/xive.c
index 363c7f0..a744c0b 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -2371,7 +2371,7 @@ static int64_t xive_source_set_xive(struct irq_source *is, uint32_t isn,
 	return OPAL_SUCCESS;
 }
 
-static void xive_source_eoi(struct irq_source *is, uint32_t isn)
+void __xive_source_eoi(struct irq_source *is, uint32_t isn)
 {
 	struct xive_src *s = container_of(is, struct xive_src, is);
 	uint32_t idx = isn - s->esb_base;
@@ -2431,6 +2431,16 @@ static void xive_source_eoi(struct irq_source *is, uint32_t isn)
 	}
 }
 
+static void xive_source_eoi(struct irq_source *is, uint32_t isn)
+{
+	struct xive_src *s = container_of(is, struct xive_src, is);
+
+	if (s->orig_ops && s->orig_ops->eoi)
+		s->orig_ops->eoi(is, isn);
+
+	__xive_source_eoi(is, isn);
+}
+
 static void xive_source_interrupt(struct irq_source *is, uint32_t isn)
 {
 	struct xive_src *s = container_of(is, struct xive_src, is);
diff --git a/include/lpc.h b/include/lpc.h
index 59f0779..3e92d53 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -98,6 +98,9 @@ extern void lpc_register_client(uint32_t chip_id, const struct lpc_client *clt);
 /* Manual control of routing on P9 for use by platforms if necessary */
 extern void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx);
 
+/* Clear SerIRQ latch on P9 DD1 */
+extern void lpc_p9_sirq_eoi(uint32_t chip_id, uint32_t index);
+
 /* Default bus accessors */
 extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
 			 uint32_t data, uint32_t sz);
diff --git a/include/xive.h b/include/xive.h
index e9a01c3..58c4da9 100644
--- a/include/xive.h
+++ b/include/xive.h
@@ -460,4 +460,8 @@ void xive_cpu_callin(struct cpu_thread *cpu);
  */
 void *xive_get_trigger_port(uint32_t girq);
 
+/* To be used by special EOI override in PSI */
+struct irq_source;
+void __xive_source_eoi(struct irq_source *is, uint32_t isn);
+
 #endif /* __XIVE_H__ */
-- 
2.9.3



More information about the Skiboot mailing list