[PATCH linux 3/4] drivers/fsi: Add link presence detect functionality

christopher.lee.bostic at gmail.com christopher.lee.bostic at gmail.com
Fri Jul 29 04:14:57 AEST 2016


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

Start scanning for CFAM presence on available links.

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

diff --git a/drivers/fsi/fsimaster.c b/drivers/fsi/fsimaster.c
index 96f6f60..0827f0b 100644
--- a/drivers/fsi/fsimaster.c
+++ b/drivers/fsi/fsimaster.c
@@ -22,9 +22,28 @@
 
 extern struct primaster prim;
 
+static int fsi_nextbit(u32 *new, int words)
+{
+	int bit, i;
+	u32 mask;
+
+	for (i = 0; i < words; i++) {
+		mask = 0x80000000;
+		for (bit = 0; bit < BITS_PER_LONG; bit++) {
+			if( new[bit] & mask )
+				return bit + i * BITS_PER_LONG;
+		}
+	}
+	return -1;
+}
+
+u32 fsim_cfam2pa(struct fsimaster *p, int link, int cfam)
+{
+	return 0;
+}
+
 static int hpinfo_alloc(struct fsimaster *p)
 {
-	/* Stub */
 	return 0;
 }
 
@@ -179,6 +198,20 @@ struct fsimaster * fsim_get_top_master(struct fsimaster *p)
 	return parent;
 }
 
+/*
+ * Work queue function to probe for links
+ */
+static void probe_wq(struct work_struct *nix)
+{
+}
+
+/*
+ * Work queue function to build up links
+ */
+static void build_wq(struct work_struct *nix)
+{
+}
+
 static int fsimaster_reset(struct fsimaster *p)
 {
 	u32 mresp, maeb, mver, mectrl, mmode, menp[2];
@@ -251,6 +284,10 @@ struct fsimaster *fsimaster_build_init(struct fsimaster *p, int type,
 	}
 	p->type = type;
 	p->fsidev = parent;
+	init_timer(&p->hotp.mytimer);
+	init_completion(&p->hotp.i_am_dead);
+	INIT_WORK(&p->hotp.probewrk, probe_wq);
+	INIT_WORK(&p->hotp.buildwrk, build_wq);
 	if( fsimaster_init(p)) {
 		p = 0;
 		goto out;
@@ -264,9 +301,124 @@ out:
 	return p ? : ERR_PTR(rc);
 }
 
+static void plugadd_link(struct fsimaster *p, struct hp_info *hp)
+{
+	u32 addr;
+
+	hp->ec = 0;
+	atomic_set(&hp->state, FSI_LINK_INPROBE);
+	set_bit(hp->linkno, p->hotp.probing);
+	addr = fsi_mtype2pa(fsim_cfam2pa(p, hp->linkno, 0), p->type);
+}
+
+static void plugadd(struct fsimaster *p, u32 *diff)
+{
+	int linkno;
+	struct fsidd *dd = to_fsidd_prim(fsim_get_top_master(p));
+
+	set_bit(FSIDD_PROBE, &dd->state);
+	while ((linkno = fsi_nextbit(diff, 2)) >= 0) {
+		if (linkno >= p->maxlinks)
+			break;
+		plugadd_link(p, p->hotp.plug[linkno]);
+	}
+}
+
+static void plugdel(struct fsimaster *p, u32 *diff)
+{
+}
+
+/*
+ * Look for CFAMs plugged into any of the available link slots
+ */
+int fsim_staticplug_nl(struct fsimaster *p, u32 *menp, u32 *mlevp)
+{
+	int rc = 0;
+
+	rc = (*p->read_f2)(p->mp, FSI_N_MENP0, menp);
+	if (rc)
+		goto done;
+	rc = (*p->read_f2)(p->mp, FSI_N_MLEVP0, mlevp);
+
+done:
+	return rc;
+}
+
+int fsim_staticplug(struct fsimaster *p, u32 *menp, u32 *mlevp)
+{
+	int rc = 0;
+	unsigned long msr = 0;
+
+	spin_lock_irqsave(&p->lock, msr);
+	rc = fsim_staticplug_nl(p, menp, mlevp);
+	spin_unlock_irqrestore(&p->lock, msr);
+
+	return rc;
+}
+
+/*
+ * Periodically called function to check for static plug changes
+ */
+static void plugmgr(unsigned long para)
+{
+	struct fsimaster *p = (struct fsimaster *)para;
+	struct fsidd *dd;
+	int rc;
+	u32 mlevp[2], menp[2], delta[2], new[2], gone[2];
+
+	rc = fsim_staticplug(p, menp, mlevp);
+	if (rc)
+		goto done;
+
+	dd = to_fsidd_prim(fsim_get_top_master(p));
+
+	mlevp[0] |= menp[0];
+	mlevp[1] |= menp[1];
+	delta[0] = p->hotp.mlevp_last[0] ^ mlevp[0];
+	delta[1] = p->hotp.mlevp_last[1] ^ mlevp[1];
+	new[0] = delta[0] & mlevp[0];
+	new[1] = delta[1] & mlevp[1];
+	gone[0] = delta[0] & p->hotp.mlevp_last[0];
+	gone[1] = delta[1] & p->hotp.mlevp_last[1];
+	p->hotp.mlevp_last[0] = mlevp[0];
+	p->hotp.mlevp_last[1] = mlevp[1];
+
+	if (gone[0] || gone[1])
+		plugdel(p, gone);
+	if (new[0] || new[1])
+		plugadd(p, new);
+
+	queue_work(dd->hotp_wq, &p->hotp.probewrk);
+	if (p->hotp.building[0] == 0 && p->hotp.building[1] == 0)
+		clear_bit(FSIDD_PROBE, &dd->state);
+	if (p->hotp.building[0] || p->hotp.building[1])
+		queue_work(dd->hotp_wq, &p->hotp.buildwrk);
+
+	mod_timer(&p->hotp.mytimer,
+		  jiffies + msecs_to_jiffies(FSI_DFLT_PLUG_CHECK));
+done:
+	return;
+}
+
 /*
  * Kick off the master so it can start probing for attached CFAMs
  */
 void fsimaster_start(struct fsimaster *p)
 {
+	struct fsi_mreg *regs = &(prim.regs);
+
+	memset(regs, 0, sizeof(struct fsi_mreg));
+	p->mp = (u32 *)regs;
+
+	/*
+	 * TODO: Implement presence detect via I/O
+	 * For now we'll define the default as link 0 as present
+	 */
+	regs->mlevp0[0] = 0x80000000;
+
+	/* Kick off the presence detect polling routine */
+	p->hotp.mytimer.function = plugmgr;
+	p->hotp.mytimer.data = (unsigned long)p;
+	p->hotp.mytimer.expires = jiffies + msecs_to_jiffies(FSI_DFLT_PLUG_CHECK);
+	add_timer(&p->hotp.mytimer);
 }
diff --git a/drivers/fsi/fsimaster.h b/drivers/fsi/fsimaster.h
index b07934e..40f4f4c 100644
--- a/drivers/fsi/fsimaster.h
+++ b/drivers/fsi/fsimaster.h
@@ -21,6 +21,16 @@
 #define	FSI_DFLT_PLUG_CHECK	100
 #define FSI_DFLT_IPOLL_CHECK	800
 
+/* Link states */
+#define FSI_LINK_FREE		1	/* Nothing plugged */
+#define FSI_LINK_INPROBE	2	/* Link probed */
+#define FSI_LINK_INPOLL		3	/* I-Poll test */
+#define FSI_LINK_INBUILD	4	/* Building */
+#define FSI_LINK_RUNNING	5	/* Up and functional */
+#define FSI_LINK_INLOST		6	/* Link dropped */
+#define FSI_LINK_DEAD		7	/* No longer useable */
+#define FSI_LINK_WAITFOR	8	/* Notify on completion */
+
 /* FSI master register numbers */
 #define	FSI_N_MMODE	0	/* 0x0   R/W: mode register */
 #define	FSI_N_MDLYR	1	/* 0x4   R/W: delay register */
@@ -522,6 +532,26 @@ static inline unsigned long get_termpa(unsigned long slv_pa)
 	return slv_pa & ~0xfff;
 }
 
