[Skiboot] [PATCH 13/15] lpc: Add routing support for SerIRQs
Benjamin Herrenschmidt
benh at kernel.crashing.org
Tue Aug 9 16:38:17 AEST 2016
We can route them to any of 4 PSI interrupts. We use the device-tree
to determine the default routing
Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---
doc/device-tree/examples/power9-phb4.dts | 4 +-
hw/lpc.c | 105 +++++++++++++++++++++++++++----
include/lpc.h | 3 +
3 files changed, 99 insertions(+), 13 deletions(-)
diff --git a/doc/device-tree/examples/power9-phb4.dts b/doc/device-tree/examples/power9-phb4.dts
index a845a86..17453da 100644
--- a/doc/device-tree/examples/power9-phb4.dts
+++ b/doc/device-tree/examples/power9-phb4.dts
@@ -203,8 +203,8 @@
/* Route the LPC interrupts to one of the 4 supported
* PSI interrupt inputs [7...10].
*/
- interrupt-map = < 0 0 4 &PSI_X0 7
- 0 0 10 &PSI_X0 7>;
+ interrupt-map = < 0 0 4 &PSI_X0 8
+ 0 0 10 &PSI_X0 9>;
interrupt-map-mask = < 0 0 0xff >;
/*
diff --git a/hw/lpc.c b/hw/lpc.c
index 882bcf8..b252a54 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -25,6 +25,7 @@
#include <timebase.h>
#include <errorlog.h>
#include <opal-api.h>
+#include <psi.h>
//#define DBG_IRQ(fmt...) prerror(fmt)
#define DBG_IRQ(fmt...) do { } while(0)
@@ -110,6 +111,8 @@ DEFINE_LOG_ENTRY(OPAL_RC_LPC_SYNC, OPAL_PLATFORM_ERR_EVT, OPAL_LPC,
LPC_HC_IRQ_BM_TAR_ERR)
#define LPC_HC_ERROR_ADDRESS 0x40
+#define LPC_NUM_SERIRQ 17
+
struct lpcm {
uint32_t chip_id;
uint32_t xbase;
@@ -119,6 +122,7 @@ struct lpcm {
uint8_t fw_rdsz;
struct list_head clients;
bool has_serirq;
+ uint8_t sirq_routes[LPC_NUM_SERIRQ];
};
struct lpc_client_entry {
@@ -622,12 +626,17 @@ static void lpc_setup_serirq(struct lpcm *lpc)
}
}
-static void __unused lpc_route_serirq(struct lpcm *lpc, uint32_t sirq, uint32_t psi_idx)
+static void __lpc_route_serirq(struct lpcm *lpc, uint32_t sirq,
+ uint32_t psi_idx)
{
uint32_t reg, shift, val;
int64_t rc;
- assert(proc_gen == proc_gen_p9);
+ lpc->sirq_routes[sirq] = psi_idx;
+
+ /* We may not be ready yet ... */
+ if (!lpc->has_serirq)
+ return;
if (sirq < 14) {
reg = 0xc;
@@ -645,10 +654,30 @@ static void __unused lpc_route_serirq(struct lpcm *lpc, uint32_t sirq, uint32_t
opb_write(lpc, opb_master_reg_base + reg, val, 4);
}
+void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx)
+{
+ struct proc_chip *chip;
+ struct lpcm *lpc;
+
+ if (sirq >= LPC_NUM_SERIRQ) {
+ prerror("LPC[%03x]: Routing request for invalid SerIRQ %d\n",
+ chip_id, sirq);
+ return;
+ }
+
+ chip = get_chip(chip_id);
+ if (!chip || !chip->lpc)
+ return;
+ lpc = chip->lpc;
+ lock(&lpc->lock);
+ __lpc_route_serirq(lpc, sirq, psi_idx);
+ unlock(&lpc->lock);
+}
+
static void lpc_init_interrupts_one(struct proc_chip *chip)
{
struct lpcm *lpc = chip->lpc;
- int rc;
+ int i, rc;
lock(&lpc->lock);
@@ -674,14 +703,21 @@ static void lpc_init_interrupts_one(struct proc_chip *chip)
opb_write(lpc, 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
*/
lpc->has_serirq = true;
- lpc_setup_serirq(lpc);
+ lpc_setup_serirq(lpc);
+ break;
+ case PROC_CHIP_P9_NIMBUS:
+ case PROC_CHIP_P9_CUMULUS:
+ /* On P9, we additionall setup the routing */
+ lpc->has_serirq = true;
+ for (i = 0; i < LPC_NUM_SERIRQ; i++)
+ __lpc_route_serirq(lpc, i, lpc->sirq_routes[i]);
+ lpc_setup_serirq(lpc);
+ break;
default:
;
}
@@ -938,9 +974,47 @@ static void lpc_init_chip_p8(struct dt_node *xn)
chip->lpc = lpc;
}
+static void lpc_parse_interrupt_map(struct lpcm *lpc, struct dt_node *lpc_node)
+{
+ const u32 *imap;
+ size_t imap_size;
+
+ imap = dt_prop_get_def_size(lpc_node, "interrupt-map", NULL, &imap_size);
+ if (!imap)
+ return;
+ imap_size >>= 2;
+ if (imap_size % 5) {
+ prerror("LPC[%03x]: Odd format for LPC interrupt-map !\n",
+ lpc->chip_id);
+ return;
+ }
+
+ while(imap_size >= 5) {
+ uint32_t sirq = be32_to_cpu(imap[2]);
+ uint32_t pirq = be32_to_cpu(imap[4]);
+
+ if (sirq >= LPC_NUM_SERIRQ) {
+ prerror("LPC[%03x]: LPC irq %d out of range in"
+ " interrupt-map\n", lpc->chip_id, sirq);
+ } else if (pirq < P9_PSI_IRQ_LPC_SIRQ0 ||
+ pirq > P9_PSI_IRQ_LPC_SIRQ3) {
+ prerror("LPC[%03x]: PSI irq %d out of range in"
+ " interrupt-map\n", lpc->chip_id, pirq);
+ } else {
+ uint32_t pin = pirq - P9_PSI_IRQ_LPC_SIRQ0;
+ lpc->sirq_routes[sirq] = pin;
+ prlog(PR_INFO, "LPC[%03x]: SerIRQ %d routed to PSI input %d\n",
+ lpc->chip_id, sirq, pin);
+ }
+ imap += 5;
+ imap_size -= 5;
+ }
+}
+
static void lpc_init_chip_p9(struct dt_node *opb_node)
{
uint32_t gcid = dt_get_chip_id(opb_node);
+ struct dt_node *lpc_node;
struct proc_chip *chip;
struct lpcm *lpc;
u64 addr;
@@ -949,15 +1023,19 @@ static void lpc_init_chip_p9(struct dt_node *opb_node)
chip = get_chip(gcid);
assert(chip);
- lpc = zalloc(sizeof(struct lpcm));
- assert(lpc);
- lpc->chip_id = gcid;
-
/* Grab OPB base address */
addr = dt_prop_get_cell(opb_node, "ranges", 1);
addr <<= 32;
addr |= dt_prop_get_cell(opb_node, "ranges", 2);
+ /* Find the "lpc" child node */
+ lpc_node = dt_find_compatible_node(opb_node, NULL, "ibm,power9-lpc");
+ if (!lpc_node)
+ return;
+
+ lpc = zalloc(sizeof(struct lpcm));
+ assert(lpc);
+ lpc->chip_id = gcid;
lpc->mbase = (void *)addr;
lpc->fw_idsel = 0xff;
lpc->fw_rdsz = 0xff;
@@ -969,10 +1047,15 @@ static void lpc_init_chip_p9(struct dt_node *opb_node)
lpc_default_chip_id = gcid;
}
+ /* Parse interrupt map if any to setup initial routing */
+ lpc_parse_interrupt_map(lpc, lpc_node);
+
/* Mask all interrupts for now */
opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
- /* Default with routing to PSI SerIRQ 0 */
+ /* Default with routing to PSI SerIRQ 0, this will be updated
+ * later when interrupts are initialized.
+ */
opb_read(lpc, opb_master_reg_base + 8, &val, 4);
val &= 0xff03ffff;
opb_write(lpc, opb_master_reg_base + 8, val, 4);
diff --git a/include/lpc.h b/include/lpc.h
index f173145..59f0779 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -95,6 +95,9 @@ struct lpc_client {
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);
+
/* Default bus accessors */
extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz);
--
2.7.4
More information about the Skiboot
mailing list