[Skiboot] [PATCH 06/12] lpc: Add P9 LPC interrupts support

Michael Neuling mikey at neuling.org
Thu Jul 21 17:20:44 AEST 2016


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

We currently don't exploit the new MUX that allow to spread them
around different PSI interrupts, they all go to LPC#0

Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
Signed-off-by: Michael Neuling <mikey at neuling.org>
---
 core/init.c                              |   6 ++
 doc/device-tree/examples/power9-phb4.dts |   1 +
 hw/lpc.c                                 | 112 ++++++++++++++++++++++++++-----
 hw/psi.c                                 |  29 ++++----
 include/lpc.h                            |   7 ++
 include/psi.h                            |   2 -
 platforms/astbmc/common.c                |   1 +
 7 files changed, 128 insertions(+), 30 deletions(-)

diff --git a/core/init.c b/core/init.c
index ca3ad55..86bcdea 100644
--- a/core/init.c
+++ b/core/init.c
@@ -724,6 +724,12 @@ void __noreturn main_cpu_entry(const void *fdt, u32 master_cpu)
 	/* Initialize PSI (depends on probe_platform being called) */
 	psi_init();
 
+	/* Initialize/enable LPC interrupts. This must be done after the
+	 * PSI interface has been initialized since it serves as an interrupt
+	 * source for LPC interrupts.
+	 */
+	lpc_init_interrupts();
+
 	/* Call in secondary CPUs */
 	cpu_bringup();
 
diff --git a/doc/device-tree/examples/power9-phb4.dts b/doc/device-tree/examples/power9-phb4.dts
index 36de6e2..e5743f3 100644
--- a/doc/device-tree/examples/power9-phb4.dts
+++ b/doc/device-tree/examples/power9-phb4.dts
@@ -190,6 +190,7 @@
 		        reg = < 1 0x3f8 0x10 >;
 			current-speed = < 115200 >;
 			clock-frequency = < 1843200 >;
+			interrupts = <4>;
 		    };
 		};
 	};
diff --git a/hw/lpc.c b/hw/lpc.c
index 32cb7b1..e92d356 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -117,6 +117,7 @@ struct lpc_client_entry {
 
 /* Default LPC bus */
 static int32_t lpc_default_chip_id = -1;
+static bool lpc_irqs_ready;
 
 /*
  * These are expected to be the same on all chips and should probably
@@ -546,6 +547,9 @@ static void lpc_setup_serirq(struct proc_chip *chip)
 	uint32_t mask = LPC_HC_IRQ_BASE_IRQS;
 	int rc;
 
+	if (!lpc_irqs_ready)
+		return;
+
 	/* Collect serirq enable bits */
 	list_for_each(&chip->lpc_clients, ent, node)
 		mask |= ent->clt->interrupts & LPC_HC_IRQ_SERIRQ_ALL;
@@ -584,7 +588,8 @@ static void lpc_setup_serirq(struct proc_chip *chip)
 		else
 			DBG_IRQ("LPC: MASK READBACK=%x\n", val);
 
-		rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, &val, 4);
+		rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL,
+			      &val, 4);
 		if (rc)
 			prerror("Failed to readback ctrl");
 		else
@@ -592,10 +597,42 @@ static void lpc_setup_serirq(struct proc_chip *chip)
 	}
 }
 