+struct hp_info {			/* Hot plug information */
+	struct completion done;		/* Link build done */
+	unsigned short tries;		/* # of tries before probing */
+	unsigned char linkno;		/* Link # */
+	atomic_t state;			/* State of this entry */
+	int ec;				/* Error code */
+	unsigned long state_w;		/* Wait state */
+};
+
+struct hotplug {			/* Hot Plug work structure */
+	u32 mlevp_last[2];		/* Last known plug state */
+	unsigned long building[2];	/* Bit mask of links to build up */
+	unsigned long probing[2];	/* Bit mask of links to  probe */
+	struct timer_list mytimer;	/* For periodic plug check */
+	struct work_struct probewrk;	/* Probe worker */
+	struct work_struct buildwrk;	/* Build worker */
+	struct completion i_am_dead;	/* Wait for workw to finish */
+	struct hp_info *plug[FSI_MAX_LINKS];	/* Data to work on */
+};
+
 struct master_quirks {
 	int break_cfam_id;
 	void (*port_reset)(struct fsidevice *, struct fsidevice *, int);
@@ -546,6 +576,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;		/* Hot plug link information */
 	int (*write_f)(volatile u32 *, int, u32);	/* Write function */
 	int (*read_f)(volatile u32 *, int, u32 *);	/* Read function */
 	int (*write_f2)(volatile u32 *, int, u32 *);	/* Write function */
@@ -624,7 +655,7 @@ unsigned long fsi_matrb_addr(unsigned long);
 /*
  * Helper utilities for link/cfam calculations.
  */
-phys_addr_t fsim_cfam2pa(struct fsimaster *, int, int);
+u32 fsim_cfam2pa(struct fsimaster *, int, int);
 unsigned long fsim_linksz(struct fsimaster *);
 unsigned long fsim_cfamsz(struct fsimaster *);
 
-- 
1.8.2.2



More information about the openbmc mailing list