[SCSI] bfa: Added support to obtain SFP info.
- Added SFP sub-module to BFA.
- Added interface to collect sfp media info and sfp speed.
Signed-off-by: Krishna Gudipati <kgudipat@brocade.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index bfe3a87..d579036 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -3364,3 +3364,460 @@
return BFA_STATUS_OK;
}
+
+/*
+ * SFP module specific
+ */
+
+/* forward declarations */
+static void bfa_sfp_getdata_send(struct bfa_sfp_s *sfp);
+static void bfa_sfp_media_get(struct bfa_sfp_s *sfp);
+static bfa_status_t bfa_sfp_speed_valid(struct bfa_sfp_s *sfp,
+ enum bfa_port_speed portspeed);
+
+static void
+bfa_cb_sfp_show(struct bfa_sfp_s *sfp)
+{
+ bfa_trc(sfp, sfp->lock);
+ if (sfp->cbfn)
+ sfp->cbfn(sfp->cbarg, sfp->status);
+ sfp->lock = 0;
+ sfp->cbfn = NULL;
+}
+
+static void
+bfa_cb_sfp_state_query(struct bfa_sfp_s *sfp)
+{
+ bfa_trc(sfp, sfp->portspeed);
+ if (sfp->media) {
+ bfa_sfp_media_get(sfp);
+ if (sfp->state_query_cbfn)
+ sfp->state_query_cbfn(sfp->state_query_cbarg,
+ sfp->status);
+ sfp->media = NULL;
+ }
+
+ if (sfp->portspeed) {
+ sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
+ if (sfp->state_query_cbfn)
+ sfp->state_query_cbfn(sfp->state_query_cbarg,
+ sfp->status);
+ sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+ }
+
+ sfp->state_query_lock = 0;
+ sfp->state_query_cbfn = NULL;
+}
+
+/*
+ * IOC event handler.
+ */
+static void
+bfa_sfp_notify(void *sfp_arg, enum bfa_ioc_event_e event)
+{
+ struct bfa_sfp_s *sfp = sfp_arg;
+
+ bfa_trc(sfp, event);
+ bfa_trc(sfp, sfp->lock);
+ bfa_trc(sfp, sfp->state_query_lock);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (sfp->lock) {
+ sfp->status = BFA_STATUS_IOC_FAILURE;
+ bfa_cb_sfp_show(sfp);
+ }
+
+ if (sfp->state_query_lock) {
+ sfp->status = BFA_STATUS_IOC_FAILURE;
+ bfa_cb_sfp_state_query(sfp);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * SFP get data send
+ */
+static void
+bfa_sfp_getdata_send(struct bfa_sfp_s *sfp)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ bfa_trc(sfp, req->memtype);
+
+ /* build host command */
+ bfi_h2i_set(req->mh, BFI_MC_SFP, BFI_SFP_H2I_SHOW,
+ bfa_ioc_portid(sfp->ioc));
+
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(sfp->ioc, &sfp->mbcmd);
+}
+
+/*
+ * SFP is valid, read sfp data
+ */
+static void
+bfa_sfp_getdata(struct bfa_sfp_s *sfp, enum bfi_sfp_mem_e memtype)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ WARN_ON(sfp->lock != 0);
+ bfa_trc(sfp, sfp->state);
+
+ sfp->lock = 1;
+ sfp->memtype = memtype;
+ req->memtype = memtype;
+
+ /* Setup SG list */
+ bfa_alen_set(&req->alen, sizeof(struct sfp_mem_s), sfp->dbuf_pa);
+
+ bfa_sfp_getdata_send(sfp);
+}
+
+/*
+ * SFP show complete
+ */
+static void
+bfa_sfp_show_comp(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg)
+{
+ struct bfi_sfp_rsp_s *rsp = (struct bfi_sfp_rsp_s *) msg;
+
+ if (!sfp->lock) {
+ /*
+ * receiving response after ioc failure
+ */
+ bfa_trc(sfp, sfp->lock);
+ return;
+ }
+
+ bfa_trc(sfp, rsp->status);
+ if (rsp->status == BFA_STATUS_OK) {
+ sfp->data_valid = 1;
+ if (sfp->state == BFA_SFP_STATE_VALID)
+ sfp->status = BFA_STATUS_OK;
+ else if (sfp->state == BFA_SFP_STATE_UNSUPPORT)
+ sfp->status = BFA_STATUS_SFP_UNSUPP;
+ else
+ bfa_trc(sfp, sfp->state);
+ } else {
+ sfp->data_valid = 0;
+ sfp->status = rsp->status;
+ /* sfpshow shouldn't change sfp state */
+ }
+
+ bfa_trc(sfp, sfp->memtype);
+ if (sfp->memtype == BFI_SFP_MEM_DIAGEXT) {
+ bfa_trc(sfp, sfp->data_valid);
+ if (sfp->data_valid) {
+ u32 size = sizeof(struct sfp_mem_s);
+ u8 *des = (u8 *) &(sfp->sfpmem->srlid_base);
+ memcpy(des, sfp->dbuf_kva, size);
+ }
+ /*
+ * Queue completion callback.
+ */
+ bfa_cb_sfp_show(sfp);
+ } else
+ sfp->lock = 0;
+
+ bfa_trc(sfp, sfp->state_query_lock);
+ if (sfp->state_query_lock) {
+ sfp->state = rsp->state;
+ /* Complete callback */
+ bfa_cb_sfp_state_query(sfp);
+ }
+}
+
+/*
+ * SFP query fw sfp state
+ */
+static void
+bfa_sfp_state_query(struct bfa_sfp_s *sfp)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ /* Should not be doing query if not in _INIT state */
+ WARN_ON(sfp->state != BFA_SFP_STATE_INIT);
+ WARN_ON(sfp->state_query_lock != 0);
+ bfa_trc(sfp, sfp->state);
+
+ sfp->state_query_lock = 1;
+ req->memtype = 0;
+
+ if (!sfp->lock)
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+}
+
+static void
+bfa_sfp_media_get(struct bfa_sfp_s *sfp)
+{
+ enum bfa_defs_sfp_media_e *media = sfp->media;
+
+ *media = BFA_SFP_MEDIA_UNKNOWN;
+
+ if (sfp->state == BFA_SFP_STATE_UNSUPPORT)
+ *media = BFA_SFP_MEDIA_UNSUPPORT;
+ else if (sfp->state == BFA_SFP_STATE_VALID) {
+ union sfp_xcvr_e10g_code_u e10g;
+ struct sfp_mem_s *sfpmem = (struct sfp_mem_s *)sfp->dbuf_kva;
+ u16 xmtr_tech = (sfpmem->srlid_base.xcvr[4] & 0x3) << 7 |
+ (sfpmem->srlid_base.xcvr[5] >> 1);
+
+ e10g.b = sfpmem->srlid_base.xcvr[0];
+ bfa_trc(sfp, e10g.b);
+ bfa_trc(sfp, xmtr_tech);
+ /* check fc transmitter tech */
+ if ((xmtr_tech & SFP_XMTR_TECH_CU) ||
+ (xmtr_tech & SFP_XMTR_TECH_CP) ||
+ (xmtr_tech & SFP_XMTR_TECH_CA))
+ *media = BFA_SFP_MEDIA_CU;
+ else if ((xmtr_tech & SFP_XMTR_TECH_EL_INTRA) ||
+ (xmtr_tech & SFP_XMTR_TECH_EL_INTER))
+ *media = BFA_SFP_MEDIA_EL;
+ else if ((xmtr_tech & SFP_XMTR_TECH_LL) ||
+ (xmtr_tech & SFP_XMTR_TECH_LC))
+ *media = BFA_SFP_MEDIA_LW;
+ else if ((xmtr_tech & SFP_XMTR_TECH_SL) ||
+ (xmtr_tech & SFP_XMTR_TECH_SN) ||
+ (xmtr_tech & SFP_XMTR_TECH_SA))
+ *media = BFA_SFP_MEDIA_SW;
+ /* Check 10G Ethernet Compilance code */
+ else if (e10g.b & 0x10)
+ *media = BFA_SFP_MEDIA_SW;
+ else if (e10g.b & 0x60)
+ *media = BFA_SFP_MEDIA_LW;
+ else if (e10g.r.e10g_unall & 0x80)
+ *media = BFA_SFP_MEDIA_UNKNOWN;
+ else
+ bfa_trc(sfp, 0);
+ } else
+ bfa_trc(sfp, sfp->state);
+}
+
+static bfa_status_t
+bfa_sfp_speed_valid(struct bfa_sfp_s *sfp, enum bfa_port_speed portspeed)
+{
+ struct sfp_mem_s *sfpmem = (struct sfp_mem_s *)sfp->dbuf_kva;
+ struct sfp_xcvr_s *xcvr = (struct sfp_xcvr_s *) sfpmem->srlid_base.xcvr;
+ union sfp_xcvr_fc3_code_u fc3 = xcvr->fc3;
+ union sfp_xcvr_e10g_code_u e10g = xcvr->e10g;
+
+ if (portspeed == BFA_PORT_SPEED_10GBPS) {
+ if (e10g.r.e10g_sr || e10g.r.e10g_lr)
+ return BFA_STATUS_OK;
+ else {
+ bfa_trc(sfp, e10g.b);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+ }
+ if (((portspeed & BFA_PORT_SPEED_16GBPS) && fc3.r.mb1600) ||
+ ((portspeed & BFA_PORT_SPEED_8GBPS) && fc3.r.mb800) ||
+ ((portspeed & BFA_PORT_SPEED_4GBPS) && fc3.r.mb400) ||
+ ((portspeed & BFA_PORT_SPEED_2GBPS) && fc3.r.mb200) ||
+ ((portspeed & BFA_PORT_SPEED_1GBPS) && fc3.r.mb100))
+ return BFA_STATUS_OK;
+ else {
+ bfa_trc(sfp, portspeed);
+ bfa_trc(sfp, fc3.b);
+ bfa_trc(sfp, e10g.b);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+}
+
+/*
+ * SFP hmbox handler
+ */
+void
+bfa_sfp_intr(void *sfparg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_sfp_s *sfp = sfparg;
+
+ switch (msg->mh.msg_id) {
+ case BFI_SFP_I2H_SHOW:
+ bfa_sfp_show_comp(sfp, msg);
+ break;
+
+ case BFI_SFP_I2H_SCN:
+ bfa_trc(sfp, msg->mh.msg_id);
+ break;
+
+ default:
+ bfa_trc(sfp, msg->mh.msg_id);
+ WARN_ON(1);
+ }
+}
+
+/*
+ * Return DMA memory needed by sfp module.
+ */
+u32
+bfa_sfp_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Attach virtual and physical memory for SFP.
+ */
+void
+bfa_sfp_attach(struct bfa_sfp_s *sfp, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod)
+{
+ sfp->dev = dev;
+ sfp->ioc = ioc;
+ sfp->trcmod = trcmod;
+
+ sfp->cbfn = NULL;
+ sfp->cbarg = NULL;
+ sfp->sfpmem = NULL;
+ sfp->lock = 0;
+ sfp->data_valid = 0;
+ sfp->state = BFA_SFP_STATE_INIT;
+ sfp->state_query_lock = 0;
+ sfp->state_query_cbfn = NULL;
+ sfp->state_query_cbarg = NULL;
+ sfp->media = NULL;
+ sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+ sfp->is_elb = BFA_FALSE;
+
+ bfa_ioc_mbox_regisr(sfp->ioc, BFI_MC_SFP, bfa_sfp_intr, sfp);
+ bfa_q_qe_init(&sfp->ioc_notify);
+ bfa_ioc_notify_init(&sfp->ioc_notify, bfa_sfp_notify, sfp);
+ list_add_tail(&sfp->ioc_notify.qe, &sfp->ioc->notify_q);
+}
+
+/*
+ * Claim Memory for SFP
+ */
+void
+bfa_sfp_memclaim(struct bfa_sfp_s *sfp, u8 *dm_kva, u64 dm_pa)
+{
+ sfp->dbuf_kva = dm_kva;
+ sfp->dbuf_pa = dm_pa;
+ memset(sfp->dbuf_kva, 0, sizeof(struct sfp_mem_s));
+
+ dm_kva += BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+ dm_pa += BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Show SFP eeprom content
+ *
+ * @param[in] sfp - bfa sfp module
+ *
+ * @param[out] sfpmem - sfp eeprom data
+ *
+ */
+bfa_status_t
+bfa_sfp_show(struct bfa_sfp_s *sfp, struct sfp_mem_s *sfpmem,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+
+ if (!bfa_ioc_is_operational(sfp->ioc)) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_IOC_NON_OP;
+ }
+
+ if (sfp->lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ sfp->cbfn = cbfn;
+ sfp->cbarg = cbarg;
+ sfp->sfpmem = sfpmem;
+
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_DIAGEXT);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Return SFP Media type
+ *
+ * @param[in] sfp - bfa sfp module
+ *
+ * @param[out] media - port speed from user
+ *
+ */
+bfa_status_t
+bfa_sfp_media(struct bfa_sfp_s *sfp, enum bfa_defs_sfp_media_e *media,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+ if (!bfa_ioc_is_operational(sfp->ioc)) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_IOC_NON_OP;
+ }
+
+ sfp->media = media;
+ if (sfp->state == BFA_SFP_STATE_INIT) {
+ if (sfp->state_query_lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ } else {
+ sfp->state_query_cbfn = cbfn;
+ sfp->state_query_cbarg = cbarg;
+ bfa_sfp_state_query(sfp);
+ return BFA_STATUS_SFP_NOT_READY;
+ }
+ }
+
+ bfa_sfp_media_get(sfp);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Check if user set port speed is allowed by the SFP
+ *
+ * @param[in] sfp - bfa sfp module
+ * @param[in] portspeed - port speed from user
+ *
+ */
+bfa_status_t
+bfa_sfp_speed(struct bfa_sfp_s *sfp, enum bfa_port_speed portspeed,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+ WARN_ON(portspeed == BFA_PORT_SPEED_UNKNOWN);
+
+ if (!bfa_ioc_is_operational(sfp->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* For Mezz card, all speed is allowed */
+ if (bfa_mfg_is_mezz(sfp->ioc->attr->card_type))
+ return BFA_STATUS_OK;
+
+ /* Check SFP state */
+ sfp->portspeed = portspeed;
+ if (sfp->state == BFA_SFP_STATE_INIT) {
+ if (sfp->state_query_lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ } else {
+ sfp->state_query_cbfn = cbfn;
+ sfp->state_query_cbarg = cbarg;
+ bfa_sfp_state_query(sfp);
+ return BFA_STATUS_SFP_NOT_READY;
+ }
+ }
+
+ if (sfp->state == BFA_SFP_STATE_REMOVED ||
+ sfp->state == BFA_SFP_STATE_FAILED) {
+ bfa_trc(sfp, sfp->state);
+ return BFA_STATUS_NO_SFP_DEV;
+ }
+
+ if (sfp->state == BFA_SFP_STATE_INSERTED) {
+ bfa_trc(sfp, sfp->state);
+ return BFA_STATUS_DEVBUSY; /* sfp is reading data */
+ }
+
+ /* For eloopback, all speed is allowed */
+ if (sfp->is_elb)
+ return BFA_STATUS_OK;
+
+ return bfa_sfp_speed_valid(sfp, portspeed);
+}