[SCSI] bfa: Added Fabric Assigned Address(FAA) support

- Updated/added data structures and definitions to support FAA protocol.
- Modified the IOC state machine to support FAA.
- Introduced FAA feature configuration - enable/disable/query.

Signed-off-by: Krishna Gudipati <kgudipat@brocade.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h
index 8c73265..42769bc 100644
--- a/drivers/scsi/bfa/bfa.h
+++ b/drivers/scsi/bfa/bfa.h
@@ -241,6 +241,31 @@
 };
 typedef void (*bfa_cb_iocfc_t) (void *cbarg, enum bfa_status status);
 
+struct bfa_faa_cbfn_s {
+	bfa_cb_iocfc_t	faa_cbfn;
+	void		*faa_cbarg;
+};
+
+#define BFA_FAA_ENABLED		1
+#define BFA_FAA_DISABLED	2
+
+/*
+ *	FAA attributes
+ */
+struct bfa_faa_attr_s {
+	wwn_t	faa;
+	u8	faa_state;
+	u8	pwwn_source;
+	u8	rsvd[6];
+};
+
+struct bfa_faa_args_s {
+	struct bfa_faa_attr_s	*faa_attr;
+	struct bfa_faa_cbfn_s	faa_cb;
+	u8			faa_state;
+	bfa_boolean_t		busy;
+};
+
 struct bfa_iocfc_s {
 	struct bfa_s		*bfa;
 	struct bfa_iocfc_cfg_s	cfg;
@@ -266,6 +291,7 @@
 	bfa_cb_iocfc_t		updateq_cbfn; /*  bios callback function */
 	void			*updateq_cbarg;	/*  bios callback arg */
 	u32	intr_mask;
+	struct bfa_faa_args_s	faa_args;
 };
 
 #define bfa_lpuid(__bfa)						\
diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c
index 2b49724..4befbf9 100644
--- a/drivers/scsi/bfa/bfa_core.c
+++ b/drivers/scsi/bfa/bfa_core.c
@@ -795,6 +795,181 @@
 	}
 }
 
