[PATCH linux v5 3/7] drivers/fsi: Add FSI master target device scanning function

christopher.lee.bostic at gmail.com christopher.lee.bostic at gmail.com
Thu Aug 25 05:54:08 AEST 2016


From: Chris Bostic <cbostic at us.ibm.com>

Initialize and start the periodic target device presence detect function.
Add FSI master control register accessors to allow check of physical target
device plug state.  Any target devices will show as present with a '1' in
the FSI master control MLEVP register.

Signed-off-by: Chris Bostic <cbostic at us.ibm.com>
---
 drivers/fsi/fsimaster.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/fsi/fsimaster.h |  21 +++++++++
 2 files changed, 140 insertions(+)

diff --git a/drivers/fsi/fsimaster.c b/drivers/fsi/fsimaster.c
index b5d16db..8054051 100644
--- a/drivers/fsi/fsimaster.c
+++ b/drivers/fsi/fsimaster.c
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/ktime.h>
 #include <linux/io.h>
+#include <linux/bitops.h>
 #include "fsi.h"
 #include "fsiinit.h"
 #include "fsimaster.h"
@@ -161,6 +162,20 @@ struct fsimaster *fsimaster_get_top_master(struct fsimaster *master)
 	return parent;
 }
 
+/*
+ * Work queue function to probe links
+ */
+static void probe_wq(struct work_struct *work)
+{
+}
+
+/*
+ * Work queue function to build up links
+ */
+static void build_wq(struct work_struct *work)
+{
+}
+
 static int fsimaster_reset(struct fsimaster *master)
 {
 	u32 reg = 0;
@@ -221,6 +236,10 @@ struct fsimaster *fsimaster_build_init(struct fsimaster *master, int type,
 	}
 	master->type = type;
 	master->fsidev = parent;
+	init_timer(&master->hotp.mytimer);
+	init_completion(&master->hotp.link_dead);
+	INIT_WORK(&master->hotp.probework, probe_wq);
+	INIT_WORK(&master->hotp.buildwork, build_wq);
 	if (fsimaster_init(master)) {
 		master = NULL;
 		goto out;
@@ -234,9 +253,109 @@ out:
 	return master ? : ERR_PTR(rc);
 }
 
+static void plugadd_link(struct fsimaster *master, struct fsi_hotplug *hp)
+{
+	hp->error_code = 0;
+	atomic_set(&hp->state, FSI_LINK_PROBE);
+	set_bit(hp->linkno, &master->hotp.probing);
+}
+
+static void plugadd(struct fsimaster *master, u64 *now_avail)
+{
+	int linkno;
+	struct fsidd *dd = to_fsidd_prim(fsimaster_get_top_master(master));
+
+	set_bit(FSI_LINK_PROBE, &dd->state);
+	while ((linkno = ffs(*now_avail)) >= 0) {
+		if (linkno >= master->maxlinks)
+			break;
+		plugadd_link(master, master->hotp.plug[linkno]);
+	}
+}
+
+static void plugdel(struct fsimaster *master, u64 *missing)
+{
+}
+
+/*
+ * Read in data on CFAM plug states
+ * nl: Not wrapped in a spin lock for those situations that don't require it
+ */
+void fsimaster_staticplug_nl(struct fsimaster *master, u64 *menp, u64 *mlevp)
+{
+	master->read_reg2(master->base, FSI_N_MENP, menp);
+	master->read_reg2(master->base, FSI_N_MLEVP, mlevp);
+}
+
+void fsimaster_staticplug(struct fsimaster *master, u64 *menp, u64 *mlevp)
+{
+	unsigned long msr = 0;
+
+	spin_lock_irqsave(&master->lock, msr);
+	fsimaster_staticplug_nl(master, menp, mlevp);
+	spin_unlock_irqrestore(&master->lock, msr);
+}
+
+/*
+ * Periodically called to check for static plug changes
+ */
+static void plugmgr(unsigned long para)
+{
+	struct fsimaster *master = (struct fsimaster *)para;
+	struct fsidd *dd;
+	u64 curr_plug_state, curr_enable_state, changed, now_avail, missing;
+
+	fsimaster_staticplug(master, &curr_enable_state, &curr_plug_state);
+	dd = to_fsidd_prim(fsimaster_get_top_master(master));
+
+	/* What is currently plugged and in use */
+	curr_plug_state |= curr_enable_state;
+
+	/* What plug states have changed since last checked */
+	changed = master->hotp.mlevp_last ^ curr_plug_state;
+
+	/* Slots where something has shown up since last checked */
+	now_avail = changed & curr_plug_state;
+
+	/* Slots where something disappeared since last checked */
+	missing = changed & master->hotp.mlevp_last;
+
+	/* Save off current plug state for next pass */
+	master->hotp.mlevp_last = curr_plug_state;
+
+	if (missing)
+		plugdel(master, &missing);
+	if (now_avail)
+		plugadd(master, &now_avail);
+
+	/* We have links to probe based on newest plug state data */
+	queue_work(dd->hotp_wq, &master->hotp.probework);
+	if (master->hotp.building == 0)
+		clear_bit(FSI_LINK_PROBE, &dd->state);
+
+	/* Probe has finished so can now proceed with link scan, aka 'build' */
+	if (master->hotp.building)
+		queue_work(dd->hotp_wq, &master->hotp.buildwork);
+
+	mod_timer(&master->hotp.mytimer,
+		  jiffies + msecs_to_jiffies(FSI_PLUG_CHECK_TIME));
+}
+
 /*
  * Kick off the master so it can start probing for attached CFAMs
  */
 void fsimaster_start(struct fsimaster *master)
 {
+	/*
+	 * TODO: Implement presence detect via I/O
+	 * For now we'll define the default as only link 0 present
+	 */
+	master->base->mlevp = 0x8000000000000000ULL;
+
+	/* Kick off the presence detect polling routine */
+	master->hotp.mytimer.function = plugmgr;
+	master->hotp.mytimer.data = (unsigned long)master;
+	master->hotp.mytimer.expires = jiffies +
+					msecs_to_jiffies(FSI_PLUG_CHECK_TIME);
+	add_timer(&master->hotp.mytimer);
 }
diff --git a/drivers/fsi/fsimaster.h b/drivers/fsi/fsimaster.h
index 4df2caf..61e8c8a 100644
--- a/drivers/fsi/fsimaster.h
+++ b/drivers/fsi/fsimaster.h
@@ -520,6 +520,26 @@ static inline unsigned long get_termpa(unsigned long slv_pa)
 	return slv_pa & ~0xfff;
 }
 
