[Skiboot] [PATCH 41/60] xive/phb4: Work around broken LSI control on P9 DD1

Benjamin Herrenschmidt benh at kernel.crashing.org
Thu Dec 22 14:16:49 AEDT 2016


Exposes a new flag to the OS to indicate that OPAL calls
are needed for masking and unmasking and forward the calls
to the source so that PHB4 can do the right thing

Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---
 core/interrupts.c    | 14 +++++++++++---
 hw/phb4.c            | 44 +++++++++++++++++++++++++++++++++++++++++++-
 hw/xive.c            | 24 ++++++++++++++++++++----
 include/interrupts.h |  1 +
 include/opal-api.h   |  1 +
 5 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/core/interrupts.c b/core/interrupts.c
index c01a430..91da627 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -394,15 +394,23 @@ uint32_t p8_irq_to_phb(uint32_t irq)
 	return p8_irq_to_block(irq) - P8_IRQ_BLOCK_PHB_BASE;
 }
 
+bool __irq_source_eoi(struct irq_source *is, uint32_t isn)
+{
+	if (!is->ops->eoi)
+		return false;
+
+	is->ops->eoi(is, isn);
+	return true;
+}
+
 bool irq_source_eoi(uint32_t isn)
 {
 	struct irq_source *is = irq_find_source(isn);
 
-	if (!is || !is->ops->eoi)
+	if (!is)
 		return false;
 
-	is->ops->eoi(is, isn);
-	return true;
+	return __irq_source_eoi(is, isn);
 }
 
 static int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority)
diff --git a/hw/phb4.c b/hw/phb4.c
index fa8bc7b..20befa6 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -3041,6 +3041,45 @@ static uint64_t phb4_lsi_attributes(struct irq_source *is __unused,
 	return IRQ_ATTR_TARGET_LINUX;
 }
 
+static int64_t phb4_dd1_lsi_set_xive(struct irq_source *is, uint32_t isn,
+				     uint16_t server, uint8_t priority)
+{
+	struct phb4 *p = is->data;
+	uint32_t idx = isn - p->base_lsi;
+
+	PHBERR(p, "DD1 LSI set_xive idx %d prio=%d\n", idx, priority);
+
+	if (idx > 8)
+		return OPAL_PARAMETER;
+
+	phb_lock(&p->phb);
+
+	phb4_ioda_sel(p, IODA3_TBL_LIST, idx, false);
+
+	/* Mask using P=0,Q=1, unmask using P=1,Q=0 followed by EOI */
+	/* XXX FIXME: A quick mask/umask can make us shoot an interrupt
+	 * more than once to a queue. We need to keep track better
+	 */
+	PHBERR(p, " LIST before: %016llx\n", in_be64(p->regs + PHB_IODA_DATA0));
+	if (priority == 0xff)
+		out_be64(p->regs + PHB_IODA_DATA0, IODA3_LIST_Q);
+	else {
+		out_be64(p->regs + PHB_IODA_DATA0, IODA3_LIST_P);
+		__irq_source_eoi(is, isn);
+	}
+	PHBERR(p, " LIST after: %016llx\n", in_be64(p->regs + PHB_IODA_DATA0));
+
+	phb_unlock(&p->phb);
+
+	return 0;
+}
+
+static const struct irq_source_ops phb4_dd1_lsi_ops = {
+	.set_xive = phb4_dd1_lsi_set_xive,
+	.interrupt = phb4_err_interrupt,
+	.attributes = phb4_lsi_attributes,
+};
+
 static const struct irq_source_ops phb4_lsi_ops = {
 	.interrupt = phb4_err_interrupt,
 	.attributes = phb4_lsi_attributes,
@@ -3208,10 +3247,13 @@ static void phb4_create(struct dt_node *np)
 	xive_register_hw_source(p->base_msi, p->num_irqs - 8, 16,
 				p->int_mmio, XIVE_SRC_SHIFT_BUG,
 				NULL, NULL);
+
 	xive_register_hw_source(p->base_lsi, 8, 16,
 				p->int_mmio + ((p->num_irqs - 8) << 16),
 				XIVE_SRC_LSI | XIVE_SRC_SHIFT_BUG,
-				p, &phb4_lsi_ops);
+				p,
+				(p->rev == PHB4_REV_NIMBUS_DD10) ?
+				&phb4_dd1_lsi_ops : &phb4_lsi_ops);
 
 	/* Platform additional setup */
 	if (platform.pci_setup_phb)