+/* Fabric Assigned Address specific functions */
+
+/*
+ *	Check whether IOC is ready before sending command down
+ */
+static bfa_status_t
+bfa_faa_validate_request(struct bfa_s *bfa)
+{
+	enum bfa_ioc_type_e	ioc_type = bfa_get_type(bfa);
+	u32	card_type = bfa->ioc.attr->card_type;
+
+	if (bfa_ioc_is_operational(&bfa->ioc)) {
+		if ((ioc_type != BFA_IOC_TYPE_FC) || bfa_mfg_is_mezz(card_type))
+			return BFA_STATUS_FEATURE_NOT_SUPPORTED;
+	} else {
+		if (!bfa_ioc_is_acq_addr(&bfa->ioc))
+			return BFA_STATUS_IOC_NON_OP;
+	}
+
+	return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_faa_enable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn, void *cbarg)
+{
+	struct bfi_faa_en_dis_s faa_enable_req;
+	struct bfa_iocfc_s	*iocfc = &bfa->iocfc;
+	bfa_status_t		status;
+
+	iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
+	iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
+
+	status = bfa_faa_validate_request(bfa);
+	if (status != BFA_STATUS_OK)
+		return status;
+
+	if (iocfc->faa_args.busy == BFA_TRUE)
+		return BFA_STATUS_DEVBUSY;
+
+	if (iocfc->faa_args.faa_state == BFA_FAA_ENABLED)
+		return BFA_STATUS_FAA_ENABLED;
+
+	if (bfa_fcport_is_trunk_enabled(bfa))
+		return BFA_STATUS_ERROR_TRUNK_ENABLED;
+
+	bfa_fcport_cfg_faa(bfa, BFA_FAA_ENABLED);
+	iocfc->faa_args.busy = BFA_TRUE;
+
+	memset(&faa_enable_req, 0, sizeof(struct bfi_faa_en_dis_s));
+	bfi_h2i_set(faa_enable_req.mh, BFI_MC_IOCFC,
+		BFI_IOCFC_H2I_FAA_ENABLE_REQ, bfa_lpuid(bfa));
+
+	bfa_ioc_mbox_send(&bfa->ioc, &faa_enable_req,
+			sizeof(struct bfi_faa_en_dis_s));
+
+	return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_faa_disable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn,
+		void *cbarg)
+{
+	struct bfi_faa_en_dis_s faa_disable_req;
+	struct bfa_iocfc_s	*iocfc = &bfa->iocfc;
+	bfa_status_t		status;
+
+	iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
+	iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
+
+	status = bfa_faa_validate_request(bfa);
+	if (status != BFA_STATUS_OK)
+		return status;
+
+	if (iocfc->faa_args.busy == BFA_TRUE)
+		return BFA_STATUS_DEVBUSY;
+
+	if (iocfc->faa_args.faa_state == BFA_FAA_DISABLED)
+		return BFA_STATUS_FAA_DISABLED;
+
+	bfa_fcport_cfg_faa(bfa, BFA_FAA_DISABLED);
+	iocfc->faa_args.busy = BFA_TRUE;
+
+	memset(&faa_disable_req, 0, sizeof(struct bfi_faa_en_dis_s));
+	bfi_h2i_set(faa_disable_req.mh, BFI_MC_IOCFC,
+		BFI_IOCFC_H2I_FAA_DISABLE_REQ, bfa_lpuid(bfa));
+
+	bfa_ioc_mbox_send(&bfa->ioc, &faa_disable_req,
+		sizeof(struct bfi_faa_en_dis_s));
+
+	return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
+		bfa_cb_iocfc_t cbfn, void *cbarg)
+{
+	struct bfi_faa_query_s  faa_attr_req;
+	struct bfa_iocfc_s      *iocfc = &bfa->iocfc;
+	bfa_status_t            status;
+
+	iocfc->faa_args.faa_attr = attr;
+	iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
+	iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
+
+	status = bfa_faa_validate_request(bfa);
+	if (status != BFA_STATUS_OK)
+		return status;
+
+	if (iocfc->faa_args.busy == BFA_TRUE)
+		return BFA_STATUS_DEVBUSY;
+
+	iocfc->faa_args.busy = BFA_TRUE;
+	memset(&faa_attr_req, 0, sizeof(struct bfi_faa_query_s));
+	bfi_h2i_set(faa_attr_req.mh, BFI_MC_IOCFC,
+		BFI_IOCFC_H2I_FAA_QUERY_REQ, bfa_lpuid(bfa));
+
+	bfa_ioc_mbox_send(&bfa->ioc, &faa_attr_req,
+		sizeof(struct bfi_faa_query_s));
+
+	return BFA_STATUS_OK;
+}
+
+/*
+ *	FAA enable response
+ */
+static void
+bfa_faa_enable_reply(struct bfa_iocfc_s *iocfc,
+		struct bfi_faa_en_dis_rsp_s *rsp)
+{
+	void	*cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
+	bfa_status_t	status = rsp->status;
+
+	WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
+
+	iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
+	iocfc->faa_args.busy = BFA_FALSE;
+}
+
+/*
+ *	FAA disable response
+ */
+static void
+bfa_faa_disable_reply(struct bfa_iocfc_s *iocfc,
+		struct bfi_faa_en_dis_rsp_s *rsp)
+{
+	void	*cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
+	bfa_status_t	status = rsp->status;
+
+	WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
+
+	iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
+	iocfc->faa_args.busy = BFA_FALSE;
+}
+
+/*
+ *	FAA query response
+ */
+static void
+bfa_faa_query_reply(struct bfa_iocfc_s *iocfc,
+		bfi_faa_query_rsp_t *rsp)
+{
+	void	*cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
+
+	if (iocfc->faa_args.faa_attr) {
+		iocfc->faa_args.faa_attr->faa = rsp->faa;
+		iocfc->faa_args.faa_attr->faa_state = rsp->faa_status;
+		iocfc->faa_args.faa_attr->pwwn_source = rsp->addr_source;
+	}
+
+	WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
+
+	iocfc->faa_args.faa_cb.faa_cbfn(cbarg, BFA_STATUS_OK);
+	iocfc->faa_args.busy = BFA_FALSE;
+}
+
 /*
  * IOC enable request is complete
  */
