[PATCH linux dev-4.10 3/7] drivers/fsi: Scan for hub sourced IRQ's

Christopher Bostic cbostic at linux.vnet.ibm.com
Wed May 10 07:38:58 AEST 2017


Look for the hub link that may have sourced the IRQ being handled.
Clear out hub link interrupting conditions that are latched in
hardware after FSI client handler has been dispatched.

Signed-off-by: Christopher Bostic <cbostic at linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c       | 32 ++++++++++++---
 drivers/fsi/fsi-master-hub.c | 93 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/fsi/fsi-master.h     |  2 +
 3 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index ca3021f..d29af9a 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -54,6 +54,8 @@
 #define FSI_SSTAT		0x14	/* R  : Slave status */
 #define FSI_SI1M		0x18	/* R/W: IRQ mask */
 #define FSI_SI1S		0x1C	/* R  : IRQ status */
+#define FSI_SRSIC0		0x68	/* R/W: Hub IRQ condition 0 */
+#define FSI_SRSIC1		0x6C	/* R/W: Hub IRQ condition 1 */
 #define FSI_LLMODE		0x100	/* R/W: Link layer mode register */
 
 /*
@@ -573,6 +575,21 @@ static void fsi_slave_release(struct device *dev)
 	kfree(slave);
 }
 
+static int fsi_slave_irq_clear(struct fsi_slave *slave)
+{
+	uint32_t clear = ~0;
+	int rc;
+
+	rc = fsi_slave_write(slave, FSI_SLAVE_BASE + FSI_SRSIC0, &clear,
+				sizeof(clear));
+	if (rc) {
+		dev_dbg(&slave->dev, "Failed on write to SRSIC0\n");
+		return rc;
+	}
+	return fsi_slave_write(slave, FSI_SLAVE_BASE + FSI_SRSIC1, &clear,
+				sizeof(clear));
+}
+
 static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 {
 	uint32_t chip_id, llmode;
@@ -657,8 +674,11 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 	if (rc)
 		dev_warn(&slave->dev, "failed to create term attr: %d\n", rc);
 
-	fsi_slave_scan(slave);
-	return 0;
+	rc = fsi_slave_scan(slave);
+	if (rc)
+		return rc;
+
+	return fsi_slave_irq_clear(slave);
 }
 
 /* FSI master support */
@@ -774,6 +794,7 @@ static int __fsi_dev_irq(struct device *dev, void *data)
 {
 	uint32_t *si1s = data;
 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
+	int rc;
 
 	if (!fsi_dev || !si1s) {
 		dev_dbg(dev, "Invalid input: %p %p\n", fsi_dev, si1s);
@@ -782,8 +803,8 @@ static int __fsi_dev_irq(struct device *dev, void *data)
 
 	if (*si1s & (0x80000000 >> fsi_dev->si1s_bit) &&
 					fsi_dev->irq_handler) {
-		fsi_dev->irq_handler(0, &fsi_dev->dev);
-		return 1;
+		rc = fsi_dev->irq_handler(0, &fsi_dev->dev);
+		dev_dbg(dev, "IRQ handler exit: %d\n", rc);
 	}
 
 	return 0;
@@ -794,7 +815,7 @@ static int __fsi_slave_irq(struct device *dev, void *data)
 	return device_for_each_child(dev, data, __fsi_dev_irq);
 }
 
-static void fsi_master_irq(struct fsi_master *master, int link, uint32_t si1s)
+void fsi_master_irq(struct fsi_master *master, int link, uint32_t si1s)
 {
 	device_for_each_child(&master->dev, &si1s, __fsi_slave_irq);
 }
@@ -817,6 +838,7 @@ static int fsi_master_ipoll(void *data)
 		if (rc)
 			goto done;
 
+		si1s = be32_to_cpu(si1s);
 		if (si1s & master->ipoll)
 			fsi_master_irq(master, 0, si1s);
 done:
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 133b9bf..aebaa6b 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -20,6 +20,18 @@
 
 #include "fsi-master.h"
 
+/* Upstream Slave Hub Related Registers */
+#define FSI_SLAVE_BASE		0x800
+#define FSI_SI1S		0x1C		/* R:   IRQ status */
+#define FSI_SRSIC0		0x68		/* R/W: Hub IRQ condition 0 */
+#define FSI_SRSIC1		0x6C		/* R/W: Hub IRQ condition 1 */
+#define FSI_SRSIM0		0x70		/* R/W: Hub IRQ mask 0 */
+#define FSI_SRSIS0		0x78		/* R: Hub IRQ status 0 */
+
+/* SRSIS / SRSIM / SRSIC fields */
+#define FSI_SRSIX_IRQ1_MASK	0x00aaaaaa      /* SI1 IRQ sources */
+#define FSI_SRSIX_BITS_PER_LINK	8
+
 /* Control Registers */
 #define FSI_MMODE		0x0		/* R/W: mode */
 #define FSI_MDLYR		0x4		/* R/W: delay */
@@ -64,6 +76,7 @@
 #define FSI_MECTRL_P8_AUTO_TERM	0x4000		/* Auto terminate */
 
 #define FSI_ENGID_HUB_MASTER		0x1c
+#define FSI_HUB_UPSTREAM_SI1S_BIT	11
 #define FSI_HUB_LINK_OFFSET		0x80000
 #define FSI_HUB_LINK_SIZE		0x80000
 #define FSI_HUB_MASTER_MAX_LINKS	8
@@ -168,6 +181,83 @@ static inline u32 fsi_mmode_crs1(u32 x)
 	return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
 }
 