+struct fsi_hotplug {			/* Hot plug information */
+	struct completion done;		/* Link build done */
+	u16 tries;			/* # of tries before probing */
+	u8 linkno;			/* Link # */
+	atomic_t state;			/* State of this entry */
+	int error_code;			/* Error code */
+	unsigned long wait_state;	/* Wait state */
+};
+
+struct hotplug {			/* Hot Plug work */
+	u64 mlevp_last;			/* Last known plug state */
+	unsigned long building;		/* Bit mask of links to build up */
+	unsigned long probing;		/* Bit mask of links to probe */
+	struct timer_list mytimer;	/* For plug check period */
+	struct work_struct probework;	/* Probe worker */
+	struct work_struct buildwork;	/* Build worker */
+	struct completion link_dead;	/* Wait for workw to finish */
+	struct fsi_hotplug *plug[FSI_MAX_LINKS];	/* Data to work on */
+};
+
 struct master_quirks {
 	int break_cfam_id;
 	void (*port_reset)(struct fsidevice *, struct fsidevice *, int);
@@ -544,6 +564,7 @@ struct fsimaster {			/* FSI master definition */
 	unsigned char hw_version;	/* FSI master hardware version */
 	unsigned char maxlinks;		/* FSI master links */
 	struct fsilink *link[FSI_MAX_LINKS];
+	struct hotplug hotp;		/* CFAM plug status information */
 	void (*write_reg)(struct fsi_mreg *, int, u32);	/* Write 32 bit word */
 	u32 (*read_reg)(struct fsi_mreg *, int);	/* Read 32 bit word */
 	void (*write_reg2)(struct fsi_mreg *, int, u64 *);	/* 64 bit */
-- 
1.8.2.2



More information about the openbmc mailing list