@@ -803,6 +978,12 @@
 {
 	struct bfa_s	*bfa = bfa_arg;
 
+	if (status == BFA_STATUS_FAA_ACQ_ADDR) {
+		bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
+				bfa_iocfc_init_cb, bfa);
+		return;
+	}
+
 	if (status != BFA_STATUS_OK) {
 		bfa_isr_disable(bfa);
 		if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
@@ -968,6 +1149,17 @@
 	case BFI_IOCFC_I2H_UPDATEQ_RSP:
 		iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK);
 		break;
+	case BFI_IOCFC_I2H_FAA_ENABLE_RSP:
+		bfa_faa_enable_reply(iocfc,
+			(struct bfi_faa_en_dis_rsp_s *)msg);
+		break;
+	case BFI_IOCFC_I2H_FAA_DISABLE_RSP:
+		bfa_faa_disable_reply(iocfc,
+			(struct bfi_faa_en_dis_rsp_s *)msg);
+		break;
+	case BFI_IOCFC_I2H_FAA_QUERY_RSP:
+		bfa_faa_query_reply(iocfc, (bfi_faa_query_rsp_t *)msg);
+		break;
 	default:
 		WARN_ON(1);
 	}
diff --git a/drivers/scsi/bfa/bfa_defs.h b/drivers/scsi/bfa/bfa_defs.h
index c675e65..bb38265 100644
--- a/drivers/scsi/bfa/bfa_defs.h
+++ b/drivers/scsi/bfa/bfa_defs.h
@@ -145,6 +145,7 @@
 	BFA_STATUS_IOC_FAILURE	= 56,	/* IOC failure - Retry, if persists
 					 * contact support */
 	BFA_STATUS_INVALID_WWN	= 57,	/*  Invalid WWN */
+	BFA_STATUS_IOC_NON_OP   = 61,	/* IOC is not operational */
 	BFA_STATUS_VERSION_FAIL = 70, /* Application/Driver version mismatch */
 	BFA_STATUS_DIAG_BUSY	= 71,	/*  diag busy */
 	BFA_STATUS_ENOFSAVE	= 78,	/*  No saved firmware trace */
@@ -157,6 +158,12 @@
 	BFA_STATUS_TRUNK_DISABLED  = 165, /* Trunking is disabled on
 					   * the adapter */
 	BFA_STATUS_IOPROFILE_OFF = 175, /* IO profile OFF */
+	BFA_STATUS_FEATURE_NOT_SUPPORTED = 192,	/* Feature not supported */
+	BFA_STATUS_FAA_ENABLED = 197,	/* FAA is already enabled */
+	BFA_STATUS_FAA_DISABLED = 198,	/* FAA is already disabled */
+	BFA_STATUS_FAA_ACQUIRED = 199,	/* FAA is already acquired */
+	BFA_STATUS_FAA_ACQ_ADDR = 200,	/* Acquiring addr */
+	BFA_STATUS_ERROR_TRUNK_ENABLED = 203,	/* Trunk enabled on adapter */
 	BFA_STATUS_MAX_VAL		/* Unknown error code */
 };
 #define bfa_status_t enum bfa_status
@@ -275,6 +282,7 @@
 	BFA_IOC_FWMISMATCH	= 11,	/*  IOC f/w different from drivers */
 	BFA_IOC_ENABLING	= 12,	/*  IOC is being enabled */
 	BFA_IOC_HWFAIL		= 13,	/*  PCI mapping doesn't exist */