-static void lpc_init_interrupts(struct proc_chip *chip)
+void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx)
+{
+	struct proc_chip *chip = get_chip(chip_id);
+	uint32_t reg, shift, val;
+	int64_t rc;
+
+	assert(chip);
+	assert(proc_gen == proc_gen_p9);
+
+	if (sirq < 14) {
+		reg = 0xc;
+		shift = 4 + (sirq << 1);
+	} else {
+		reg = 0x8;
+		shift = 8 + ((sirq - 14) << 1);
+	}
+	shift = 30-shift;
+	rc = opb_read(chip, opb_master_reg_base + reg, &val, 4);
+	if (rc)
+		return;
+	val = val & ~(3 << shift);
+	val |= (psi_idx & 3) << shift;
+	opb_write(chip, opb_master_reg_base + reg, val, 4);
+}
+
+void lpc_init_interrupts(void)
 {
+	struct proc_chip *chip;
 	int rc;
 
+	if (lpc_default_chip_id < 0)
+		return;
+	chip = get_chip(lpc_default_chip_id);
+	if (chip == NULL)
+		return;
+
 	/* First mask them all */
 	rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
 	if (rc) {
@@ -603,6 +640,8 @@ static void lpc_init_interrupts(struct proc_chip *chip)
 		return;
 	}
 
+	lpc_irqs_ready = true;
+
 	switch(chip->type) {
 	case PROC_CHIP_P8_MURANO:
 	case PROC_CHIP_P8_VENICE:
@@ -618,6 +657,8 @@ static void lpc_init_interrupts(struct proc_chip *chip)
 		opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4);
 		break;
 	case PROC_CHIP_P8_NAPLES:
+	case PROC_CHIP_P9_NIMBUS:
+	case PROC_CHIP_P9_CUMULUS:
 		/* On Naples, we support LPC interrupts, enable them based
 		 * on what clients requests. This will setup the mask and
 		 * enable processing
@@ -726,8 +767,7 @@ static void lpc_dispatch_ser_irqs(struct proc_chip *chip, uint32_t irqs,
 	if (!clear_latch)
 		return;
 
-	rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT,
-		       irqs, 4);
+	rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT, irqs, 4);
 	if (rc)
 		prerror("Failed to clear SerIRQ latches !\n");
 }
@@ -752,6 +792,8 @@ void lpc_interrupt(uint32_t chip_id)
 		goto bail;
 	}
 
+	DBG_IRQ("LPC: OPB IRQ on chip 0x%x, oirqs=0x%08x\n", chip_id, opb_irqs);
+
 	/* Check if it's an LPC interrupt */
 	if (!(opb_irqs & OPB_MASTER_IRQ_LPC)) {
 		/* Something we don't support ? Ack it anyway... */
@@ -767,7 +809,7 @@ void lpc_interrupt(uint32_t chip_id)
 		goto bail;
 	}
 
-	DBG_IRQ("LPC: IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs);
+	DBG_IRQ("LPC: LPC IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs);
 
 	/* Handle error interrupts */
 	if (irqs & LPC_HC_IRQ_BASE_IRQS)
@@ -777,9 +819,35 @@ void lpc_interrupt(uint32_t chip_id)
 	if (irqs & LPC_HC_IRQ_SERIRQ_ALL)
 		lpc_dispatch_ser_irqs(chip, irqs, true);
 
-	/* Ack it at the OPB level */
-	opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT,
-		  opb_irqs, 4);
+ bail:
+	unlock(&chip->lpc_lock);
+}
+
+void lpc_serirq(uint32_t chip_id, uint32_t index __unused)
+{
+	struct proc_chip *chip = get_chip(chip_id);
+	uint32_t irqs;
+	int rc;
+
+	/* No initialized LPC controller on that chip */
+	if (!chip || (!chip->lpc_xbase && !chip->lpc_mbase))
+		return;
+
+	lock(&chip->lpc_lock);
+
+	/* Handle the lpc interrupt source (errors etc...) */
+	rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4);
+	if (rc) {
+		prerror("LPC: Failed to read LPC IRQ state\n");
+		goto bail;
+	}
+
+	DBG_IRQ("LPC: IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs);
+
+	/* Handle SerIRQ interrupts */
+	if (irqs & LPC_HC_IRQ_SERIRQ_ALL)
+		lpc_dispatch_ser_irqs(chip, irqs, true);
+
  bail:
 	unlock(&chip->lpc_lock);
 }
@@ -812,10 +880,11 @@ static void lpc_init_chip_p8(struct dt_node *xn)
 		lpc_default_chip_id = chip->id;
 	}
 
-	prlog(PR_NOTICE, "Bus on chip %d, access via XSCOM, PCB_Addr=0x%x\n",
-	      chip->id, chip->lpc_xbase);
+	/* Mask all interrupts for now */
+	opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
+
+	printf("LPC: Bus on chip %d, access via XSCOM, PCB_Addr=0x%x\n", chip->id, chip->lpc_xbase);
 
