[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