+	BFA_IOC_ACQ_ADDR	= 14,	/*  Acquiring addr from fabric */
 };
 
 /*
@@ -455,10 +463,7 @@
 	BFA_PORT_SPEED_8GBPS	= 8,
 	BFA_PORT_SPEED_10GBPS	= 10,
 	BFA_PORT_SPEED_16GBPS	= 16,
-	BFA_PORT_SPEED_AUTO =
-		(BFA_PORT_SPEED_1GBPS | BFA_PORT_SPEED_2GBPS |
-		 BFA_PORT_SPEED_4GBPS | BFA_PORT_SPEED_8GBPS |
-		 BFA_PORT_SPEED_16GBPS),
+	BFA_PORT_SPEED_AUTO	= 0xf,
 };
 #define bfa_port_speed_t enum bfa_port_speed
 
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index 0ee3f95..d886187 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -635,6 +635,7 @@
 	BFA_PORT_ST_FWMISMATCH		= 12,
 	BFA_PORT_ST_PREBOOT_DISABLED	= 13,
 	BFA_PORT_ST_TOGGLING_QWAIT	= 14,
+	BFA_PORT_ST_ACQ_ADDR		= 15,
 	BFA_PORT_ST_MAX_STATE,
 };
 
@@ -748,7 +749,8 @@
 	u8	 ratelimit;	/*  ratelimit enabled or not	*/
 	u8	 trl_def_speed;	/*  ratelimit default speed	*/
 	u8	bb_scn;
-	u8	rsvd[3];
+	u8	faa_state;	/*  FAA enabled/disabled        */
+	u8	rsvd[2];
 	u16 path_tov;	/*  device path timeout	*/
 	u16 q_depth;	/*  SCSI Queue depth		*/
 };
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 931bf97..7b4a567 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -113,6 +113,7 @@
 	IOC_E_HWERROR		= 10,	/*  hardware error interrupt	*/
 	IOC_E_TIMEOUT		= 11,	/*  timeout			*/
 	IOC_E_HWFAILED		= 12,	/*  PCI mapping failure notice	*/
+	IOC_E_FWRSP_ACQ_ADDR	= 13,	/*  Acquiring address		*/
 };
 
 bfa_fsm_state_decl(bfa_ioc, uninit, struct bfa_ioc_s, enum ioc_event);
@@ -125,6 +126,7 @@
 bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event);
 bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event);
 bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, acq_addr, struct bfa_ioc_s, enum ioc_event);
 
 static struct bfa_sm_table_s ioc_sm_table[] = {
 	{BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT},
@@ -137,6 +139,7 @@
 	{BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
 	{BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
 	{BFA_SM(bfa_ioc_sm_hwfail), BFA_IOC_HWFAIL},
+	{BFA_SM(bfa_ioc_sm_acq_addr), BFA_IOC_ACQ_ADDR},
 };
 
 /*
@@ -368,10 +371,16 @@
 	case IOC_E_FWRSP_GETATTR:
 		bfa_ioc_timer_stop(ioc);
 		bfa_ioc_check_attr_wwns(ioc);
+		bfa_ioc_hb_monitor(ioc);
 		bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
 		break;
 
+	case IOC_E_FWRSP_ACQ_ADDR:
+		bfa_ioc_timer_stop(ioc);
+		bfa_ioc_hb_monitor(ioc);
+		bfa_fsm_set_state(ioc, bfa_ioc_sm_acq_addr);
 		break;
+
 	case IOC_E_PFFAILED:
 	case IOC_E_HWERROR:
 		bfa_ioc_timer_stop(ioc);
@@ -396,6 +405,50 @@
 	}
 }
 
+/*
+ * Acquiring address from fabric (entry function)
+ */
+static void
+bfa_ioc_sm_acq_addr_entry(struct bfa_ioc_s *ioc)
+{
+}
+
+/*
+ *	Acquiring address from the fabric
+ */
+static void
+bfa_ioc_sm_acq_addr(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+	bfa_trc(ioc, event);
+
+	switch (event) {
+	case IOC_E_FWRSP_GETATTR:
+		bfa_ioc_check_attr_wwns(ioc);
+		bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
+		break;
+
+	case IOC_E_PFFAILED:
+	case IOC_E_HWERROR:
+		bfa_hb_timer_stop(ioc);
+	case IOC_E_HBFAIL:
+		ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+		bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+		if (event != IOC_E_PFFAILED)
+			bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_GETATTRFAIL);
+		break;
+
+	case IOC_E_DISABLE:
+		bfa_hb_timer_stop(ioc);
+		bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
+		break;
+
+	case IOC_E_ENABLE:
+		break;
+
+	default:
+		bfa_sm_fault(ioc, event);
+	}
+}
 
 static void
 bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
@@ -404,7 +457,6 @@
 
 	ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
 	bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
-	bfa_ioc_hb_monitor(ioc);
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC enabled\n");
 }
 