-	lpc_init_interrupts(chip);
 	dt_add_property(xn, "interrupt-controller", NULL, 0);
 	dt_add_property_cells(xn, "#interrupt-cells", 1);
 	assert(dt_prop_get_u32(xn, "#address-cells") == 2);
@@ -826,6 +895,7 @@ static void lpc_init_chip_p9(struct dt_node *opb_node)
 	uint32_t gcid = dt_get_chip_id(opb_node);
 	struct proc_chip *chip;
 	u64 addr;
+	u32 val;
 
 	chip = get_chip(gcid);
 	assert(chip);
@@ -845,11 +915,19 @@ static void lpc_init_chip_p9(struct dt_node *opb_node)
 		lpc_default_chip_id = chip->id;
 	}
 
-	prlog(PR_NOTICE, "Bus on chip %d, access via MMIO @%p\n",
-	       chip->id, chip->lpc_mbase);
+	/* Mask all interrupts for now */
+	opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
+
+	/* On P9, setup routing to PSI SerIRQ 0 */
+	opb_read(chip, opb_master_reg_base + 8, &val, 4);
+	val &= 0xff03ffff;
+	opb_write(chip, opb_master_reg_base + 8, val, 4);
+	opb_read(chip, opb_master_reg_base + 0xc, &val, 4);
+	val &= 0xf0000000;
+	opb_write(chip, opb_master_reg_base + 0xc, val, 4);
 
-	// XXX TODO
-	//lpc_init_interrupts(chip);
+	printf("LPC: Bus on chip %d, access via MMIO @%p\n",
+		gcid, chip->lpc_mbase);
 }
 
 void lpc_init(void)
@@ -914,7 +992,9 @@ void lpc_register_client(uint32_t chip_id,
 	lock(&chip->lpc_lock);
 	list_add(&chip->lpc_clients, &ent->node);
 	/* Re-evaluate ser irqs on Naples */
-	if (chip->type == PROC_CHIP_P8_NAPLES)
+	if (chip->type == PROC_CHIP_P8_NAPLES ||
+	    chip->type == PROC_CHIP_P9_NIMBUS ||
+	    chip->type == PROC_CHIP_P9_CUMULUS)
 		lpc_setup_serirq(chip);
 	unlock(&chip->lpc_lock);
 }
diff --git a/hw/psi.c b/hw/psi.c
index 08dc589..16f88c2 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -38,7 +38,7 @@ static LIST_HEAD(psis);
 static u64 psi_link_timer;
 static u64 psi_link_timeout;
 static bool psi_link_poll_active;
-static bool psi_ext_irq_policy = EXTERNAL_IRQ_POLICY_LINUX;
+static bool psi_ext_irq_policy = EXTERNAL_IRQ_POLICY_SKIBOOT;
 
 static void psi_activate_phb(struct psi *psi);
 
@@ -519,7 +519,6 @@ static void psihb_p8_interrupt(struct irq_source *is, uint32_t isn)
 		break;
 	case P8_IRQ_PSI_LPC:
 		lpc_interrupt(psi->chip_id);
-
 		/*
 		 * i2c interrupts are ORed with the LPC ones on
 		 * Murano DD2.1 and Venice DD2.0
@@ -598,7 +597,7 @@ static void psihb_p9_interrupt(struct irq_source *is, uint32_t isn)
 	case P9_PSI_IRQ_LPC_SIRQ1:
 	case P9_PSI_IRQ_LPC_SIRQ2:
 	case P9_PSI_IRQ_LPC_SIRQ3:
-		/* XXX TODO */
+		lpc_serirq(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0);
 		break;
 	case P9_PSI_IRQ_SBE_I2C:
 		p8_i2c_interrupt(psi->chip_id);
@@ -770,6 +769,16 @@ static void psi_init_p9_interrupts(struct psi *psi)
 
 #define PSIHB_ESB_MMIO_DEFAULT 0x0060302031c0000ull
 
