[PATCH] drivers/fsi: Add slave interrupt polling

Christopher Bostic cbostic at linux.vnet.ibm.com
Thu Feb 2 09:22:01 AEDT 2017


Scan slaves present for asserting interrupt signals in the si1s
register and call a registered client's interrupt handler as
appropriate.

Signed-off-by: Eddie James <eajames at us.ibm.com>
Signed-off-by: Christopher Bostic <cbostic at linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c        | 99 +++++++++++++++++++++++++++++++++++++++++++
 drivers/fsi/fsi-master-gpio.c |  1 +
 drivers/fsi/fsi-master.h      |  2 +
 include/linux/fsi.h           |  5 +++
 4 files changed, 107 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 72e0ca3..4df7218 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -15,11 +15,15 @@
 
 #include <linux/device.h>
 #include <linux/fsi.h>
+#include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/jiffies.h>
 
 #include "fsi-master.h"
 
+#define DEBUG
+
 #define FSI_N_SLAVES	4
 
 #define FSI_SLAVE_CONF_NEXT_MASK	0x80000000
@@ -36,7 +40,11 @@
 #define FSI_PEEK_BASE			0x410
 #define	FSI_SLAVE_BASE			0x800
 
+#define FSI_IPOLL_PERIOD		msecs_to_jiffies(fsi_ipoll_period_ms)
+
 static const int engine_page_size = 0x400;
+static struct task_struct *master_ipoll;
+static unsigned int fsi_ipoll_period_ms = 100;
 
 static atomic_t master_idx = ATOMIC_INIT(-1);
 
@@ -60,6 +68,8 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
  * FSI slave engine control register offsets
  */
 #define	FSI_SMODE		0x0	/* R/W: Mode register */
+#define FSI_SI1M		0x18	/* R/W: IRQ mask */
+#define FSI_SI1S		0x1C	/* R: IRQ status */
 
 /*
  * SMODE fields
@@ -197,6 +207,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 	uint32_t engine_addr;
 	uint32_t conf;
 	int rc, i;
+	uint8_t si1s_bit = 1;
 
 	INIT_LIST_HEAD(&slave->my_engines);
 
@@ -253,6 +264,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 			dev->unit = i;
 			dev->addr = engine_addr;
 			dev->size = slots * engine_page_size;
+			dev->si1s_bit = si1s_bit++;
 
 			dev_info(&slave->dev,
 			"engine[%i]: type %x, version %x, addr %x size %x\n",
@@ -507,6 +519,56 @@ static void fsi_master_unscan(struct fsi_master *master)
 	master->slave_list = false;
 }
 
+static void fsi_master_irq(struct fsi_master *master, int link, uint32_t si1s)
+{
+	struct fsi_slave *slave;
+	struct fsi_device *fsi_dev;
+
+	if (list_empty(&master->my_slaves))
+		return;
+
+	slave = list_first_entry(&master->my_slaves, struct fsi_slave,
+				list_link);
+
+	list_for_each_entry(fsi_dev, &slave->my_engines, link) {
+		if (si1s & (0x80000000 >> fsi_dev->si1s_bit) &&
+		    fsi_dev->irq_handler)
+			fsi_dev->irq_handler(0, &fsi_dev->dev);
+	}
+}
+
+static int fsi_master_ipoll(void *data)
+{
+	int rc;
+	uint32_t si1s;
+	unsigned long elapsed = 0;
+	unsigned long previous_jiffies = jiffies;
+	struct fsi_master *master = data;
+
+	while (!kthread_should_stop()) {
+		if (!master->ipoll)
+			goto done;
+
+		/* Ignore errors for now */
+		rc = master->read(master, 0, 0, FSI_SLAVE_BASE + FSI_SI1S,
+				  &si1s, sizeof(uint32_t));
+		if (rc)
+			goto done;
+
+		if (si1s & master->ipoll)
+			fsi_master_irq(master, 0, si1s);
+done:
+		elapsed = jiffies - previous_jiffies;
+		if (elapsed < FSI_IPOLL_PERIOD) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(FSI_IPOLL_PERIOD - elapsed);
+		}
+		previous_jiffies = jiffies;
+	}
+
+	return 0;
+}
+
 int fsi_master_register(struct fsi_master *master)
 {
 	if (!master || !master->dev)
@@ -524,9 +586,36 @@ void fsi_master_unregister(struct fsi_master *master)
 {
 	fsi_master_unscan(master);
 	put_device(master->dev);
+	if (master_ipoll) {
+		kthread_stop(master_ipoll);
+		master_ipoll = NULL;
+	}
 }
 EXPORT_SYMBOL_GPL(fsi_master_unregister);
 
+/*
+ * TODO: move this to master->start_ipoll( ) -each master may have its
+ * own way of doing this
+ */
+int fsi_master_start_ipoll(struct fsi_master *master)
+{
+	if (master_ipoll) {
+		dev_err(master->dev, "Already polling for irqs\n");
+		return -EALREADY;
+	}
+	master_ipoll = kthread_create(fsi_master_ipoll, master,
+				"fsi_master_ipoll");
+	if (IS_ERR(master_ipoll)) {
+		dev_err(master->dev, "Couldn't create ipoll thread rc:%d\n",
+			(int)PTR_ERR(master_ipoll));
+		return PTR_ERR(master_ipoll);
+	}
+	wake_up_process(master_ipoll);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_master_start_ipoll);
+
 /* FSI core & Linux bus type definitions */
 
 static int fsi_bus_match(struct device *dev, struct device_driver *drv)
@@ -566,6 +655,16 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv)
 }
 EXPORT_SYMBOL_GPL(fsi_driver_unregister);
 