@@ -2065,6 +2117,10 @@
 		bfa_ioc_getattr_reply(ioc);
 		break;
 
+	case BFI_IOC_I2H_ACQ_ADDR_REPLY:
+		bfa_fsm_send_event(ioc, IOC_E_FWRSP_ACQ_ADDR);
+		break;
+
 	default:
 		bfa_trc(ioc, msg->mh.msg_id);
 		WARN_ON(1);
@@ -2360,6 +2416,15 @@
 }
 
 /*
+ * Return TRUE if IOC is in acquiring address state
+ */
+bfa_boolean_t
+bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc)
+{
+	return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_acq_addr);
+}
+
+/*
  * return true if IOC firmware is different.
  */
 bfa_boolean_t
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 337d036..78e9606 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -390,6 +390,7 @@
 bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc);
 bfa_boolean_t bfa_ioc_is_initialized(struct bfa_ioc_s *ioc);
 bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc);
+bfa_boolean_t bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc);
 bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc);
 bfa_boolean_t bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc);
 void bfa_ioc_reset_fwstate(struct bfa_ioc_s *ioc);
diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c
index 5373e5d..fe99ce2 100644
--- a/drivers/scsi/bfa/bfa_svc.c
+++ b/drivers/scsi/bfa/bfa_svc.c
@@ -3502,6 +3502,28 @@
 		return BFA_STATUS_UNSUPP_SPEED;
 	}
 
+	/* For Mezz card, port speed entered needs to be checked */
+	if (bfa_mfg_is_mezz(fcport->bfa->ioc.attr->card_type)) {
+		if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
+			/* For CT2, 1G is not supported */
+			if ((speed == BFA_PORT_SPEED_1GBPS) &&
+			    (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
+				return BFA_STATUS_UNSUPP_SPEED;
+
+			/* Already checked for Auto Speed and Max Speed supp */
+			if (!(speed == BFA_PORT_SPEED_1GBPS ||
+			      speed == BFA_PORT_SPEED_2GBPS ||
+			      speed == BFA_PORT_SPEED_4GBPS ||
+			      speed == BFA_PORT_SPEED_8GBPS ||
+			      speed == BFA_PORT_SPEED_16GBPS ||
+			      speed == BFA_PORT_SPEED_AUTO))
+				return BFA_STATUS_UNSUPP_SPEED;
+		} else {
+			if (speed != BFA_PORT_SPEED_10GBPS)
+				return BFA_STATUS_UNSUPP_SPEED;
+		}
+	}
+
 	fcport->cfg.speed = speed;
 
 	return BFA_STATUS_OK;
@@ -3705,6 +3727,8 @@
 			attr->port_state = BFA_PORT_ST_IOCDIS;
 		else if (bfa_ioc_fw_mismatch(&fcport->bfa->ioc))
 			attr->port_state = BFA_PORT_ST_FWMISMATCH;
+		else if (bfa_ioc_is_acq_addr(&fcport->bfa->ioc))
+			attr->port_state = BFA_PORT_ST_ACQ_ADDR;
 	}
 
 	/* FCoE vlan */