+/* TODO: Add support for hub links 4-7 */
+static int next_hub_irq_source(struct device *dev, uint32_t srsis)
+{
+	int index;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!(srsis & FSI_SRSIX_IRQ1_MASK)) {
+		dev_dbg(dev, "Unexpected IRQ source SRSIS:0x%08x\n",
+			srsis);
+		return -EINVAL;
+	}
+
+	/*
+	 * TODO: add a round robin scheduler to ensure we don't favor
+	 * lower hub link irq sources over others
+	 */
+	index = __clz(srsis);
+	dev_dbg(dev, "SRSIS:0x%08x index:%d\n", srsis, index);
+	return index / FSI_SRSIX_BITS_PER_LINK;
+}
+
+/*
+ * Interrupt handler for IRQs sourced from a hub FSI link
+ */
+int fsi_master_hub_irq_handler(int irq, void *device_data)
+{
+	int rc, hublink;
+	struct device *dev = device_data;
+	struct fsi_master_hub *hub = dev_get_drvdata(dev);
+	uint32_t srsis, si1s;
+
+	if (!hub)
+		return 1;
+
+	rc = fsi_slave_read(hub->upstream->slave, FSI_SLAVE_BASE + FSI_SRSIS0,
+			&srsis, sizeof(srsis));
+	if (rc) {
+		dev_dbg(dev, "Hub IRQ: Failed to read SRSIS\n");
+		return rc;
+	}
+	srsis = be32_to_cpu(srsis);
+	if (srsis) {
+		hublink = next_hub_irq_source(dev, srsis);
+		if (hublink < 0)
+			return 1;
+
+		if (!(&hub->master.dev))
+			return 0;
+
+		rc = hub_master_read(&hub->master, hublink, 0,
+				FSI_SLAVE_BASE + FSI_SI1S, &si1s,
+				sizeof(si1s));
+		if (rc) {
+			dev_dbg(dev,
+				"Hub IRQ: Failed to read hub slave si1s\n");
+			return 1;
+		}
+		si1s = be32_to_cpu(si1s);
+		fsi_master_irq(&hub->master, hublink, si1s);
+
+		/* Clear the interrupting condition */
+		srsis = 0xff000000 >> (hublink * FSI_SRSIX_BITS_PER_LINK);
+		srsis = be32_to_cpu(srsis);
+		rc =  fsi_slave_write(hub->upstream->slave,
+					FSI_SLAVE_BASE + FSI_SRSIC0,
+					&srsis, sizeof(srsis));
+		if (rc)
+			dev_dbg(dev,
+				"Hub IRQ: Failed on write to clear out SRSIC\n");
+	} else
+		dev_dbg(dev, "Hub IRQ: No SRSIS bits asserted\n");
+
+	return 1;
+}
+
 static int hub_master_init(struct fsi_master_hub *hub)
 {
 	struct fsi_device *dev = hub->upstream;
@@ -281,6 +371,9 @@ static int hub_master_probe(struct device *dev)
 	hub->master.send_break = hub_master_break;
 	hub->master.link_enable = hub_master_link_enable;
 
+	/* Override the parent slave's setting for si1s */
+	fsi_dev->si1s_bit = FSI_HUB_UPSTREAM_SI1S_BIT;
+	fsi_dev->irq_handler = fsi_master_hub_irq_handler;
 	dev_set_drvdata(dev, hub);
 
 	hub_master_init(hub);
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 668ab0c..3334558 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -41,6 +41,8 @@ struct fsi_master {
 extern int fsi_master_register(struct fsi_master *master);
 extern void fsi_master_unregister(struct fsi_master *master);
 
+void fsi_master_irq(struct fsi_master *master, int link, uint32_t si1s);
+
 /**
  * crc4 helper: Given a starting crc4 state @c, calculate the crc4 vaue of @x,
  * which is @bits in length. This may be required by master implementations
-- 
1.8.2.2



More information about the openbmc mailing list