[Skiboot] [PATCH 22/28] lpc: Add P9 LPC interrupts support
Benjamin Herrenschmidt
benh at kernel.crashing.org
Thu Jul 7 11:50:13 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 | 111 ++++++++++++++++++++++++++-----
hw/psi.c | 29 ++++----
include/lpc.h | 7 ++
include/psi.h | 2 -
platforms/astbmc/common.c | 1 +
7 files changed, 127 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..65ac4d6 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,18 @@ 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",
}
void lpc_init(void)
@@ -914,7 +991,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