@@ -3787,6 +3811,18 @@
 }
 
 /*
+ *	Enable/Disable FAA feature in port config
+ */
+void
+bfa_fcport_cfg_faa(struct bfa_s *bfa, u8 state)
+{
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+	bfa_trc(bfa, state);
+	fcport->cfg.faa_state = state;
+}
+
+/*
  * Get default minimum ratelim speed
  */
 enum bfa_port_speed
diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h
index c81cf5c..acb30ef 100644
--- a/drivers/scsi/bfa/bfa_svc.h
+++ b/drivers/scsi/bfa/bfa_svc.h
@@ -529,6 +529,7 @@
 bfa_boolean_t bfa_fcport_is_qos_enabled(struct bfa_s *bfa);
 bfa_boolean_t bfa_fcport_is_trunk_enabled(struct bfa_s *bfa);
 bfa_status_t bfa_fcport_is_pbcdisabled(struct bfa_s *bfa);
+void bfa_fcport_cfg_faa(struct bfa_s *bfa, u8 state);
 
 /*
  * bfa rport API functions
@@ -623,4 +624,12 @@
 void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg);
 void bfa_cb_lps_cvl_event(void *bfad, void *uarg);
 
+/* FAA specific APIs */
+bfa_status_t bfa_faa_enable(struct bfa_s *bfa,
+			bfa_cb_iocfc_t cbfn, void *cbarg);
+bfa_status_t bfa_faa_disable(struct bfa_s *bfa,
+			bfa_cb_iocfc_t cbfn, void *cbarg);
+bfa_status_t bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
+			bfa_cb_iocfc_t cbfn, void *cbarg);
+
 #endif /* __BFA_SVC_H__ */
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 35c415c..9ec8f04 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -437,6 +437,73 @@
 	return 0;
 }
 