+	/* Setup interrupt offset */
+	val = xive_get_notify_base(psi->interrupt);
+	val <<= 32;
+	out_be64(psi->regs + PSIHB_IVT_OFFSET, val);
+
+	/* Grab and configure the notification port */
+	val = xive_get_notify_port(psi->chip_id, XIVE_HW_SRC_PSI);
+	val |= PSIHB_ESB_NOTIF_VALID;
+	out_be64(psi->regs + PSIHB_ESB_NOTIF_ADDR, val);
+
 	/* Configure the CI BAR if necessary */
 	val = in_be64(psi->regs + PSIHB_ESB_CI_BASE);
 	if (!(val & PSIHB_ESB_CI_VALID)) {
@@ -782,15 +791,9 @@ static void psi_init_p9_interrupts(struct psi *psi)
 	psi->esb_mmio = (void *)(val & ~PSIHB_ESB_CI_VALID);
 	printf("PSI[0x%03x]: ESB MMIO at @%p\n", psi->chip_id, psi->esb_mmio);
 
-	/* Grab and configure the notification port */
-	val = xive_get_notify_port(psi->chip_id, XIVE_HW_SRC_PSI);
-	val |= PSIHB_ESB_NOTIF_VALID;
-	out_be64(psi->regs + PSIHB_ESB_NOTIF_ADDR, val);
-
-	/* Setup interrupt offset */
-	val = xive_get_notify_base(psi->interrupt);
-	val <<= 32;
-	out_be64(psi->regs + PSIHB_IVT_OFFSET, val);
+	/* XXX HACK */
+	out_be64(psi->regs + PSIHB_CR,
+		 in_be64(psi->regs + PSIHB_CR) | PSIHB_CR_FSP_IRQ_ENABLE);
 
 	/* Register sources */
 	xive_register_source(psi->interrupt, P9_PSI_NUM_IRQS,
@@ -978,6 +981,8 @@ static struct psi *psi_probe_p9(struct proc_chip *chip, u64 base)
 	psi->working = true;
 	psi->regs = (void *)(val & ~PSIHB_XSCOM_P9_HBBAR_EN);
 
+	printf("PSI[0x%03x]: PSI MMIO at @%p\n", psi->chip_id, psi->regs);
+
 	psi->interrupt = xive_alloc_hw_irqs(psi->chip_id, P9_PSI_NUM_IRQS, 16);
 	return psi;
 }
diff --git a/include/lpc.h b/include/lpc.h
index a79d256..42c4755 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -59,6 +59,7 @@
 /* Routines for accessing the LPC bus on Power8 */
 
 extern void lpc_init(void);
+extern void lpc_init_interrupts(void);
 
 /* Check for a default bus */
 extern bool lpc_present(void);
@@ -72,6 +73,9 @@ extern bool lpc_ok(void);
 /* Handle the interrupt from the LPC controller */
 extern void lpc_interrupt(uint32_t chip_id);
 
+/* On P9, we have a different route for SerIRQ */
+extern void lpc_serirq(uint32_t chip_id, uint32_t index);
+
 /* Call all external handlers */
 extern void lpc_all_interrupts(uint32_t chip_id);
 
@@ -100,6 +104,9 @@ extern int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
 /* Mark LPC bus as used by console */
 extern void lpc_used_by_console(void);
 
+/* Route SerIRQs to specific PSI/LPC interrupt sources */
+void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx);
+
 /*
  * Simplified big endian FW accessors
  */
diff --git a/include/psi.h b/include/psi.h
index 68e076c..8832762 100644
--- a/include/psi.h
+++ b/include/psi.h
@@ -161,8 +161,6 @@
 #define P9_PSI_IRQ_PSU			13
 #define P9_PSI_NUM_IRQS			16
 
-
-
 /*
  * Layout of the PSI DMA address space
  *
diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c
index e1a8a4d..c53ed16 100644
--- a/platforms/astbmc/common.c
+++ b/platforms/astbmc/common.c
@@ -120,6 +120,7 @@ void astbmc_init(void)
 
 	/* Register the BT interface with the IPMI layer */
 	bt_init();
+
 	/* Initialize elog */
 	elog_init();
 	ipmi_sel_init();
-- 
2.7.4



More information about the Skiboot mailing list