+int fsi_enable_irq(struct fsi_device *dev)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_enable_irq);
+
+void fsi_disable_irq(struct fsi_device *dev)
+{
+}
+
 struct bus_type fsi_bus_type = {
 	.name		= "fsi",
 	.match		= fsi_bus_match,
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 83c7cf0..73d9985 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -457,6 +457,7 @@ static ssize_t store_scan(struct device *dev,
 	/* clear out any old scan data if present */
 	fsi_master_unregister(&master->master);
 	fsi_master_register(&master->master);
+	fsi_master_start_ipoll(&master->master);
 
 	return count;
 }
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 454af2b..3737404 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -25,6 +25,7 @@ struct fsi_master {
 	struct device	*dev;
 	int		idx;
 	int		n_links;
+	uint32_t	ipoll;
 	int		(*read)(struct fsi_master *, int link,
 				uint8_t slave, uint32_t addr,
 				void *val, size_t size);
@@ -37,6 +38,7 @@ struct fsi_master {
 
 extern int fsi_master_register(struct fsi_master *master);
 extern void fsi_master_unregister(struct fsi_master *master);
+extern int fsi_master_start_ipoll(struct fsi_master *master);
 
 /**
  * crc4 helper: Given a starting crc4 state @c, calculate the crc4 vaue of @x,
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 2721255..d22d0c5 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -23,9 +23,11 @@ struct fsi_device {
 	u8			engine_type;
 	u8			version;
 	u8			unit;
+	u8			si1s_bit;
 	struct fsi_slave	*slave;
 	uint32_t		addr;
 	uint32_t		size;
+	int (*irq_handler)(int, void *);
 };
 
 extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
@@ -69,4 +71,7 @@ extern void fsi_driver_unregister(struct fsi_driver *);
 
 extern struct bus_type fsi_bus_type;
 
+extern int fsi_enable_irq(struct fsi_device *dev);
+extern void fsi_disable_irq(struct fsi_device *dev);
+
 #endif /* LINUX_FSI_H */
-- 
1.8.2.2



More information about the openbmc mailing list