+int
+bfad_iocmd_faa_enable(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	unsigned long   flags;
+	struct bfad_hal_comp    fcomp;
+
+	init_completion(&fcomp.comp);
+	iocmd->status = BFA_STATUS_OK;
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_faa_enable(&bfad->bfa, bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_faa_disable(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	unsigned long   flags;
+	struct bfad_hal_comp    fcomp;
+
+	init_completion(&fcomp.comp);
+	iocmd->status = BFA_STATUS_OK;
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_faa_disable(&bfad->bfa, bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_faa_query(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_faa_attr_s *iocmd = (struct bfa_bsg_faa_attr_s *)cmd;
+	struct bfad_hal_comp    fcomp;
+	unsigned long   flags;
+
+	init_completion(&fcomp.comp);
+	iocmd->status = BFA_STATUS_OK;
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_faa_query(&bfad->bfa, &iocmd->faa_attr,
+				bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
 static int
 bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd,
 		unsigned int payload_len)
@@ -487,6 +554,15 @@
 	case IOCMD_FLASH_DISABLE_OPTROM:
 		rc = bfad_iocmd_ablk_optrom(bfad, cmd, iocmd);
 		break;
+	case IOCMD_FAA_ENABLE:
+		rc = bfad_iocmd_faa_enable(bfad, iocmd);
+		break;
+	case IOCMD_FAA_DISABLE:
+		rc = bfad_iocmd_faa_disable(bfad, iocmd);
+		break;
+	case IOCMD_FAA_QUERY:
+		rc = bfad_iocmd_faa_query(bfad, iocmd);
+		break;
 	default:
 		rc = EINVAL;
 		break;
diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h
index a4dcb39..49f558f 100644
--- a/drivers/scsi/bfa/bfad_bsg.h
+++ b/drivers/scsi/bfa/bfad_bsg.h
@@ -39,6 +39,9 @@
 	IOCMD_PORT_CFG_MODE,
 	IOCMD_FLASH_ENABLE_OPTROM,
 	IOCMD_FLASH_DISABLE_OPTROM,
+	IOCMD_FAA_ENABLE,
+	IOCMD_FAA_DISABLE,
+	IOCMD_FAA_QUERY,
 };
 
 struct bfa_bsg_gen_s {
@@ -156,6 +159,13 @@
 	struct bfa_port_cfg_mode_s cfg;
 };
 
+struct bfa_bsg_faa_attr_s {
+	bfa_status_t		status;
+	u16			bfad_num;
+	u16			rsvd;
+	struct bfa_faa_attr_s	faa_attr;
+};
+
 struct bfa_bsg_fcpt_s {
 	bfa_status_t    status;
 	u16		vf_id;
diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h
index 02e4456..d8f1a71 100644
--- a/drivers/scsi/bfa/bfi.h
+++ b/drivers/scsi/bfa/bfi.h
@@ -222,6 +222,7 @@
 	BFI_IOC_I2H_DISABLE_REPLY	= BFA_I2HM(2),
 	BFI_IOC_I2H_GETATTR_REPLY	= BFA_I2HM(3),
 	BFI_IOC_I2H_HBEAT		= BFA_I2HM(4),
+	BFI_IOC_I2H_ACQ_ADDR_REPLY	= BFA_I2HM(5),
 };
 
 /*
diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h
index 263631d..c665a80 100644
--- a/drivers/scsi/bfa/bfi_ms.h
+++ b/drivers/scsi/bfa/bfi_ms.h
@@ -28,11 +28,17 @@
 	BFI_IOCFC_H2I_CFG_REQ		= 1,
 	BFI_IOCFC_H2I_SET_INTR_REQ	= 2,
 	BFI_IOCFC_H2I_UPDATEQ_REQ	= 3,
+	BFI_IOCFC_H2I_FAA_ENABLE_REQ	= 4,
+	BFI_IOCFC_H2I_FAA_DISABLE_REQ	= 5,
+	BFI_IOCFC_H2I_FAA_QUERY_REQ	= 6,
 };
 
 enum bfi_iocfc_i2h_msgs {
 	BFI_IOCFC_I2H_CFG_REPLY		= BFA_I2HM(1),
 	BFI_IOCFC_I2H_UPDATEQ_RSP	= BFA_I2HM(3),
+	BFI_IOCFC_I2H_FAA_ENABLE_RSP	= BFA_I2HM(4),
+	BFI_IOCFC_I2H_FAA_DISABLE_RSP	= BFA_I2HM(5),
+	BFI_IOCFC_I2H_FAA_QUERY_RSP	= BFA_I2HM(6),
 };
 
 struct bfi_iocfc_cfg_s {
@@ -166,6 +172,37 @@
 	u32 mboxmsg[BFI_IOC_MSGSZ];
 };
 
+/*
+ * BFI_IOCFC_H2I_FAA_ENABLE_REQ BFI_IOCFC_H2I_FAA_DISABLE_REQ message
+ */
+struct bfi_faa_en_dis_s {
+	struct bfi_mhdr_s mh;	/* common msg header    */
+};
+
+/*
+ * BFI_IOCFC_H2I_FAA_QUERY_REQ message
+ */
+struct bfi_faa_query_s {
+	struct bfi_mhdr_s mh;	/* common msg header    */
+	u8	faa_status;	/* FAA status           */
+	u8	addr_source;	/* PWWN source          */
+	u8	rsvd[2];
+	wwn_t	faa;		/* Fabric acquired PWWN	*/
+};
+
+/*
+ * BFI_IOCFC_I2H_FAA_ENABLE_RSP, BFI_IOCFC_I2H_FAA_DISABLE_RSP message
+ */
+struct bfi_faa_en_dis_rsp_s {
+	struct bfi_mhdr_s mh;	/* common msg header    */
+	u8	status;		/* updateq  status      */
+	u8	rsvd[3];
+};
+
+/*
+ * BFI_IOCFC_I2H_FAA_QUERY_RSP message
+ */
+#define bfi_faa_query_rsp_t struct bfi_faa_query_s
 
 enum bfi_fcport_h2i {
 	BFI_FCPORT_H2I_ENABLE_REQ		= (1),