13146240fSJames Smart // SPDX-License-Identifier: GPL-2.0
23146240fSJames Smart /*
33146240fSJames Smart  * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
43146240fSJames Smart  * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
53146240fSJames Smart  */
63146240fSJames Smart 
73146240fSJames Smart /*
83146240fSJames Smart  * domain_sm Domain State Machine: States
93146240fSJames Smart  */
103146240fSJames Smart 
113146240fSJames Smart #include "efc.h"
123146240fSJames Smart 
133146240fSJames Smart int
efc_domain_cb(void * arg,int event,void * data)143146240fSJames Smart efc_domain_cb(void *arg, int event, void *data)
153146240fSJames Smart {
163146240fSJames Smart 	struct efc *efc = arg;
173146240fSJames Smart 	struct efc_domain *domain = NULL;
183146240fSJames Smart 	int rc = 0;
193146240fSJames Smart 	unsigned long flags = 0;
203146240fSJames Smart 
213146240fSJames Smart 	if (event != EFC_HW_DOMAIN_FOUND)
223146240fSJames Smart 		domain = data;
233146240fSJames Smart 
243146240fSJames Smart 	/* Accept domain callback events from the user driver */
253146240fSJames Smart 	spin_lock_irqsave(&efc->lock, flags);
263146240fSJames Smart 	switch (event) {
273146240fSJames Smart 	case EFC_HW_DOMAIN_FOUND: {
283146240fSJames Smart 		u64 fcf_wwn = 0;
293146240fSJames Smart 		struct efc_domain_record *drec = data;
303146240fSJames Smart 
313146240fSJames Smart 		/* extract the fcf_wwn */
323146240fSJames Smart 		fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
333146240fSJames Smart 
343146240fSJames Smart 		efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
353146240fSJames Smart 
363146240fSJames Smart 		/* lookup domain, or allocate a new one */
373146240fSJames Smart 		domain = efc->domain;
383146240fSJames Smart 		if (!domain) {
393146240fSJames Smart 			domain = efc_domain_alloc(efc, fcf_wwn);
403146240fSJames Smart 			if (!domain) {
413146240fSJames Smart 				efc_log_err(efc, "efc_domain_alloc() failed\n");
423146240fSJames Smart 				rc = -1;
433146240fSJames Smart 				break;
443146240fSJames Smart 			}
453146240fSJames Smart 			efc_sm_transition(&domain->drvsm, __efc_domain_init,
463146240fSJames Smart 					  NULL);
473146240fSJames Smart 		}
483146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
493146240fSJames Smart 		break;
503146240fSJames Smart 	}
513146240fSJames Smart 
523146240fSJames Smart 	case EFC_HW_DOMAIN_LOST:
533146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
543146240fSJames Smart 		efc->hold_frames = true;
553146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
563146240fSJames Smart 		break;
573146240fSJames Smart 
583146240fSJames Smart 	case EFC_HW_DOMAIN_ALLOC_OK:
593146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
603146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
613146240fSJames Smart 		break;
623146240fSJames Smart 
633146240fSJames Smart 	case EFC_HW_DOMAIN_ALLOC_FAIL:
643146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
653146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
663146240fSJames Smart 				      NULL);
673146240fSJames Smart 		break;
683146240fSJames Smart 
693146240fSJames Smart 	case EFC_HW_DOMAIN_ATTACH_OK:
703146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
713146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
723146240fSJames Smart 		break;
733146240fSJames Smart 
743146240fSJames Smart 	case EFC_HW_DOMAIN_ATTACH_FAIL:
753146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
763146240fSJames Smart 		efc_domain_post_event(domain,
773146240fSJames Smart 				      EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
783146240fSJames Smart 		break;
793146240fSJames Smart 
803146240fSJames Smart 	case EFC_HW_DOMAIN_FREE_OK:
813146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
823146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
833146240fSJames Smart 		break;
843146240fSJames Smart 
853146240fSJames Smart 	case EFC_HW_DOMAIN_FREE_FAIL:
863146240fSJames Smart 		domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
873146240fSJames Smart 		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
883146240fSJames Smart 		break;
893146240fSJames Smart 
903146240fSJames Smart 	default:
913146240fSJames Smart 		efc_log_warn(efc, "unsupported event %#x\n", event);
923146240fSJames Smart 	}
933146240fSJames Smart 	spin_unlock_irqrestore(&efc->lock, flags);
943146240fSJames Smart 
953146240fSJames Smart 	if (efc->domain && domain->req_accept_frames) {
963146240fSJames Smart 		domain->req_accept_frames = false;
973146240fSJames Smart 		efc->hold_frames = false;
983146240fSJames Smart 	}
993146240fSJames Smart 
1003146240fSJames Smart 	return rc;
1013146240fSJames Smart }
1023146240fSJames Smart 
1033146240fSJames Smart static void
_efc_domain_free(struct kref * arg)1043146240fSJames Smart _efc_domain_free(struct kref *arg)
1053146240fSJames Smart {
1063146240fSJames Smart 	struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
1073146240fSJames Smart 	struct efc *efc = domain->efc;
1083146240fSJames Smart 
1093146240fSJames Smart 	if (efc->domain_free_cb)
1103146240fSJames Smart 		(*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
1113146240fSJames Smart 
1123146240fSJames Smart 	kfree(domain);
1133146240fSJames Smart }
1143146240fSJames Smart 
1153146240fSJames Smart void
efc_domain_free(struct efc_domain * domain)1163146240fSJames Smart efc_domain_free(struct efc_domain *domain)
1173146240fSJames Smart {
1183146240fSJames Smart 	struct efc *efc;
1193146240fSJames Smart 
1203146240fSJames Smart 	efc = domain->efc;
1213146240fSJames Smart 
1223146240fSJames Smart 	/* Hold frames to clear the domain pointer from the xport lookup */
1233146240fSJames Smart 	efc->hold_frames = false;
1243146240fSJames Smart 
1253146240fSJames Smart 	efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
1263146240fSJames Smart 
1273146240fSJames Smart 	xa_destroy(&domain->lookup);
1283146240fSJames Smart 	efc->domain = NULL;
1293146240fSJames Smart 	kref_put(&domain->ref, domain->release);
1303146240fSJames Smart }
1313146240fSJames Smart 
1323146240fSJames Smart struct efc_domain *
efc_domain_alloc(struct efc * efc,uint64_t fcf_wwn)1333146240fSJames Smart efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
1343146240fSJames Smart {
1353146240fSJames Smart 	struct efc_domain *domain;
1363146240fSJames Smart 
1373146240fSJames Smart 	domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
1383146240fSJames Smart 	if (!domain)
1393146240fSJames Smart 		return NULL;
1403146240fSJames Smart 
1413146240fSJames Smart 	domain->efc = efc;
1423146240fSJames Smart 	domain->drvsm.app = domain;
1433146240fSJames Smart 
1443146240fSJames Smart 	/* initialize refcount */
1453146240fSJames Smart 	kref_init(&domain->ref);
1463146240fSJames Smart 	domain->release = _efc_domain_free;
1473146240fSJames Smart 
1483146240fSJames Smart 	xa_init(&domain->lookup);
1493146240fSJames Smart 
1503146240fSJames Smart 	INIT_LIST_HEAD(&domain->nport_list);
1513146240fSJames Smart 	efc->domain = domain;
1523146240fSJames Smart 	domain->fcf_wwn = fcf_wwn;
1533146240fSJames Smart 	efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
1543146240fSJames Smart 
1553146240fSJames Smart 	return domain;
1563146240fSJames Smart }
1573146240fSJames Smart 
1583146240fSJames Smart void
efc_register_domain_free_cb(struct efc * efc,void (* callback)(struct efc * efc,void * arg),void * arg)1593146240fSJames Smart efc_register_domain_free_cb(struct efc *efc,
1603146240fSJames Smart 			    void (*callback)(struct efc *efc, void *arg),
1613146240fSJames Smart 			    void *arg)
1623146240fSJames Smart {
1633146240fSJames Smart 	/* Register a callback to be called when the domain is freed */
1643146240fSJames Smart 	efc->domain_free_cb = callback;
1653146240fSJames Smart 	efc->domain_free_cb_arg = arg;
1663146240fSJames Smart 	if (!efc->domain && callback)
1673146240fSJames Smart 		(*callback)(efc, arg);
1683146240fSJames Smart }
1693146240fSJames Smart 
1703146240fSJames Smart static void
__efc_domain_common(const char * funcname,struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)1713146240fSJames Smart __efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,
1723146240fSJames Smart 		    enum efc_sm_event evt, void *arg)
1733146240fSJames Smart {
1743146240fSJames Smart 	struct efc_domain *domain = ctx->app;
1753146240fSJames Smart 
1763146240fSJames Smart 	switch (evt) {
1773146240fSJames Smart 	case EFC_EVT_ENTER:
1783146240fSJames Smart 	case EFC_EVT_REENTER:
1793146240fSJames Smart 	case EFC_EVT_EXIT:
1803146240fSJames Smart 	case EFC_EVT_ALL_CHILD_NODES_FREE:
1813146240fSJames Smart 		/*
1823146240fSJames Smart 		 * this can arise if an FLOGI fails on the NPORT,
1833146240fSJames Smart 		 * and the NPORT is shutdown
1843146240fSJames Smart 		 */
1853146240fSJames Smart 		break;
1863146240fSJames Smart 	default:
1873146240fSJames Smart 		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
1883146240fSJames Smart 			     funcname, efc_sm_event_name(evt));
1893146240fSJames Smart 	}
1903146240fSJames Smart }
1913146240fSJames Smart 
1923146240fSJames Smart static void
__efc_domain_common_shutdown(const char * funcname,struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)1933146240fSJames Smart __efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,
1943146240fSJames Smart 			     enum efc_sm_event evt, void *arg)
1953146240fSJames Smart {
1963146240fSJames Smart 	struct efc_domain *domain = ctx->app;
1973146240fSJames Smart 
1983146240fSJames Smart 	switch (evt) {
1993146240fSJames Smart 	case EFC_EVT_ENTER:
2003146240fSJames Smart 	case EFC_EVT_REENTER:
2013146240fSJames Smart 	case EFC_EVT_EXIT:
2023146240fSJames Smart 		break;
2033146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND:
2043146240fSJames Smart 		/* save drec, mark domain_found_pending */
2053146240fSJames Smart 		memcpy(&domain->pending_drec, arg,
2063146240fSJames Smart 		       sizeof(domain->pending_drec));
2073146240fSJames Smart 		domain->domain_found_pending = true;
2083146240fSJames Smart 		break;
2093146240fSJames Smart 	case EFC_EVT_DOMAIN_LOST:
2103146240fSJames Smart 		/* unmark domain_found_pending */
2113146240fSJames Smart 		domain->domain_found_pending = false;
2123146240fSJames Smart 		break;
2133146240fSJames Smart 
2143146240fSJames Smart 	default:
2153146240fSJames Smart 		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
2163146240fSJames Smart 			     funcname, efc_sm_event_name(evt));
2173146240fSJames Smart 	}
2183146240fSJames Smart }
2193146240fSJames Smart 
2203146240fSJames Smart #define std_domain_state_decl(...)\
2213146240fSJames Smart 	struct efc_domain *domain = NULL;\
2223146240fSJames Smart 	struct efc *efc = NULL;\
2233146240fSJames Smart 	\
2243146240fSJames Smart 	WARN_ON(!ctx || !ctx->app);\
2253146240fSJames Smart 	domain = ctx->app;\
2263146240fSJames Smart 	WARN_ON(!domain->efc);\
2273146240fSJames Smart 	efc = domain->efc
2283146240fSJames Smart 
2293146240fSJames Smart void
__efc_domain_init(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)2303146240fSJames Smart __efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
2313146240fSJames Smart 		  void *arg)
2323146240fSJames Smart {
2333146240fSJames Smart 	std_domain_state_decl();
2343146240fSJames Smart 
2353146240fSJames Smart 	domain_sm_trace(domain);
2363146240fSJames Smart 
2373146240fSJames Smart 	switch (evt) {
2383146240fSJames Smart 	case EFC_EVT_ENTER:
2393146240fSJames Smart 		domain->attached = false;
2403146240fSJames Smart 		break;
2413146240fSJames Smart 
2423146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND: {
2433146240fSJames Smart 		u32	i;
2443146240fSJames Smart 		struct efc_domain_record *drec = arg;
2453146240fSJames Smart 		struct efc_nport *nport;
2463146240fSJames Smart 
2473146240fSJames Smart 		u64 my_wwnn = efc->req_wwnn;
2483146240fSJames Smart 		u64 my_wwpn = efc->req_wwpn;
2493146240fSJames Smart 		__be64 bewwpn;
2503146240fSJames Smart 
2513146240fSJames Smart 		if (my_wwpn == 0 || my_wwnn == 0) {
2523146240fSJames Smart 			efc_log_debug(efc, "using default hardware WWN config\n");
2533146240fSJames Smart 			my_wwpn = efc->def_wwpn;
2543146240fSJames Smart 			my_wwnn = efc->def_wwnn;
2553146240fSJames Smart 		}
2563146240fSJames Smart 
2573146240fSJames Smart 		efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",
2583146240fSJames Smart 			      my_wwpn, my_wwnn);
2593146240fSJames Smart 
2603146240fSJames Smart 		/* Allocate a nport and transition to __efc_nport_allocated */
2613146240fSJames Smart 		nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,
2623146240fSJames Smart 					efc->enable_ini, efc->enable_tgt);
2633146240fSJames Smart 
2643146240fSJames Smart 		if (!nport) {
2653146240fSJames Smart 			efc_log_err(efc, "efc_nport_alloc() failed\n");
2663146240fSJames Smart 			break;
2673146240fSJames Smart 		}
2683146240fSJames Smart 		efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);
2693146240fSJames Smart 
2703146240fSJames Smart 		bewwpn = cpu_to_be64(nport->wwpn);
2713146240fSJames Smart 
2723146240fSJames Smart 		/* allocate struct efc_nport object for local port
2733146240fSJames Smart 		 * Note: drec->fc_id is ALPA from read_topology only if loop
2743146240fSJames Smart 		 */
2753146240fSJames Smart 		if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {
2763146240fSJames Smart 			efc_log_err(efc, "Can't allocate port\n");
2773146240fSJames Smart 			efc_nport_free(nport);
2783146240fSJames Smart 			break;
2793146240fSJames Smart 		}
2803146240fSJames Smart 
2813146240fSJames Smart 		domain->is_loop = drec->is_loop;
2823146240fSJames Smart 
2833146240fSJames Smart 		/*
2843146240fSJames Smart 		 * If the loop position map includes ALPA == 0,
2853146240fSJames Smart 		 * then we are in a public loop (NL_PORT)
2863146240fSJames Smart 		 * Note that the first element of the loopmap[]
2873146240fSJames Smart 		 * contains the count of elements, and if
2883146240fSJames Smart 		 * ALPA == 0 is present, it will occupy the first
2893146240fSJames Smart 		 * location after the count.
2903146240fSJames Smart 		 */
2913146240fSJames Smart 		domain->is_nlport = drec->map.loop[1] == 0x00;
2923146240fSJames Smart 
2933146240fSJames Smart 		if (!domain->is_loop) {
2943146240fSJames Smart 			/* Initiate HW domain alloc */
2953146240fSJames Smart 			if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
2963146240fSJames Smart 				efc_log_err(efc,
2973146240fSJames Smart 					    "Failed to initiate HW domain allocation\n");
2983146240fSJames Smart 				break;
2993146240fSJames Smart 			}
3003146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
3013146240fSJames Smart 			break;
3023146240fSJames Smart 		}
3033146240fSJames Smart 
3043146240fSJames Smart 		efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",
3053146240fSJames Smart 			      drec->is_loop ?
3063146240fSJames Smart 			      (domain->is_nlport ?
3073146240fSJames Smart 			      "public-loop" : "loop") : "other",
3083146240fSJames Smart 			      drec->fc_id, drec->speed);
3093146240fSJames Smart 
3103146240fSJames Smart 		nport->fc_id = drec->fc_id;
3113146240fSJames Smart 		nport->topology = EFC_NPORT_TOPO_FC_AL;
3123146240fSJames Smart 		snprintf(nport->display_name, sizeof(nport->display_name),
3133146240fSJames Smart 			 "s%06x", drec->fc_id);
3143146240fSJames Smart 
3153146240fSJames Smart 		if (efc->enable_ini) {
3163146240fSJames Smart 			u32 count = drec->map.loop[0];
3173146240fSJames Smart 
3183146240fSJames Smart 			efc_log_debug(efc, "%d position map entries\n",
3193146240fSJames Smart 				      count);
3203146240fSJames Smart 			for (i = 1; i <= count; i++) {
3213146240fSJames Smart 				if (drec->map.loop[i] != drec->fc_id) {
3223146240fSJames Smart 					struct efc_node *node;
3233146240fSJames Smart 
3243146240fSJames Smart 					efc_log_debug(efc, "%#x -> %#x\n",
3253146240fSJames Smart 						      drec->fc_id,
3263146240fSJames Smart 						      drec->map.loop[i]);
3273146240fSJames Smart 					node = efc_node_alloc(nport,
3283146240fSJames Smart 							      drec->map.loop[i],
3293146240fSJames Smart 							      false, true);
3303146240fSJames Smart 					if (!node) {
3313146240fSJames Smart 						efc_log_err(efc,
3323146240fSJames Smart 							    "efc_node_alloc() failed\n");
3333146240fSJames Smart 						break;
3343146240fSJames Smart 					}
3353146240fSJames Smart 					efc_node_transition(node,
3363146240fSJames Smart 							    __efc_d_wait_loop,
3373146240fSJames Smart 							    NULL);
3383146240fSJames Smart 				}
3393146240fSJames Smart 			}
3403146240fSJames Smart 		}
3413146240fSJames Smart 
3423146240fSJames Smart 		/* Initiate HW domain alloc */
3433146240fSJames Smart 		if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
3443146240fSJames Smart 			efc_log_err(efc,
3453146240fSJames Smart 				    "Failed to initiate HW domain allocation\n");
3463146240fSJames Smart 			break;
3473146240fSJames Smart 		}
3483146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
3493146240fSJames Smart 		break;
3503146240fSJames Smart 	}
3513146240fSJames Smart 	default:
3523146240fSJames Smart 		__efc_domain_common(__func__, ctx, evt, arg);
3533146240fSJames Smart 	}
3543146240fSJames Smart }
3553146240fSJames Smart 
3563146240fSJames Smart void
__efc_domain_wait_alloc(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)3573146240fSJames Smart __efc_domain_wait_alloc(struct efc_sm_ctx *ctx,
3583146240fSJames Smart 			enum efc_sm_event evt, void *arg)
3593146240fSJames Smart {
3603146240fSJames Smart 	std_domain_state_decl();
3613146240fSJames Smart 
3623146240fSJames Smart 	domain_sm_trace(domain);
3633146240fSJames Smart 
3643146240fSJames Smart 	switch (evt) {
3653146240fSJames Smart 	case EFC_EVT_DOMAIN_ALLOC_OK: {
3663146240fSJames Smart 		struct fc_els_flogi  *sp;
3673146240fSJames Smart 		struct efc_nport *nport;
3683146240fSJames Smart 
3693146240fSJames Smart 		nport = domain->nport;
3703146240fSJames Smart 		if (WARN_ON(!nport))
3713146240fSJames Smart 			return;
3723146240fSJames Smart 
3733146240fSJames Smart 		sp = (struct fc_els_flogi  *)nport->service_params;
3743146240fSJames Smart 
3753146240fSJames Smart 		/* Save the domain service parameters */
3763146240fSJames Smart 		memcpy(domain->service_params + 4, domain->dma.virt,
3773146240fSJames Smart 		       sizeof(struct fc_els_flogi) - 4);
3783146240fSJames Smart 		memcpy(nport->service_params + 4, domain->dma.virt,
3793146240fSJames Smart 		       sizeof(struct fc_els_flogi) - 4);
3803146240fSJames Smart 
3813146240fSJames Smart 		/*
3823146240fSJames Smart 		 * Update the nport's service parameters,
3833146240fSJames Smart 		 * user might have specified non-default names
3843146240fSJames Smart 		 */
3853146240fSJames Smart 		sp->fl_wwpn = cpu_to_be64(nport->wwpn);
3863146240fSJames Smart 		sp->fl_wwnn = cpu_to_be64(nport->wwnn);
3873146240fSJames Smart 
3883146240fSJames Smart 		/*
3893146240fSJames Smart 		 * Take the loop topology path,
3903146240fSJames Smart 		 * unless we are an NL_PORT (public loop)
3913146240fSJames Smart 		 */
3923146240fSJames Smart 		if (domain->is_loop && !domain->is_nlport) {
3933146240fSJames Smart 			/*
3943146240fSJames Smart 			 * For loop, we already have our FC ID
3953146240fSJames Smart 			 * and don't need fabric login.
3963146240fSJames Smart 			 * Transition to the allocated state and
3973146240fSJames Smart 			 * post an event to attach to
3983146240fSJames Smart 			 * the domain. Note that this breaks the
3993146240fSJames Smart 			 * normal action/transition
4003146240fSJames Smart 			 * pattern here to avoid a race with the
4013146240fSJames Smart 			 * domain attach callback.
4023146240fSJames Smart 			 */
4033146240fSJames Smart 			/* sm: is_loop / domain_attach */
4043146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_allocated, NULL);
4053146240fSJames Smart 			__efc_domain_attach_internal(domain, nport->fc_id);
4063146240fSJames Smart 			break;
4073146240fSJames Smart 		}
4083146240fSJames Smart 		{
4093146240fSJames Smart 			struct efc_node *node;
4103146240fSJames Smart 
4113146240fSJames Smart 			/* alloc fabric node, send FLOGI */
4123146240fSJames Smart 			node = efc_node_find(nport, FC_FID_FLOGI);
4133146240fSJames Smart 			if (node) {
4143146240fSJames Smart 				efc_log_err(efc,
4153146240fSJames Smart 					    "Fabric Controller node already exists\n");
4163146240fSJames Smart 				break;
4173146240fSJames Smart 			}
4183146240fSJames Smart 			node = efc_node_alloc(nport, FC_FID_FLOGI,
4193146240fSJames Smart 					      false, false);
4203146240fSJames Smart 			if (!node) {
4213146240fSJames Smart 				efc_log_err(efc,
4223146240fSJames Smart 					    "Error: efc_node_alloc() failed\n");
4233146240fSJames Smart 			} else {
4243146240fSJames Smart 				efc_node_transition(node,
4253146240fSJames Smart 						    __efc_fabric_init, NULL);
4263146240fSJames Smart 			}
4273146240fSJames Smart 			/* Accept frames */
4283146240fSJames Smart 			domain->req_accept_frames = true;
4293146240fSJames Smart 		}
4303146240fSJames Smart 		/* sm: / start fabric logins */
4313146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_allocated, NULL);
4323146240fSJames Smart 		break;
4333146240fSJames Smart 	}
4343146240fSJames Smart 
4353146240fSJames Smart 	case EFC_EVT_DOMAIN_ALLOC_FAIL:
4363146240fSJames Smart 		efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",
4373146240fSJames Smart 			    efc_sm_event_name(evt));
4383146240fSJames Smart 		efc_log_err(efc, "shutting down domain\n");
4393146240fSJames Smart 		domain->req_domain_free = true;
4403146240fSJames Smart 		break;
4413146240fSJames Smart 
4423146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND:
4433146240fSJames Smart 		/* Should not happen */
4443146240fSJames Smart 		break;
4453146240fSJames Smart 
4463146240fSJames Smart 	case EFC_EVT_DOMAIN_LOST:
4473146240fSJames Smart 		efc_log_debug(efc,
4483146240fSJames Smart 			      "%s received while waiting for hw_domain_alloc()\n",
4493146240fSJames Smart 			efc_sm_event_name(evt));
4503146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
4513146240fSJames Smart 		break;
4523146240fSJames Smart 
4533146240fSJames Smart 	default:
4543146240fSJames Smart 		__efc_domain_common(__func__, ctx, evt, arg);
4553146240fSJames Smart 	}
4563146240fSJames Smart }
4573146240fSJames Smart 
4583146240fSJames Smart void
__efc_domain_allocated(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)4593146240fSJames Smart __efc_domain_allocated(struct efc_sm_ctx *ctx,
4603146240fSJames Smart 		       enum efc_sm_event evt, void *arg)
4613146240fSJames Smart {
4623146240fSJames Smart 	std_domain_state_decl();
4633146240fSJames Smart 
4643146240fSJames Smart 	domain_sm_trace(domain);
4653146240fSJames Smart 
4663146240fSJames Smart 	switch (evt) {
4673146240fSJames Smart 	case EFC_EVT_DOMAIN_REQ_ATTACH: {
4683146240fSJames Smart 		int rc = 0;
4693146240fSJames Smart 		u32 fc_id;
4703146240fSJames Smart 
4713146240fSJames Smart 		if (WARN_ON(!arg))
4723146240fSJames Smart 			return;
4733146240fSJames Smart 
4743146240fSJames Smart 		fc_id = *((u32 *)arg);
4753146240fSJames Smart 		efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",
4763146240fSJames Smart 			      fc_id);
4773146240fSJames Smart 		/* Update nport lookup */
4783146240fSJames Smart 		rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,
4793146240fSJames Smart 				     GFP_ATOMIC));
4803146240fSJames Smart 		if (rc) {
4813146240fSJames Smart 			efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
4823146240fSJames Smart 			return;
4833146240fSJames Smart 		}
4843146240fSJames Smart 
4853146240fSJames Smart 		/* Update display name for the nport */
4863146240fSJames Smart 		efc_node_fcid_display(fc_id, domain->nport->display_name,
4873146240fSJames Smart 				      sizeof(domain->nport->display_name));
4883146240fSJames Smart 
4893146240fSJames Smart 		/* Issue domain attach call */
4903146240fSJames Smart 		rc = efc_cmd_domain_attach(efc, domain, fc_id);
4913146240fSJames Smart 		if (rc) {
4923146240fSJames Smart 			efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",
4933146240fSJames Smart 				    rc);
4943146240fSJames Smart 			return;
4953146240fSJames Smart 		}
4963146240fSJames Smart 		/* sm: / domain_attach */
4973146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);
4983146240fSJames Smart 		break;
4993146240fSJames Smart 	}
5003146240fSJames Smart 
5013146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND:
5023146240fSJames Smart 		/* Should not happen */
5033146240fSJames Smart 		efc_log_err(efc, "%s: evt: %d should not happen\n",
5043146240fSJames Smart 			    __func__, evt);
5053146240fSJames Smart 		break;
5063146240fSJames Smart 
5073146240fSJames Smart 	case EFC_EVT_DOMAIN_LOST: {
5083146240fSJames Smart 		efc_log_debug(efc,
5093146240fSJames Smart 			      "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",
5103146240fSJames Smart 			efc_sm_event_name(evt));
5113146240fSJames Smart 		if (!list_empty(&domain->nport_list)) {
5123146240fSJames Smart 			/*
5133146240fSJames Smart 			 * if there are nports, transition to
5143146240fSJames Smart 			 * wait state and send shutdown to each
5153146240fSJames Smart 			 * nport
5163146240fSJames Smart 			 */
5173146240fSJames Smart 			struct efc_nport *nport = NULL, *nport_next = NULL;
5183146240fSJames Smart 
5193146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
5203146240fSJames Smart 					  NULL);
5213146240fSJames Smart 			list_for_each_entry_safe(nport, nport_next,
5223146240fSJames Smart 						 &domain->nport_list,
5233146240fSJames Smart 						 list_entry) {
5243146240fSJames Smart 				efc_sm_post_event(&nport->sm,
5253146240fSJames Smart 						  EFC_EVT_SHUTDOWN, NULL);
5263146240fSJames Smart 			}
5273146240fSJames Smart 		} else {
5283146240fSJames Smart 			/* no nports exist, free domain */
5293146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
5303146240fSJames Smart 					  NULL);
5313146240fSJames Smart 			if (efc_cmd_domain_free(efc, domain))
5323146240fSJames Smart 				efc_log_err(efc, "hw_domain_free failed\n");
5333146240fSJames Smart 		}
5343146240fSJames Smart 
5353146240fSJames Smart 		break;
5363146240fSJames Smart 	}
5373146240fSJames Smart 
5383146240fSJames Smart 	default:
5393146240fSJames Smart 		__efc_domain_common(__func__, ctx, evt, arg);
5403146240fSJames Smart 	}
5413146240fSJames Smart }
5423146240fSJames Smart 
5433146240fSJames Smart void
__efc_domain_wait_attach(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)5443146240fSJames Smart __efc_domain_wait_attach(struct efc_sm_ctx *ctx,
5453146240fSJames Smart 			 enum efc_sm_event evt, void *arg)
5463146240fSJames Smart {
5473146240fSJames Smart 	std_domain_state_decl();
5483146240fSJames Smart 
5493146240fSJames Smart 	domain_sm_trace(domain);
5503146240fSJames Smart 
5513146240fSJames Smart 	switch (evt) {
5523146240fSJames Smart 	case EFC_EVT_DOMAIN_ATTACH_OK: {
5533146240fSJames Smart 		struct efc_node *node = NULL;
5543146240fSJames Smart 		struct efc_nport *nport, *next_nport;
5553146240fSJames Smart 		unsigned long index;
5563146240fSJames Smart 
5573146240fSJames Smart 		/*
5583146240fSJames Smart 		 * Set domain notify pending state to avoid
5593146240fSJames Smart 		 * duplicate domain event post
5603146240fSJames Smart 		 */
5613146240fSJames Smart 		domain->domain_notify_pend = true;
5623146240fSJames Smart 
5633146240fSJames Smart 		/* Mark as attached */
5643146240fSJames Smart 		domain->attached = true;
5653146240fSJames Smart 
5663146240fSJames Smart 		/* Transition to ready */
5673146240fSJames Smart 		/* sm: / forward event to all nports and nodes */
5683146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_ready, NULL);
5693146240fSJames Smart 
5703146240fSJames Smart 		/* We have an FCFI, so we can accept frames */
5713146240fSJames Smart 		domain->req_accept_frames = true;
5723146240fSJames Smart 
5733146240fSJames Smart 		/*
5743146240fSJames Smart 		 * Notify all nodes that the domain attach request
5753146240fSJames Smart 		 * has completed
5763146240fSJames Smart 		 * Note: nport will have already received notification
5773146240fSJames Smart 		 * of nport attached as a result of the HW's port attach.
5783146240fSJames Smart 		 */
5793146240fSJames Smart 		list_for_each_entry_safe(nport, next_nport,
5803146240fSJames Smart 					 &domain->nport_list, list_entry) {
5813146240fSJames Smart 			xa_for_each(&nport->lookup, index, node) {
5823146240fSJames Smart 				efc_node_post_event(node,
5833146240fSJames Smart 						    EFC_EVT_DOMAIN_ATTACH_OK,
5843146240fSJames Smart 						    NULL);
5853146240fSJames Smart 			}
5863146240fSJames Smart 		}
5873146240fSJames Smart 		domain->domain_notify_pend = false;
5883146240fSJames Smart 		break;
5893146240fSJames Smart 	}
5903146240fSJames Smart 
5913146240fSJames Smart 	case EFC_EVT_DOMAIN_ATTACH_FAIL:
5923146240fSJames Smart 		efc_log_debug(efc,
5933146240fSJames Smart 			      "%s received while waiting for hw attach\n",
5943146240fSJames Smart 			      efc_sm_event_name(evt));
5953146240fSJames Smart 		break;
5963146240fSJames Smart 
5973146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND:
5983146240fSJames Smart 		/* Should not happen */
5993146240fSJames Smart 		efc_log_err(efc, "%s: evt: %d should not happen\n",
6003146240fSJames Smart 			    __func__, evt);
6013146240fSJames Smart 		break;
6023146240fSJames Smart 
6033146240fSJames Smart 	case EFC_EVT_DOMAIN_LOST:
6043146240fSJames Smart 		/*
6053146240fSJames Smart 		 * Domain lost while waiting for an attach to complete,
6063146240fSJames Smart 		 * go to a state that waits for  the domain attach to
6073146240fSJames Smart 		 * complete, then handle domain lost
6083146240fSJames Smart 		 */
6093146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
6103146240fSJames Smart 		break;
6113146240fSJames Smart 
6123146240fSJames Smart 	case EFC_EVT_DOMAIN_REQ_ATTACH:
6133146240fSJames Smart 		/*
6143146240fSJames Smart 		 * In P2P we can get an attach request from
6153146240fSJames Smart 		 * the other FLOGI path, so drop this one
6163146240fSJames Smart 		 */
6173146240fSJames Smart 		break;
6183146240fSJames Smart 
6193146240fSJames Smart 	default:
6203146240fSJames Smart 		__efc_domain_common(__func__, ctx, evt, arg);
6213146240fSJames Smart 	}
6223146240fSJames Smart }
6233146240fSJames Smart 
6243146240fSJames Smart void
__efc_domain_ready(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)6253146240fSJames Smart __efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
6263146240fSJames Smart {
6273146240fSJames Smart 	std_domain_state_decl();
6283146240fSJames Smart 
6293146240fSJames Smart 	domain_sm_trace(domain);
6303146240fSJames Smart 
6313146240fSJames Smart 	switch (evt) {
6323146240fSJames Smart 	case EFC_EVT_ENTER: {
6333146240fSJames Smart 		/* start any pending vports */
6343146240fSJames Smart 		if (efc_vport_start(domain)) {
6353146240fSJames Smart 			efc_log_debug(domain->efc,
6363146240fSJames Smart 				      "efc_vport_start didn't start vports\n");
6373146240fSJames Smart 		}
6383146240fSJames Smart 		break;
6393146240fSJames Smart 	}
6403146240fSJames Smart 	case EFC_EVT_DOMAIN_LOST: {
6413146240fSJames Smart 		if (!list_empty(&domain->nport_list)) {
6423146240fSJames Smart 			/*
6433146240fSJames Smart 			 * if there are nports, transition to wait state
6443146240fSJames Smart 			 * and send shutdown to each nport
6453146240fSJames Smart 			 */
6463146240fSJames Smart 			struct efc_nport *nport = NULL, *nport_next = NULL;
6473146240fSJames Smart 
6483146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
6493146240fSJames Smart 					  NULL);
6503146240fSJames Smart 			list_for_each_entry_safe(nport, nport_next,
6513146240fSJames Smart 						 &domain->nport_list,
6523146240fSJames Smart 						 list_entry) {
6533146240fSJames Smart 				efc_sm_post_event(&nport->sm,
6543146240fSJames Smart 						  EFC_EVT_SHUTDOWN, NULL);
6553146240fSJames Smart 			}
6563146240fSJames Smart 		} else {
6573146240fSJames Smart 			/* no nports exist, free domain */
6583146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
6593146240fSJames Smart 					  NULL);
6603146240fSJames Smart 			if (efc_cmd_domain_free(efc, domain))
6613146240fSJames Smart 				efc_log_err(efc, "hw_domain_free failed\n");
6623146240fSJames Smart 		}
6633146240fSJames Smart 		break;
6643146240fSJames Smart 	}
6653146240fSJames Smart 
6663146240fSJames Smart 	case EFC_EVT_DOMAIN_FOUND:
6673146240fSJames Smart 		/* Should not happen */
6683146240fSJames Smart 		efc_log_err(efc, "%s: evt: %d should not happen\n",
6693146240fSJames Smart 			    __func__, evt);
6703146240fSJames Smart 		break;
6713146240fSJames Smart 
6723146240fSJames Smart 	case EFC_EVT_DOMAIN_REQ_ATTACH: {
6733146240fSJames Smart 		/* can happen during p2p */
6743146240fSJames Smart 		u32 fc_id;
6753146240fSJames Smart 
6763146240fSJames Smart 		fc_id = *((u32 *)arg);
6773146240fSJames Smart 
6783146240fSJames Smart 		/* Assume that the domain is attached */
6793146240fSJames Smart 		WARN_ON(!domain->attached);
6803146240fSJames Smart 
6813146240fSJames Smart 		/*
6823146240fSJames Smart 		 * Verify that the requested FC_ID
6833146240fSJames Smart 		 * is the same as the one we're working with
6843146240fSJames Smart 		 */
6853146240fSJames Smart 		WARN_ON(domain->nport->fc_id != fc_id);
6863146240fSJames Smart 		break;
6873146240fSJames Smart 	}
6883146240fSJames Smart 
6893146240fSJames Smart 	default:
6903146240fSJames Smart 		__efc_domain_common(__func__, ctx, evt, arg);
6913146240fSJames Smart 	}
6923146240fSJames Smart }
6933146240fSJames Smart 
6943146240fSJames Smart void
__efc_domain_wait_nports_free(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)6953146240fSJames Smart __efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
6963146240fSJames Smart 			      void *arg)
6973146240fSJames Smart {
6983146240fSJames Smart 	std_domain_state_decl();
6993146240fSJames Smart 
7003146240fSJames Smart 	domain_sm_trace(domain);
7013146240fSJames Smart 
7023146240fSJames Smart 	/* Wait for nodes to free prior to the domain shutdown */
7033146240fSJames Smart 	switch (evt) {
7043146240fSJames Smart 	case EFC_EVT_ALL_CHILD_NODES_FREE: {
7053146240fSJames Smart 		int rc;
7063146240fSJames Smart 
7073146240fSJames Smart 		/* sm: / efc_hw_domain_free */
7083146240fSJames Smart 		efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);
7093146240fSJames Smart 
7103146240fSJames Smart 		/* Request efc_hw_domain_free and wait for completion */
7113146240fSJames Smart 		rc = efc_cmd_domain_free(efc, domain);
7123146240fSJames Smart 		if (rc) {
7133146240fSJames Smart 			efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",
7143146240fSJames Smart 				    rc);
7153146240fSJames Smart 		}
7163146240fSJames Smart 		break;
7173146240fSJames Smart 	}
7183146240fSJames Smart 	default:
7193146240fSJames Smart 		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
7203146240fSJames Smart 	}
7213146240fSJames Smart }
7223146240fSJames Smart 
7233146240fSJames Smart void
__efc_domain_wait_shutdown(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)7243146240fSJames Smart __efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,
7253146240fSJames Smart 			   enum efc_sm_event evt, void *arg)
7263146240fSJames Smart {
7273146240fSJames Smart 	std_domain_state_decl();
7283146240fSJames Smart 
7293146240fSJames Smart 	domain_sm_trace(domain);
7303146240fSJames Smart 
7313146240fSJames Smart 	switch (evt) {
7323146240fSJames Smart 	case EFC_EVT_DOMAIN_FREE_OK:
7333146240fSJames Smart 		/* sm: / domain_free */
7343146240fSJames Smart 		if (domain->domain_found_pending) {
7353146240fSJames Smart 			/*
7363146240fSJames Smart 			 * save fcf_wwn and drec from this domain,
7373146240fSJames Smart 			 * free current domain and allocate
7383146240fSJames Smart 			 * a new one with the same fcf_wwn
7393146240fSJames Smart 			 * could use a SLI-4 "re-register VPI"
7403146240fSJames Smart 			 * operation here?
7413146240fSJames Smart 			 */
7423146240fSJames Smart 			u64 fcf_wwn = domain->fcf_wwn;
7433146240fSJames Smart 			struct efc_domain_record drec = domain->pending_drec;
7443146240fSJames Smart 
7453146240fSJames Smart 			efc_log_debug(efc, "Reallocating domain\n");
7463146240fSJames Smart 			domain->req_domain_free = true;
7473146240fSJames Smart 			domain = efc_domain_alloc(efc, fcf_wwn);
7483146240fSJames Smart 
7493146240fSJames Smart 			if (!domain) {
7503146240fSJames Smart 				efc_log_err(efc,
7513146240fSJames Smart 					    "efc_domain_alloc() failed\n");
7523146240fSJames Smart 				return;
7533146240fSJames Smart 			}
7543146240fSJames Smart 			/*
7553146240fSJames Smart 			 * got a new domain; at this point,
7563146240fSJames Smart 			 * there are at least two domains
7573146240fSJames Smart 			 * once the req_domain_free flag is processed,
7583146240fSJames Smart 			 * the associated domain will be removed.
7593146240fSJames Smart 			 */
7603146240fSJames Smart 			efc_sm_transition(&domain->drvsm, __efc_domain_init,
7613146240fSJames Smart 					  NULL);
7623146240fSJames Smart 			efc_sm_post_event(&domain->drvsm,
7633146240fSJames Smart 					  EFC_EVT_DOMAIN_FOUND, &drec);
7643146240fSJames Smart 		} else {
7653146240fSJames Smart 			domain->req_domain_free = true;
7663146240fSJames Smart 		}
7673146240fSJames Smart 		break;
7683146240fSJames Smart 	default:
7693146240fSJames Smart 		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
7703146240fSJames Smart 	}
7713146240fSJames Smart }
7723146240fSJames Smart 
7733146240fSJames Smart void
__efc_domain_wait_domain_lost(struct efc_sm_ctx * ctx,enum efc_sm_event evt,void * arg)7743146240fSJames Smart __efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,
7753146240fSJames Smart 			      enum efc_sm_event evt, void *arg)
7763146240fSJames Smart {
7773146240fSJames Smart 	std_domain_state_decl();
7783146240fSJames Smart 
7793146240fSJames Smart 	domain_sm_trace(domain);
7803146240fSJames Smart 
7813146240fSJames Smart 	/*
7823146240fSJames Smart 	 * Wait for the domain alloc/attach completion
7833146240fSJames Smart 	 * after receiving a domain lost.
7843146240fSJames Smart 	 */
7853146240fSJames Smart 	switch (evt) {
7863146240fSJames Smart 	case EFC_EVT_DOMAIN_ALLOC_OK:
7873146240fSJames Smart 	case EFC_EVT_DOMAIN_ATTACH_OK: {
7883146240fSJames Smart 		if (!list_empty(&domain->nport_list)) {
7893146240fSJames Smart 			/*
7903146240fSJames Smart 			 * if there are nports, transition to
7913146240fSJames Smart 			 * wait state and send shutdown to each nport
7923146240fSJames Smart 			 */
7933146240fSJames Smart 			struct efc_nport *nport = NULL, *nport_next = NULL;
7943146240fSJames Smart 
7953146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
7963146240fSJames Smart 					  NULL);
7973146240fSJames Smart 			list_for_each_entry_safe(nport, nport_next,
7983146240fSJames Smart 						 &domain->nport_list,
7993146240fSJames Smart 						 list_entry) {
8003146240fSJames Smart 				efc_sm_post_event(&nport->sm,
8013146240fSJames Smart 						  EFC_EVT_SHUTDOWN, NULL);
8023146240fSJames Smart 			}
8033146240fSJames Smart 		} else {
8043146240fSJames Smart 			/* no nports exist, free domain */
8053146240fSJames Smart 			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
8063146240fSJames Smart 					  NULL);
8073146240fSJames Smart 			if (efc_cmd_domain_free(efc, domain))
8083146240fSJames Smart 				efc_log_err(efc, "hw_domain_free() failed\n");
8093146240fSJames Smart 		}
8103146240fSJames Smart 		break;
8113146240fSJames Smart 	}
8123146240fSJames Smart 	case EFC_EVT_DOMAIN_ALLOC_FAIL:
8133146240fSJames Smart 	case EFC_EVT_DOMAIN_ATTACH_FAIL:
8143146240fSJames Smart 		efc_log_err(efc, "[domain] %-20s: failed\n",
8153146240fSJames Smart 			    efc_sm_event_name(evt));
8163146240fSJames Smart 		break;
8173146240fSJames Smart 
8183146240fSJames Smart 	default:
8193146240fSJames Smart 		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
8203146240fSJames Smart 	}
8213146240fSJames Smart }
8223146240fSJames Smart 
8233146240fSJames Smart void
__efc_domain_attach_internal(struct efc_domain * domain,u32 s_id)8243146240fSJames Smart __efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)
8253146240fSJames Smart {
8263146240fSJames Smart 	memcpy(domain->dma.virt,
8273146240fSJames Smart 	       ((uint8_t *)domain->flogi_service_params) + 4,
8283146240fSJames Smart 		   sizeof(struct fc_els_flogi) - 4);
8293146240fSJames Smart 	(void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,
8303146240fSJames Smart 				 &s_id);
8313146240fSJames Smart }
8323146240fSJames Smart 
8333146240fSJames Smart void
efc_domain_attach(struct efc_domain * domain,u32 s_id)8343146240fSJames Smart efc_domain_attach(struct efc_domain *domain, u32 s_id)
8353146240fSJames Smart {
8363146240fSJames Smart 	__efc_domain_attach_internal(domain, s_id);
8373146240fSJames Smart }
8383146240fSJames Smart 
8393146240fSJames Smart int
efc_domain_post_event(struct efc_domain * domain,enum efc_sm_event event,void * arg)8403146240fSJames Smart efc_domain_post_event(struct efc_domain *domain,
8413146240fSJames Smart 		      enum efc_sm_event event, void *arg)
8423146240fSJames Smart {
8433146240fSJames Smart 	int rc;
8443146240fSJames Smart 	bool req_domain_free;
8453146240fSJames Smart 
8463146240fSJames Smart 	rc = efc_sm_post_event(&domain->drvsm, event, arg);
8473146240fSJames Smart 
8483146240fSJames Smart 	req_domain_free = domain->req_domain_free;
8493146240fSJames Smart 	domain->req_domain_free = false;
8503146240fSJames Smart 
8513146240fSJames Smart 	if (req_domain_free)
8523146240fSJames Smart 		efc_domain_free(domain);
8533146240fSJames Smart 
8543146240fSJames Smart 	return rc;
8553146240fSJames Smart }
8563146240fSJames Smart 
8573146240fSJames Smart static void
efct_domain_process_pending(struct efc_domain * domain)8583146240fSJames Smart efct_domain_process_pending(struct efc_domain *domain)
8593146240fSJames Smart {
8603146240fSJames Smart 	struct efc *efc = domain->efc;
8613146240fSJames Smart 	struct efc_hw_sequence *seq = NULL;
8623146240fSJames Smart 	u32 processed = 0;
8633146240fSJames Smart 	unsigned long flags = 0;
8643146240fSJames Smart 
8653146240fSJames Smart 	for (;;) {
8663146240fSJames Smart 		/* need to check for hold frames condition after each frame
8673146240fSJames Smart 		 * processed because any given frame could cause a transition
8683146240fSJames Smart 		 * to a state that holds frames
8693146240fSJames Smart 		 */
8703146240fSJames Smart 		if (efc->hold_frames)
8713146240fSJames Smart 			break;
8723146240fSJames Smart 
8733146240fSJames Smart 		/* Get next frame/sequence */
8743146240fSJames Smart 		spin_lock_irqsave(&efc->pend_frames_lock, flags);
8753146240fSJames Smart 
8763146240fSJames Smart 		if (!list_empty(&efc->pend_frames)) {
8773146240fSJames Smart 			seq = list_first_entry(&efc->pend_frames,
8783146240fSJames Smart 					struct efc_hw_sequence, list_entry);
8793146240fSJames Smart 			list_del(&seq->list_entry);
8803146240fSJames Smart 		}
8813146240fSJames Smart 
8823146240fSJames Smart 		if (!seq) {
8833146240fSJames Smart 			processed = efc->pend_frames_processed;
8843146240fSJames Smart 			efc->pend_frames_processed = 0;
8853146240fSJames Smart 			spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
8863146240fSJames Smart 			break;
8873146240fSJames Smart 		}
8883146240fSJames Smart 		efc->pend_frames_processed++;
8893146240fSJames Smart 
8903146240fSJames Smart 		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
8913146240fSJames Smart 
8923146240fSJames Smart 		/* now dispatch frame(s) to dispatch function */
8933146240fSJames Smart 		if (efc_domain_dispatch_frame(domain, seq))
8943146240fSJames Smart 			efc->tt.hw_seq_free(efc, seq);
8953146240fSJames Smart 
8963146240fSJames Smart 		seq = NULL;
8973146240fSJames Smart 	}
8983146240fSJames Smart 
8993146240fSJames Smart 	if (processed != 0)
9003146240fSJames Smart 		efc_log_debug(efc, "%u domain frames held and processed\n",
9013146240fSJames Smart 			      processed);
9023146240fSJames Smart }
9033146240fSJames Smart 
9043146240fSJames Smart void
efc_dispatch_frame(struct efc * efc,struct efc_hw_sequence * seq)9053146240fSJames Smart efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)
9063146240fSJames Smart {
9073146240fSJames Smart 	struct efc_domain *domain = efc->domain;
9083146240fSJames Smart 
9093146240fSJames Smart 	/*
9103146240fSJames Smart 	 * If we are holding frames or the domain is not yet registered or
9113146240fSJames Smart 	 * there's already frames on the pending list,
9123146240fSJames Smart 	 * then add the new frame to pending list
9133146240fSJames Smart 	 */
9143146240fSJames Smart 	if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {
9153146240fSJames Smart 		unsigned long flags = 0;
9163146240fSJames Smart 
9173146240fSJames Smart 		spin_lock_irqsave(&efc->pend_frames_lock, flags);
9183146240fSJames Smart 		INIT_LIST_HEAD(&seq->list_entry);
9193146240fSJames Smart 		list_add_tail(&seq->list_entry, &efc->pend_frames);
9203146240fSJames Smart 		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
9213146240fSJames Smart 
9223146240fSJames Smart 		if (domain) {
9233146240fSJames Smart 			/* immediately process pending frames */
9243146240fSJames Smart 			efct_domain_process_pending(domain);
9253146240fSJames Smart 		}
9263146240fSJames Smart 	} else {
9273146240fSJames Smart 		/*
9283146240fSJames Smart 		 * We are not holding frames and pending list is empty,
9293146240fSJames Smart 		 * just process frame. A non-zero return means the frame
9303146240fSJames Smart 		 * was not handled - so cleanup
9313146240fSJames Smart 		 */
9323146240fSJames Smart 		if (efc_domain_dispatch_frame(domain, seq))
9333146240fSJames Smart 			efc->tt.hw_seq_free(efc, seq);
9343146240fSJames Smart 	}
9353146240fSJames Smart }
9363146240fSJames Smart 
9373146240fSJames Smart int
efc_domain_dispatch_frame(void * arg,struct efc_hw_sequence * seq)9383146240fSJames Smart efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
9393146240fSJames Smart {
9403146240fSJames Smart 	struct efc_domain *domain = (struct efc_domain *)arg;
9413146240fSJames Smart 	struct efc *efc = domain->efc;
9423146240fSJames Smart 	struct fc_frame_header *hdr;
9433146240fSJames Smart 	struct efc_node *node = NULL;
9443146240fSJames Smart 	struct efc_nport *nport = NULL;
9453146240fSJames Smart 	unsigned long flags = 0;
9463146240fSJames Smart 	u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;
9473146240fSJames Smart 
9483146240fSJames Smart 	if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {
9493146240fSJames Smart 		efc_log_err(efc, "Sequence header or payload is null\n");
9503146240fSJames Smart 		return rc;
9513146240fSJames Smart 	}
9523146240fSJames Smart 
9533146240fSJames Smart 	hdr = seq->header->dma.virt;
9543146240fSJames Smart 
9553146240fSJames Smart 	/* extract the s_id and d_id */
9563146240fSJames Smart 	s_id = ntoh24(hdr->fh_s_id);
9573146240fSJames Smart 	d_id = ntoh24(hdr->fh_d_id);
9583146240fSJames Smart 
9593146240fSJames Smart 	spin_lock_irqsave(&efc->lock, flags);
9603146240fSJames Smart 
9613146240fSJames Smart 	nport = efc_nport_find(domain, d_id);
9623146240fSJames Smart 	if (!nport) {
9633146240fSJames Smart 		if (hdr->fh_type == FC_TYPE_FCP) {
9643146240fSJames Smart 			/* Drop frame */
9653146240fSJames Smart 			efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",
9663146240fSJames Smart 				     d_id);
9673146240fSJames Smart 			goto out;
9683146240fSJames Smart 		}
9693146240fSJames Smart 
9703146240fSJames Smart 		/* p2p will use this case */
9713146240fSJames Smart 		nport = domain->nport;
9723146240fSJames Smart 		if (!nport || !kref_get_unless_zero(&nport->ref)) {
9733146240fSJames Smart 			efc_log_err(efc, "Physical nport is NULL\n");
9743146240fSJames Smart 			goto out;
9753146240fSJames Smart 		}
9763146240fSJames Smart 	}
9773146240fSJames Smart 
9783146240fSJames Smart 	/* Lookup the node given the remote s_id */
9793146240fSJames Smart 	node = efc_node_find(nport, s_id);
9803146240fSJames Smart 
9813146240fSJames Smart 	/* If not found, then create a new node */
9823146240fSJames Smart 	if (!node) {
9833146240fSJames Smart 		/*
9843146240fSJames Smart 		 * If this is solicited data or control based on R_CTL and
9853146240fSJames Smart 		 * there is no node context, then we can drop the frame
9863146240fSJames Smart 		 */
9873146240fSJames Smart 		if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||
9883146240fSJames Smart 		    (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {
9893146240fSJames Smart 			efc_log_debug(efc, "sol data/ctrl frame without node\n");
9903146240fSJames Smart 			goto out_release;
9913146240fSJames Smart 		}
9923146240fSJames Smart 
9933146240fSJames Smart 		node = efc_node_alloc(nport, s_id, false, false);
9943146240fSJames Smart 		if (!node) {
9953146240fSJames Smart 			efc_log_err(efc, "efc_node_alloc() failed\n");
9963146240fSJames Smart 			goto out_release;
9973146240fSJames Smart 		}
9983146240fSJames Smart 		/* don't send PLOGI on efc_d_init entry */
9993146240fSJames Smart 		efc_node_init_device(node, false);
10003146240fSJames Smart 	}
10013146240fSJames Smart 
10023146240fSJames Smart 	if (node->hold_frames || !list_empty(&node->pend_frames)) {
10033146240fSJames Smart 		/* add frame to node's pending list */
1004*7cca85dfSDan Carpenter 		spin_lock(&node->pend_frames_lock);
10053146240fSJames Smart 		INIT_LIST_HEAD(&seq->list_entry);
10063146240fSJames Smart 		list_add_tail(&seq->list_entry, &node->pend_frames);
1007*7cca85dfSDan Carpenter 		spin_unlock(&node->pend_frames_lock);
10083146240fSJames Smart 		rc = EFC_HW_SEQ_HOLD;
10093146240fSJames Smart 		goto out_release;
10103146240fSJames Smart 	}
10113146240fSJames Smart 
10123146240fSJames Smart 	/* now dispatch frame to the node frame handler */
10133146240fSJames Smart 	efc_node_dispatch_frame(node, seq);
10143146240fSJames Smart 
10153146240fSJames Smart out_release:
10163146240fSJames Smart 	kref_put(&nport->ref, nport->release);
10173146240fSJames Smart out:
10183146240fSJames Smart 	spin_unlock_irqrestore(&efc->lock, flags);
10193146240fSJames Smart 	return rc;
10203146240fSJames Smart }
10213146240fSJames Smart 
10223146240fSJames Smart void
efc_node_dispatch_frame(void * arg,struct efc_hw_sequence * seq)10233146240fSJames Smart efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
10243146240fSJames Smart {
10253146240fSJames Smart 	struct fc_frame_header *hdr = seq->header->dma.virt;
10263146240fSJames Smart 	u32 port_id;
10273146240fSJames Smart 	struct efc_node *node = (struct efc_node *)arg;
10283146240fSJames Smart 	struct efc *efc = node->efc;
10293146240fSJames Smart 
10303146240fSJames Smart 	port_id = ntoh24(hdr->fh_s_id);
10313146240fSJames Smart 
10323146240fSJames Smart 	if (WARN_ON(port_id != node->rnode.fc_id))
10333146240fSJames Smart 		return;
10343146240fSJames Smart 
10353146240fSJames Smart 	if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||
10363146240fSJames Smart 	    !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {
10373146240fSJames Smart 		node_printf(node,
10383146240fSJames Smart 			    "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",
10393146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[0]),
10403146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[1]),
10413146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[2]),
10423146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[3]),
10433146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[4]),
10443146240fSJames Smart 			    cpu_to_be32(((u32 *)hdr)[5]));
10453146240fSJames Smart 		return;
10463146240fSJames Smart 	}
10473146240fSJames Smart 
10483146240fSJames Smart 	switch (hdr->fh_r_ctl) {
10493146240fSJames Smart 	case FC_RCTL_ELS_REQ:
10503146240fSJames Smart 	case FC_RCTL_ELS_REP:
10513146240fSJames Smart 		efc_node_recv_els_frame(node, seq);
10523146240fSJames Smart 		break;
10533146240fSJames Smart 
10543146240fSJames Smart 	case FC_RCTL_BA_ABTS:
10553146240fSJames Smart 	case FC_RCTL_BA_ACC:
10563146240fSJames Smart 	case FC_RCTL_BA_RJT:
10573146240fSJames Smart 	case FC_RCTL_BA_NOP:
10583146240fSJames Smart 		efc_log_err(efc, "Received ABTS:\n");
10593146240fSJames Smart 		break;
10603146240fSJames Smart 
10613146240fSJames Smart 	case FC_RCTL_DD_UNSOL_CMD:
10623146240fSJames Smart 	case FC_RCTL_DD_UNSOL_CTL:
10633146240fSJames Smart 		switch (hdr->fh_type) {
10643146240fSJames Smart 		case FC_TYPE_FCP:
10653146240fSJames Smart 			if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {
10663146240fSJames Smart 				if (!node->fcp_enabled) {
10673146240fSJames Smart 					efc_node_recv_fcp_cmd(node, seq);
10683146240fSJames Smart 					break;
10693146240fSJames Smart 				}
10703146240fSJames Smart 				efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");
10713146240fSJames Smart 			} else if ((hdr->fh_r_ctl & 0xf) ==
10723146240fSJames Smart 							FC_RCTL_DD_SOL_DATA) {
10733146240fSJames Smart 				node_printf(node,
10743146240fSJames Smart 					    "solicited data recvd. Drop IO\n");
10753146240fSJames Smart 			}
10763146240fSJames Smart 			break;
10773146240fSJames Smart 
10783146240fSJames Smart 		case FC_TYPE_CT:
10793146240fSJames Smart 			efc_node_recv_ct_frame(node, seq);
10803146240fSJames Smart 			break;
10813146240fSJames Smart 		default:
10823146240fSJames Smart 			break;
10833146240fSJames Smart 		}
10843146240fSJames Smart 		break;
10853146240fSJames Smart 	default:
10863146240fSJames Smart 		efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);
10873146240fSJames Smart 	}
10883146240fSJames Smart }
1089