diff --git a/hw/xive.c b/hw/xive.c
index 15e766a..16585fc 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -2285,8 +2285,12 @@ static int64_t xive_source_set_xive(struct irq_source *is, uint32_t isn,
 	if (rc)
 		return rc;
 
-	/* Ensure it's enabled/disabled in the source controller */
-	xive_update_irq_mask(s, isn - s->esb_base, prio == 0xff);
+	/* The source has special variants of masking/unmasking */
+	if (s->orig_ops && s->orig_ops->set_xive)
+		rc = s->orig_ops->set_xive(is, isn, server, prio);
+	else
+		/* Ensure it's enabled/disabled in the source controller */
+		xive_update_irq_mask(s, isn - s->esb_base, prio == 0xff);
 
 	return OPAL_SUCCESS;
 }
@@ -3105,6 +3109,14 @@ static int64_t opal_xive_get_irq_info(uint32_t girq,
 	assert(is->ops == &xive_irq_source_ops);
 
 	*out_flags = s->flags;
+	/*
+	 * If the orig source has a set_xive callback, then set
+	 * OPAL_XIVE_IRQ_MASK_VIA_FW as masking/unmasking requires
+	 * source specific workarounds.
+	 */
+	if (out_flags && s->orig_ops && s->orig_ops->set_xive)
+		*out_flags |= OPAL_XIVE_IRQ_MASK_VIA_FW;
+
 	idx = girq - s->esb_base;
 
 	if (out_esb_shift)
@@ -3167,8 +3179,12 @@ static int64_t opal_xive_set_irq_config(uint32_t girq,
 	if (rc)
 		return rc;
 
-	/* Ensure it's enabled/disabled in the source controller */
-	xive_update_irq_mask(s, girq - s->esb_base, prio == 0xff);
+	/* The source has special variants of masking/unmasking */
+	if (s->orig_ops && s->orig_ops->set_xive)
+		rc = s->orig_ops->set_xive(is, girq, vp >> 2, prio);
+	else
+		/* Ensure it's enabled/disabled in the source controller */
+		xive_update_irq_mask(s, girq - s->esb_base, prio == 0xff);
 
 	return OPAL_SUCCESS;
 }
diff --git a/include/interrupts.h b/include/interrupts.h
index 2de1f97..7576610 100644
--- a/include/interrupts.h
+++ b/include/interrupts.h
@@ -317,6 +317,7 @@ extern void icp_kick_cpu(struct cpu_thread *cpu);
 extern void init_interrupts(void);
 
 extern bool irq_source_eoi(uint32_t isn);
+extern bool __irq_source_eoi(struct irq_source *is, uint32_t isn);
 
 
 #endif /* __INTERRUPTS_H */
diff --git a/include/opal-api.h b/include/opal-api.h
index 6220e1b..9af8a45 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -1101,6 +1101,7 @@ enum {
 	OPAL_XIVE_IRQ_STORE_EOI		= 0x00000002,
 	OPAL_XIVE_IRQ_LSI		= 0x00000004,
 	OPAL_XIVE_IRQ_SHIFT_BUG		= 0x00000008,
+	OPAL_XIVE_IRQ_MASK_VIA_FW	= 0x00000010,
 };
 
 /* Flags for OPAL_XIVE_GET/SET_QUEUE_INFO */
-- 
2.9.3



More information about the Skiboot mailing list