[Skiboot] [PATCH 06/15] lpc: Add P9 LPC interrupts support
Benjamin Herrenschmidt
benh at kernel.crashing.org
Tue Aug 9 16:38:10 AEST 2016
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>
---
core/init.c | 6 ++
doc/device-tree/examples/power9-phb4.dts | 1 +
hw/lpc.c | 116 ++++++++++++++++++++++++++-----
hw/psi.c | 2 +-
include/lpc.h | 7 ++
5 files changed, 114 insertions(+), 18 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..8def775 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");
}
@@ -749,14 +789,15 @@ void lpc_interrupt(uint32_t chip_id)
&opb_irqs, 4);
if (rc) {
prerror("Failed to read OPB IRQ state\n");
- goto bail;
+ unlock(&chip->lpc_lock);
+ return;
}
+ 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... */
- opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT,
- opb_irqs, 4);
goto bail;
}
@@ -767,7 +808,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)
@@ -776,10 +817,38 @@ void lpc_interrupt(uint32_t chip_id)
/* Handle SerIRQ interrupts */
if (irqs & LPC_HC_IRQ_SERIRQ_ALL)
lpc_dispatch_ser_irqs(chip, irqs, true);
-
+bail:
/* Ack it at the OPB level */
opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT,
opb_irqs, 4);
+ 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 +881,12 @@ 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 +897,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 +917,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 +994,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..663b604 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -598,7 +598,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);
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
*/
--
2.7.4
More information about the Skiboot
mailing list