162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
462306a36Sopenharmony_ci * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * domain_sm Domain State Machine: States
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "efc.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciint
1462306a36Sopenharmony_ciefc_domain_cb(void *arg, int event, void *data)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct efc *efc = arg;
1762306a36Sopenharmony_ci	struct efc_domain *domain = NULL;
1862306a36Sopenharmony_ci	int rc = 0;
1962306a36Sopenharmony_ci	unsigned long flags = 0;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (event != EFC_HW_DOMAIN_FOUND)
2262306a36Sopenharmony_ci		domain = data;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/* Accept domain callback events from the user driver */
2562306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
2662306a36Sopenharmony_ci	switch (event) {
2762306a36Sopenharmony_ci	case EFC_HW_DOMAIN_FOUND: {
2862306a36Sopenharmony_ci		u64 fcf_wwn = 0;
2962306a36Sopenharmony_ci		struct efc_domain_record *drec = data;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci		/* extract the fcf_wwn */
3262306a36Sopenharmony_ci		fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		/* lookup domain, or allocate a new one */
3762306a36Sopenharmony_ci		domain = efc->domain;
3862306a36Sopenharmony_ci		if (!domain) {
3962306a36Sopenharmony_ci			domain = efc_domain_alloc(efc, fcf_wwn);
4062306a36Sopenharmony_ci			if (!domain) {
4162306a36Sopenharmony_ci				efc_log_err(efc, "efc_domain_alloc() failed\n");
4262306a36Sopenharmony_ci				rc = -1;
4362306a36Sopenharmony_ci				break;
4462306a36Sopenharmony_ci			}
4562306a36Sopenharmony_ci			efc_sm_transition(&domain->drvsm, __efc_domain_init,
4662306a36Sopenharmony_ci					  NULL);
4762306a36Sopenharmony_ci		}
4862306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	case EFC_HW_DOMAIN_LOST:
5362306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
5462306a36Sopenharmony_ci		efc->hold_frames = true;
5562306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	case EFC_HW_DOMAIN_ALLOC_OK:
5962306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
6062306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
6162306a36Sopenharmony_ci		break;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	case EFC_HW_DOMAIN_ALLOC_FAIL:
6462306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
6562306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
6662306a36Sopenharmony_ci				      NULL);
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	case EFC_HW_DOMAIN_ATTACH_OK:
7062306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
7162306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	case EFC_HW_DOMAIN_ATTACH_FAIL:
7562306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
7662306a36Sopenharmony_ci		efc_domain_post_event(domain,
7762306a36Sopenharmony_ci				      EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	case EFC_HW_DOMAIN_FREE_OK:
8162306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
8262306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	case EFC_HW_DOMAIN_FREE_FAIL:
8662306a36Sopenharmony_ci		domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
8762306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	default:
9162306a36Sopenharmony_ci		efc_log_warn(efc, "unsupported event %#x\n", event);
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (efc->domain && domain->req_accept_frames) {
9662306a36Sopenharmony_ci		domain->req_accept_frames = false;
9762306a36Sopenharmony_ci		efc->hold_frames = false;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return rc;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void
10462306a36Sopenharmony_ci_efc_domain_free(struct kref *arg)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
10762306a36Sopenharmony_ci	struct efc *efc = domain->efc;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (efc->domain_free_cb)
11062306a36Sopenharmony_ci		(*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	kfree(domain);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid
11662306a36Sopenharmony_ciefc_domain_free(struct efc_domain *domain)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct efc *efc;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	efc = domain->efc;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Hold frames to clear the domain pointer from the xport lookup */
12362306a36Sopenharmony_ci	efc->hold_frames = false;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	xa_destroy(&domain->lookup);
12862306a36Sopenharmony_ci	efc->domain = NULL;
12962306a36Sopenharmony_ci	kref_put(&domain->ref, domain->release);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct efc_domain *
13362306a36Sopenharmony_ciefc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct efc_domain *domain;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
13862306a36Sopenharmony_ci	if (!domain)
13962306a36Sopenharmony_ci		return NULL;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	domain->efc = efc;
14262306a36Sopenharmony_ci	domain->drvsm.app = domain;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* initialize refcount */
14562306a36Sopenharmony_ci	kref_init(&domain->ref);
14662306a36Sopenharmony_ci	domain->release = _efc_domain_free;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	xa_init(&domain->lookup);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	INIT_LIST_HEAD(&domain->nport_list);
15162306a36Sopenharmony_ci	efc->domain = domain;
15262306a36Sopenharmony_ci	domain->fcf_wwn = fcf_wwn;
15362306a36Sopenharmony_ci	efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return domain;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_civoid
15962306a36Sopenharmony_ciefc_register_domain_free_cb(struct efc *efc,
16062306a36Sopenharmony_ci			    void (*callback)(struct efc *efc, void *arg),
16162306a36Sopenharmony_ci			    void *arg)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	/* Register a callback to be called when the domain is freed */
16462306a36Sopenharmony_ci	efc->domain_free_cb = callback;
16562306a36Sopenharmony_ci	efc->domain_free_cb_arg = arg;
16662306a36Sopenharmony_ci	if (!efc->domain && callback)
16762306a36Sopenharmony_ci		(*callback)(efc, arg);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void
17162306a36Sopenharmony_ci__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,
17262306a36Sopenharmony_ci		    enum efc_sm_event evt, void *arg)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct efc_domain *domain = ctx->app;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (evt) {
17762306a36Sopenharmony_ci	case EFC_EVT_ENTER:
17862306a36Sopenharmony_ci	case EFC_EVT_REENTER:
17962306a36Sopenharmony_ci	case EFC_EVT_EXIT:
18062306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE:
18162306a36Sopenharmony_ci		/*
18262306a36Sopenharmony_ci		 * this can arise if an FLOGI fails on the NPORT,
18362306a36Sopenharmony_ci		 * and the NPORT is shutdown
18462306a36Sopenharmony_ci		 */
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci	default:
18762306a36Sopenharmony_ci		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
18862306a36Sopenharmony_ci			     funcname, efc_sm_event_name(evt));
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void
19362306a36Sopenharmony_ci__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,
19462306a36Sopenharmony_ci			     enum efc_sm_event evt, void *arg)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct efc_domain *domain = ctx->app;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	switch (evt) {
19962306a36Sopenharmony_ci	case EFC_EVT_ENTER:
20062306a36Sopenharmony_ci	case EFC_EVT_REENTER:
20162306a36Sopenharmony_ci	case EFC_EVT_EXIT:
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND:
20462306a36Sopenharmony_ci		/* save drec, mark domain_found_pending */
20562306a36Sopenharmony_ci		memcpy(&domain->pending_drec, arg,
20662306a36Sopenharmony_ci		       sizeof(domain->pending_drec));
20762306a36Sopenharmony_ci		domain->domain_found_pending = true;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_LOST:
21062306a36Sopenharmony_ci		/* unmark domain_found_pending */
21162306a36Sopenharmony_ci		domain->domain_found_pending = false;
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	default:
21562306a36Sopenharmony_ci		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
21662306a36Sopenharmony_ci			     funcname, efc_sm_event_name(evt));
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci#define std_domain_state_decl(...)\
22162306a36Sopenharmony_ci	struct efc_domain *domain = NULL;\
22262306a36Sopenharmony_ci	struct efc *efc = NULL;\
22362306a36Sopenharmony_ci	\
22462306a36Sopenharmony_ci	WARN_ON(!ctx || !ctx->app);\
22562306a36Sopenharmony_ci	domain = ctx->app;\
22662306a36Sopenharmony_ci	WARN_ON(!domain->efc);\
22762306a36Sopenharmony_ci	efc = domain->efc
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_civoid
23062306a36Sopenharmony_ci__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
23162306a36Sopenharmony_ci		  void *arg)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	std_domain_state_decl();
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	domain_sm_trace(domain);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	switch (evt) {
23862306a36Sopenharmony_ci	case EFC_EVT_ENTER:
23962306a36Sopenharmony_ci		domain->attached = false;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND: {
24362306a36Sopenharmony_ci		u32	i;
24462306a36Sopenharmony_ci		struct efc_domain_record *drec = arg;
24562306a36Sopenharmony_ci		struct efc_nport *nport;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		u64 my_wwnn = efc->req_wwnn;
24862306a36Sopenharmony_ci		u64 my_wwpn = efc->req_wwpn;
24962306a36Sopenharmony_ci		__be64 bewwpn;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		if (my_wwpn == 0 || my_wwnn == 0) {
25262306a36Sopenharmony_ci			efc_log_debug(efc, "using default hardware WWN config\n");
25362306a36Sopenharmony_ci			my_wwpn = efc->def_wwpn;
25462306a36Sopenharmony_ci			my_wwnn = efc->def_wwnn;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",
25862306a36Sopenharmony_ci			      my_wwpn, my_wwnn);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		/* Allocate a nport and transition to __efc_nport_allocated */
26162306a36Sopenharmony_ci		nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,
26262306a36Sopenharmony_ci					efc->enable_ini, efc->enable_tgt);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (!nport) {
26562306a36Sopenharmony_ci			efc_log_err(efc, "efc_nport_alloc() failed\n");
26662306a36Sopenharmony_ci			break;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci		efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		bewwpn = cpu_to_be64(nport->wwpn);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		/* allocate struct efc_nport object for local port
27362306a36Sopenharmony_ci		 * Note: drec->fc_id is ALPA from read_topology only if loop
27462306a36Sopenharmony_ci		 */
27562306a36Sopenharmony_ci		if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {
27662306a36Sopenharmony_ci			efc_log_err(efc, "Can't allocate port\n");
27762306a36Sopenharmony_ci			efc_nport_free(nport);
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		domain->is_loop = drec->is_loop;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/*
28462306a36Sopenharmony_ci		 * If the loop position map includes ALPA == 0,
28562306a36Sopenharmony_ci		 * then we are in a public loop (NL_PORT)
28662306a36Sopenharmony_ci		 * Note that the first element of the loopmap[]
28762306a36Sopenharmony_ci		 * contains the count of elements, and if
28862306a36Sopenharmony_ci		 * ALPA == 0 is present, it will occupy the first
28962306a36Sopenharmony_ci		 * location after the count.
29062306a36Sopenharmony_ci		 */
29162306a36Sopenharmony_ci		domain->is_nlport = drec->map.loop[1] == 0x00;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		if (!domain->is_loop) {
29462306a36Sopenharmony_ci			/* Initiate HW domain alloc */
29562306a36Sopenharmony_ci			if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
29662306a36Sopenharmony_ci				efc_log_err(efc,
29762306a36Sopenharmony_ci					    "Failed to initiate HW domain allocation\n");
29862306a36Sopenharmony_ci				break;
29962306a36Sopenharmony_ci			}
30062306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
30162306a36Sopenharmony_ci			break;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",
30562306a36Sopenharmony_ci			      drec->is_loop ?
30662306a36Sopenharmony_ci			      (domain->is_nlport ?
30762306a36Sopenharmony_ci			      "public-loop" : "loop") : "other",
30862306a36Sopenharmony_ci			      drec->fc_id, drec->speed);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		nport->fc_id = drec->fc_id;
31162306a36Sopenharmony_ci		nport->topology = EFC_NPORT_TOPO_FC_AL;
31262306a36Sopenharmony_ci		snprintf(nport->display_name, sizeof(nport->display_name),
31362306a36Sopenharmony_ci			 "s%06x", drec->fc_id);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (efc->enable_ini) {
31662306a36Sopenharmony_ci			u32 count = drec->map.loop[0];
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			efc_log_debug(efc, "%d position map entries\n",
31962306a36Sopenharmony_ci				      count);
32062306a36Sopenharmony_ci			for (i = 1; i <= count; i++) {
32162306a36Sopenharmony_ci				if (drec->map.loop[i] != drec->fc_id) {
32262306a36Sopenharmony_ci					struct efc_node *node;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci					efc_log_debug(efc, "%#x -> %#x\n",
32562306a36Sopenharmony_ci						      drec->fc_id,
32662306a36Sopenharmony_ci						      drec->map.loop[i]);
32762306a36Sopenharmony_ci					node = efc_node_alloc(nport,
32862306a36Sopenharmony_ci							      drec->map.loop[i],
32962306a36Sopenharmony_ci							      false, true);
33062306a36Sopenharmony_ci					if (!node) {
33162306a36Sopenharmony_ci						efc_log_err(efc,
33262306a36Sopenharmony_ci							    "efc_node_alloc() failed\n");
33362306a36Sopenharmony_ci						break;
33462306a36Sopenharmony_ci					}
33562306a36Sopenharmony_ci					efc_node_transition(node,
33662306a36Sopenharmony_ci							    __efc_d_wait_loop,
33762306a36Sopenharmony_ci							    NULL);
33862306a36Sopenharmony_ci				}
33962306a36Sopenharmony_ci			}
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		/* Initiate HW domain alloc */
34362306a36Sopenharmony_ci		if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
34462306a36Sopenharmony_ci			efc_log_err(efc,
34562306a36Sopenharmony_ci				    "Failed to initiate HW domain allocation\n");
34662306a36Sopenharmony_ci			break;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	default:
35262306a36Sopenharmony_ci		__efc_domain_common(__func__, ctx, evt, arg);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_civoid
35762306a36Sopenharmony_ci__efc_domain_wait_alloc(struct efc_sm_ctx *ctx,
35862306a36Sopenharmony_ci			enum efc_sm_event evt, void *arg)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	std_domain_state_decl();
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	domain_sm_trace(domain);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	switch (evt) {
36562306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ALLOC_OK: {
36662306a36Sopenharmony_ci		struct fc_els_flogi  *sp;
36762306a36Sopenharmony_ci		struct efc_nport *nport;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		nport = domain->nport;
37062306a36Sopenharmony_ci		if (WARN_ON(!nport))
37162306a36Sopenharmony_ci			return;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		sp = (struct fc_els_flogi  *)nport->service_params;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		/* Save the domain service parameters */
37662306a36Sopenharmony_ci		memcpy(domain->service_params + 4, domain->dma.virt,
37762306a36Sopenharmony_ci		       sizeof(struct fc_els_flogi) - 4);
37862306a36Sopenharmony_ci		memcpy(nport->service_params + 4, domain->dma.virt,
37962306a36Sopenharmony_ci		       sizeof(struct fc_els_flogi) - 4);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		/*
38262306a36Sopenharmony_ci		 * Update the nport's service parameters,
38362306a36Sopenharmony_ci		 * user might have specified non-default names
38462306a36Sopenharmony_ci		 */
38562306a36Sopenharmony_ci		sp->fl_wwpn = cpu_to_be64(nport->wwpn);
38662306a36Sopenharmony_ci		sp->fl_wwnn = cpu_to_be64(nport->wwnn);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		/*
38962306a36Sopenharmony_ci		 * Take the loop topology path,
39062306a36Sopenharmony_ci		 * unless we are an NL_PORT (public loop)
39162306a36Sopenharmony_ci		 */
39262306a36Sopenharmony_ci		if (domain->is_loop && !domain->is_nlport) {
39362306a36Sopenharmony_ci			/*
39462306a36Sopenharmony_ci			 * For loop, we already have our FC ID
39562306a36Sopenharmony_ci			 * and don't need fabric login.
39662306a36Sopenharmony_ci			 * Transition to the allocated state and
39762306a36Sopenharmony_ci			 * post an event to attach to
39862306a36Sopenharmony_ci			 * the domain. Note that this breaks the
39962306a36Sopenharmony_ci			 * normal action/transition
40062306a36Sopenharmony_ci			 * pattern here to avoid a race with the
40162306a36Sopenharmony_ci			 * domain attach callback.
40262306a36Sopenharmony_ci			 */
40362306a36Sopenharmony_ci			/* sm: is_loop / domain_attach */
40462306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_allocated, NULL);
40562306a36Sopenharmony_ci			__efc_domain_attach_internal(domain, nport->fc_id);
40662306a36Sopenharmony_ci			break;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci		{
40962306a36Sopenharmony_ci			struct efc_node *node;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci			/* alloc fabric node, send FLOGI */
41262306a36Sopenharmony_ci			node = efc_node_find(nport, FC_FID_FLOGI);
41362306a36Sopenharmony_ci			if (node) {
41462306a36Sopenharmony_ci				efc_log_err(efc,
41562306a36Sopenharmony_ci					    "Fabric Controller node already exists\n");
41662306a36Sopenharmony_ci				break;
41762306a36Sopenharmony_ci			}
41862306a36Sopenharmony_ci			node = efc_node_alloc(nport, FC_FID_FLOGI,
41962306a36Sopenharmony_ci					      false, false);
42062306a36Sopenharmony_ci			if (!node) {
42162306a36Sopenharmony_ci				efc_log_err(efc,
42262306a36Sopenharmony_ci					    "Error: efc_node_alloc() failed\n");
42362306a36Sopenharmony_ci			} else {
42462306a36Sopenharmony_ci				efc_node_transition(node,
42562306a36Sopenharmony_ci						    __efc_fabric_init, NULL);
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci			/* Accept frames */
42862306a36Sopenharmony_ci			domain->req_accept_frames = true;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		/* sm: / start fabric logins */
43162306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_allocated, NULL);
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ALLOC_FAIL:
43662306a36Sopenharmony_ci		efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",
43762306a36Sopenharmony_ci			    efc_sm_event_name(evt));
43862306a36Sopenharmony_ci		efc_log_err(efc, "shutting down domain\n");
43962306a36Sopenharmony_ci		domain->req_domain_free = true;
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND:
44362306a36Sopenharmony_ci		/* Should not happen */
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_LOST:
44762306a36Sopenharmony_ci		efc_log_debug(efc,
44862306a36Sopenharmony_ci			      "%s received while waiting for hw_domain_alloc()\n",
44962306a36Sopenharmony_ci			efc_sm_event_name(evt));
45062306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
45162306a36Sopenharmony_ci		break;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	default:
45462306a36Sopenharmony_ci		__efc_domain_common(__func__, ctx, evt, arg);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_civoid
45962306a36Sopenharmony_ci__efc_domain_allocated(struct efc_sm_ctx *ctx,
46062306a36Sopenharmony_ci		       enum efc_sm_event evt, void *arg)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	std_domain_state_decl();
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	domain_sm_trace(domain);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	switch (evt) {
46762306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_REQ_ATTACH: {
46862306a36Sopenharmony_ci		int rc = 0;
46962306a36Sopenharmony_ci		u32 fc_id;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (WARN_ON(!arg))
47262306a36Sopenharmony_ci			return;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		fc_id = *((u32 *)arg);
47562306a36Sopenharmony_ci		efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",
47662306a36Sopenharmony_ci			      fc_id);
47762306a36Sopenharmony_ci		/* Update nport lookup */
47862306a36Sopenharmony_ci		rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,
47962306a36Sopenharmony_ci				     GFP_ATOMIC));
48062306a36Sopenharmony_ci		if (rc) {
48162306a36Sopenharmony_ci			efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
48262306a36Sopenharmony_ci			return;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		/* Update display name for the nport */
48662306a36Sopenharmony_ci		efc_node_fcid_display(fc_id, domain->nport->display_name,
48762306a36Sopenharmony_ci				      sizeof(domain->nport->display_name));
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		/* Issue domain attach call */
49062306a36Sopenharmony_ci		rc = efc_cmd_domain_attach(efc, domain, fc_id);
49162306a36Sopenharmony_ci		if (rc) {
49262306a36Sopenharmony_ci			efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",
49362306a36Sopenharmony_ci				    rc);
49462306a36Sopenharmony_ci			return;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci		/* sm: / domain_attach */
49762306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);
49862306a36Sopenharmony_ci		break;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND:
50262306a36Sopenharmony_ci		/* Should not happen */
50362306a36Sopenharmony_ci		efc_log_err(efc, "%s: evt: %d should not happen\n",
50462306a36Sopenharmony_ci			    __func__, evt);
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_LOST: {
50862306a36Sopenharmony_ci		efc_log_debug(efc,
50962306a36Sopenharmony_ci			      "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",
51062306a36Sopenharmony_ci			efc_sm_event_name(evt));
51162306a36Sopenharmony_ci		if (!list_empty(&domain->nport_list)) {
51262306a36Sopenharmony_ci			/*
51362306a36Sopenharmony_ci			 * if there are nports, transition to
51462306a36Sopenharmony_ci			 * wait state and send shutdown to each
51562306a36Sopenharmony_ci			 * nport
51662306a36Sopenharmony_ci			 */
51762306a36Sopenharmony_ci			struct efc_nport *nport = NULL, *nport_next = NULL;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
52062306a36Sopenharmony_ci					  NULL);
52162306a36Sopenharmony_ci			list_for_each_entry_safe(nport, nport_next,
52262306a36Sopenharmony_ci						 &domain->nport_list,
52362306a36Sopenharmony_ci						 list_entry) {
52462306a36Sopenharmony_ci				efc_sm_post_event(&nport->sm,
52562306a36Sopenharmony_ci						  EFC_EVT_SHUTDOWN, NULL);
52662306a36Sopenharmony_ci			}
52762306a36Sopenharmony_ci		} else {
52862306a36Sopenharmony_ci			/* no nports exist, free domain */
52962306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
53062306a36Sopenharmony_ci					  NULL);
53162306a36Sopenharmony_ci			if (efc_cmd_domain_free(efc, domain))
53262306a36Sopenharmony_ci				efc_log_err(efc, "hw_domain_free failed\n");
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	default:
53962306a36Sopenharmony_ci		__efc_domain_common(__func__, ctx, evt, arg);
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_civoid
54462306a36Sopenharmony_ci__efc_domain_wait_attach(struct efc_sm_ctx *ctx,
54562306a36Sopenharmony_ci			 enum efc_sm_event evt, void *arg)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	std_domain_state_decl();
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	domain_sm_trace(domain);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	switch (evt) {
55262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK: {
55362306a36Sopenharmony_ci		struct efc_node *node = NULL;
55462306a36Sopenharmony_ci		struct efc_nport *nport, *next_nport;
55562306a36Sopenharmony_ci		unsigned long index;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		/*
55862306a36Sopenharmony_ci		 * Set domain notify pending state to avoid
55962306a36Sopenharmony_ci		 * duplicate domain event post
56062306a36Sopenharmony_ci		 */
56162306a36Sopenharmony_ci		domain->domain_notify_pend = true;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		/* Mark as attached */
56462306a36Sopenharmony_ci		domain->attached = true;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		/* Transition to ready */
56762306a36Sopenharmony_ci		/* sm: / forward event to all nports and nodes */
56862306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_ready, NULL);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		/* We have an FCFI, so we can accept frames */
57162306a36Sopenharmony_ci		domain->req_accept_frames = true;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		/*
57462306a36Sopenharmony_ci		 * Notify all nodes that the domain attach request
57562306a36Sopenharmony_ci		 * has completed
57662306a36Sopenharmony_ci		 * Note: nport will have already received notification
57762306a36Sopenharmony_ci		 * of nport attached as a result of the HW's port attach.
57862306a36Sopenharmony_ci		 */
57962306a36Sopenharmony_ci		list_for_each_entry_safe(nport, next_nport,
58062306a36Sopenharmony_ci					 &domain->nport_list, list_entry) {
58162306a36Sopenharmony_ci			xa_for_each(&nport->lookup, index, node) {
58262306a36Sopenharmony_ci				efc_node_post_event(node,
58362306a36Sopenharmony_ci						    EFC_EVT_DOMAIN_ATTACH_OK,
58462306a36Sopenharmony_ci						    NULL);
58562306a36Sopenharmony_ci			}
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci		domain->domain_notify_pend = false;
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_FAIL:
59262306a36Sopenharmony_ci		efc_log_debug(efc,
59362306a36Sopenharmony_ci			      "%s received while waiting for hw attach\n",
59462306a36Sopenharmony_ci			      efc_sm_event_name(evt));
59562306a36Sopenharmony_ci		break;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND:
59862306a36Sopenharmony_ci		/* Should not happen */
59962306a36Sopenharmony_ci		efc_log_err(efc, "%s: evt: %d should not happen\n",
60062306a36Sopenharmony_ci			    __func__, evt);
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_LOST:
60462306a36Sopenharmony_ci		/*
60562306a36Sopenharmony_ci		 * Domain lost while waiting for an attach to complete,
60662306a36Sopenharmony_ci		 * go to a state that waits for  the domain attach to
60762306a36Sopenharmony_ci		 * complete, then handle domain lost
60862306a36Sopenharmony_ci		 */
60962306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_REQ_ATTACH:
61362306a36Sopenharmony_ci		/*
61462306a36Sopenharmony_ci		 * In P2P we can get an attach request from
61562306a36Sopenharmony_ci		 * the other FLOGI path, so drop this one
61662306a36Sopenharmony_ci		 */
61762306a36Sopenharmony_ci		break;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	default:
62062306a36Sopenharmony_ci		__efc_domain_common(__func__, ctx, evt, arg);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_civoid
62562306a36Sopenharmony_ci__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	std_domain_state_decl();
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	domain_sm_trace(domain);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	switch (evt) {
63262306a36Sopenharmony_ci	case EFC_EVT_ENTER: {
63362306a36Sopenharmony_ci		/* start any pending vports */
63462306a36Sopenharmony_ci		if (efc_vport_start(domain)) {
63562306a36Sopenharmony_ci			efc_log_debug(domain->efc,
63662306a36Sopenharmony_ci				      "efc_vport_start didn't start vports\n");
63762306a36Sopenharmony_ci		}
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_LOST: {
64162306a36Sopenharmony_ci		if (!list_empty(&domain->nport_list)) {
64262306a36Sopenharmony_ci			/*
64362306a36Sopenharmony_ci			 * if there are nports, transition to wait state
64462306a36Sopenharmony_ci			 * and send shutdown to each nport
64562306a36Sopenharmony_ci			 */
64662306a36Sopenharmony_ci			struct efc_nport *nport = NULL, *nport_next = NULL;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
64962306a36Sopenharmony_ci					  NULL);
65062306a36Sopenharmony_ci			list_for_each_entry_safe(nport, nport_next,
65162306a36Sopenharmony_ci						 &domain->nport_list,
65262306a36Sopenharmony_ci						 list_entry) {
65362306a36Sopenharmony_ci				efc_sm_post_event(&nport->sm,
65462306a36Sopenharmony_ci						  EFC_EVT_SHUTDOWN, NULL);
65562306a36Sopenharmony_ci			}
65662306a36Sopenharmony_ci		} else {
65762306a36Sopenharmony_ci			/* no nports exist, free domain */
65862306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
65962306a36Sopenharmony_ci					  NULL);
66062306a36Sopenharmony_ci			if (efc_cmd_domain_free(efc, domain))
66162306a36Sopenharmony_ci				efc_log_err(efc, "hw_domain_free failed\n");
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci		break;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FOUND:
66762306a36Sopenharmony_ci		/* Should not happen */
66862306a36Sopenharmony_ci		efc_log_err(efc, "%s: evt: %d should not happen\n",
66962306a36Sopenharmony_ci			    __func__, evt);
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_REQ_ATTACH: {
67362306a36Sopenharmony_ci		/* can happen during p2p */
67462306a36Sopenharmony_ci		u32 fc_id;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		fc_id = *((u32 *)arg);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		/* Assume that the domain is attached */
67962306a36Sopenharmony_ci		WARN_ON(!domain->attached);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		/*
68262306a36Sopenharmony_ci		 * Verify that the requested FC_ID
68362306a36Sopenharmony_ci		 * is the same as the one we're working with
68462306a36Sopenharmony_ci		 */
68562306a36Sopenharmony_ci		WARN_ON(domain->nport->fc_id != fc_id);
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	default:
69062306a36Sopenharmony_ci		__efc_domain_common(__func__, ctx, evt, arg);
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_civoid
69562306a36Sopenharmony_ci__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
69662306a36Sopenharmony_ci			      void *arg)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	std_domain_state_decl();
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	domain_sm_trace(domain);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* Wait for nodes to free prior to the domain shutdown */
70362306a36Sopenharmony_ci	switch (evt) {
70462306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE: {
70562306a36Sopenharmony_ci		int rc;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		/* sm: / efc_hw_domain_free */
70862306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		/* Request efc_hw_domain_free and wait for completion */
71162306a36Sopenharmony_ci		rc = efc_cmd_domain_free(efc, domain);
71262306a36Sopenharmony_ci		if (rc) {
71362306a36Sopenharmony_ci			efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",
71462306a36Sopenharmony_ci				    rc);
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci		break;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci	default:
71962306a36Sopenharmony_ci		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_civoid
72462306a36Sopenharmony_ci__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,
72562306a36Sopenharmony_ci			   enum efc_sm_event evt, void *arg)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	std_domain_state_decl();
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	domain_sm_trace(domain);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	switch (evt) {
73262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_FREE_OK:
73362306a36Sopenharmony_ci		/* sm: / domain_free */
73462306a36Sopenharmony_ci		if (domain->domain_found_pending) {
73562306a36Sopenharmony_ci			/*
73662306a36Sopenharmony_ci			 * save fcf_wwn and drec from this domain,
73762306a36Sopenharmony_ci			 * free current domain and allocate
73862306a36Sopenharmony_ci			 * a new one with the same fcf_wwn
73962306a36Sopenharmony_ci			 * could use a SLI-4 "re-register VPI"
74062306a36Sopenharmony_ci			 * operation here?
74162306a36Sopenharmony_ci			 */
74262306a36Sopenharmony_ci			u64 fcf_wwn = domain->fcf_wwn;
74362306a36Sopenharmony_ci			struct efc_domain_record drec = domain->pending_drec;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci			efc_log_debug(efc, "Reallocating domain\n");
74662306a36Sopenharmony_ci			domain->req_domain_free = true;
74762306a36Sopenharmony_ci			domain = efc_domain_alloc(efc, fcf_wwn);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci			if (!domain) {
75062306a36Sopenharmony_ci				efc_log_err(efc,
75162306a36Sopenharmony_ci					    "efc_domain_alloc() failed\n");
75262306a36Sopenharmony_ci				return;
75362306a36Sopenharmony_ci			}
75462306a36Sopenharmony_ci			/*
75562306a36Sopenharmony_ci			 * got a new domain; at this point,
75662306a36Sopenharmony_ci			 * there are at least two domains
75762306a36Sopenharmony_ci			 * once the req_domain_free flag is processed,
75862306a36Sopenharmony_ci			 * the associated domain will be removed.
75962306a36Sopenharmony_ci			 */
76062306a36Sopenharmony_ci			efc_sm_transition(&domain->drvsm, __efc_domain_init,
76162306a36Sopenharmony_ci					  NULL);
76262306a36Sopenharmony_ci			efc_sm_post_event(&domain->drvsm,
76362306a36Sopenharmony_ci					  EFC_EVT_DOMAIN_FOUND, &drec);
76462306a36Sopenharmony_ci		} else {
76562306a36Sopenharmony_ci			domain->req_domain_free = true;
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	default:
76962306a36Sopenharmony_ci		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_civoid
77462306a36Sopenharmony_ci__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,
77562306a36Sopenharmony_ci			      enum efc_sm_event evt, void *arg)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	std_domain_state_decl();
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	domain_sm_trace(domain);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	/*
78262306a36Sopenharmony_ci	 * Wait for the domain alloc/attach completion
78362306a36Sopenharmony_ci	 * after receiving a domain lost.
78462306a36Sopenharmony_ci	 */
78562306a36Sopenharmony_ci	switch (evt) {
78662306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ALLOC_OK:
78762306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK: {
78862306a36Sopenharmony_ci		if (!list_empty(&domain->nport_list)) {
78962306a36Sopenharmony_ci			/*
79062306a36Sopenharmony_ci			 * if there are nports, transition to
79162306a36Sopenharmony_ci			 * wait state and send shutdown to each nport
79262306a36Sopenharmony_ci			 */
79362306a36Sopenharmony_ci			struct efc_nport *nport = NULL, *nport_next = NULL;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_nports_free,
79662306a36Sopenharmony_ci					  NULL);
79762306a36Sopenharmony_ci			list_for_each_entry_safe(nport, nport_next,
79862306a36Sopenharmony_ci						 &domain->nport_list,
79962306a36Sopenharmony_ci						 list_entry) {
80062306a36Sopenharmony_ci				efc_sm_post_event(&nport->sm,
80162306a36Sopenharmony_ci						  EFC_EVT_SHUTDOWN, NULL);
80262306a36Sopenharmony_ci			}
80362306a36Sopenharmony_ci		} else {
80462306a36Sopenharmony_ci			/* no nports exist, free domain */
80562306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_domain_wait_shutdown,
80662306a36Sopenharmony_ci					  NULL);
80762306a36Sopenharmony_ci			if (efc_cmd_domain_free(efc, domain))
80862306a36Sopenharmony_ci				efc_log_err(efc, "hw_domain_free() failed\n");
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ALLOC_FAIL:
81362306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_FAIL:
81462306a36Sopenharmony_ci		efc_log_err(efc, "[domain] %-20s: failed\n",
81562306a36Sopenharmony_ci			    efc_sm_event_name(evt));
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	default:
81962306a36Sopenharmony_ci		__efc_domain_common_shutdown(__func__, ctx, evt, arg);
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_civoid
82462306a36Sopenharmony_ci__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	memcpy(domain->dma.virt,
82762306a36Sopenharmony_ci	       ((uint8_t *)domain->flogi_service_params) + 4,
82862306a36Sopenharmony_ci		   sizeof(struct fc_els_flogi) - 4);
82962306a36Sopenharmony_ci	(void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,
83062306a36Sopenharmony_ci				 &s_id);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_civoid
83462306a36Sopenharmony_ciefc_domain_attach(struct efc_domain *domain, u32 s_id)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	__efc_domain_attach_internal(domain, s_id);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ciint
84062306a36Sopenharmony_ciefc_domain_post_event(struct efc_domain *domain,
84162306a36Sopenharmony_ci		      enum efc_sm_event event, void *arg)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	int rc;
84462306a36Sopenharmony_ci	bool req_domain_free;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	rc = efc_sm_post_event(&domain->drvsm, event, arg);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	req_domain_free = domain->req_domain_free;
84962306a36Sopenharmony_ci	domain->req_domain_free = false;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (req_domain_free)
85262306a36Sopenharmony_ci		efc_domain_free(domain);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return rc;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic void
85862306a36Sopenharmony_ciefct_domain_process_pending(struct efc_domain *domain)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	struct efc *efc = domain->efc;
86162306a36Sopenharmony_ci	struct efc_hw_sequence *seq = NULL;
86262306a36Sopenharmony_ci	u32 processed = 0;
86362306a36Sopenharmony_ci	unsigned long flags = 0;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	for (;;) {
86662306a36Sopenharmony_ci		/* need to check for hold frames condition after each frame
86762306a36Sopenharmony_ci		 * processed because any given frame could cause a transition
86862306a36Sopenharmony_ci		 * to a state that holds frames
86962306a36Sopenharmony_ci		 */
87062306a36Sopenharmony_ci		if (efc->hold_frames)
87162306a36Sopenharmony_ci			break;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		/* Get next frame/sequence */
87462306a36Sopenharmony_ci		spin_lock_irqsave(&efc->pend_frames_lock, flags);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		if (!list_empty(&efc->pend_frames)) {
87762306a36Sopenharmony_ci			seq = list_first_entry(&efc->pend_frames,
87862306a36Sopenharmony_ci					struct efc_hw_sequence, list_entry);
87962306a36Sopenharmony_ci			list_del(&seq->list_entry);
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		if (!seq) {
88362306a36Sopenharmony_ci			processed = efc->pend_frames_processed;
88462306a36Sopenharmony_ci			efc->pend_frames_processed = 0;
88562306a36Sopenharmony_ci			spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
88662306a36Sopenharmony_ci			break;
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci		efc->pend_frames_processed++;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci		/* now dispatch frame(s) to dispatch function */
89362306a36Sopenharmony_ci		if (efc_domain_dispatch_frame(domain, seq))
89462306a36Sopenharmony_ci			efc->tt.hw_seq_free(efc, seq);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		seq = NULL;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (processed != 0)
90062306a36Sopenharmony_ci		efc_log_debug(efc, "%u domain frames held and processed\n",
90162306a36Sopenharmony_ci			      processed);
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_civoid
90562306a36Sopenharmony_ciefc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct efc_domain *domain = efc->domain;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/*
91062306a36Sopenharmony_ci	 * If we are holding frames or the domain is not yet registered or
91162306a36Sopenharmony_ci	 * there's already frames on the pending list,
91262306a36Sopenharmony_ci	 * then add the new frame to pending list
91362306a36Sopenharmony_ci	 */
91462306a36Sopenharmony_ci	if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {
91562306a36Sopenharmony_ci		unsigned long flags = 0;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		spin_lock_irqsave(&efc->pend_frames_lock, flags);
91862306a36Sopenharmony_ci		INIT_LIST_HEAD(&seq->list_entry);
91962306a36Sopenharmony_ci		list_add_tail(&seq->list_entry, &efc->pend_frames);
92062306a36Sopenharmony_ci		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (domain) {
92362306a36Sopenharmony_ci			/* immediately process pending frames */
92462306a36Sopenharmony_ci			efct_domain_process_pending(domain);
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci	} else {
92762306a36Sopenharmony_ci		/*
92862306a36Sopenharmony_ci		 * We are not holding frames and pending list is empty,
92962306a36Sopenharmony_ci		 * just process frame. A non-zero return means the frame
93062306a36Sopenharmony_ci		 * was not handled - so cleanup
93162306a36Sopenharmony_ci		 */
93262306a36Sopenharmony_ci		if (efc_domain_dispatch_frame(domain, seq))
93362306a36Sopenharmony_ci			efc->tt.hw_seq_free(efc, seq);
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciint
93862306a36Sopenharmony_ciefc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	struct efc_domain *domain = (struct efc_domain *)arg;
94162306a36Sopenharmony_ci	struct efc *efc = domain->efc;
94262306a36Sopenharmony_ci	struct fc_frame_header *hdr;
94362306a36Sopenharmony_ci	struct efc_node *node = NULL;
94462306a36Sopenharmony_ci	struct efc_nport *nport = NULL;
94562306a36Sopenharmony_ci	unsigned long flags = 0;
94662306a36Sopenharmony_ci	u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {
94962306a36Sopenharmony_ci		efc_log_err(efc, "Sequence header or payload is null\n");
95062306a36Sopenharmony_ci		return rc;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	hdr = seq->header->dma.virt;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* extract the s_id and d_id */
95662306a36Sopenharmony_ci	s_id = ntoh24(hdr->fh_s_id);
95762306a36Sopenharmony_ci	d_id = ntoh24(hdr->fh_d_id);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	nport = efc_nport_find(domain, d_id);
96262306a36Sopenharmony_ci	if (!nport) {
96362306a36Sopenharmony_ci		if (hdr->fh_type == FC_TYPE_FCP) {
96462306a36Sopenharmony_ci			/* Drop frame */
96562306a36Sopenharmony_ci			efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",
96662306a36Sopenharmony_ci				     d_id);
96762306a36Sopenharmony_ci			goto out;
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/* p2p will use this case */
97162306a36Sopenharmony_ci		nport = domain->nport;
97262306a36Sopenharmony_ci		if (!nport || !kref_get_unless_zero(&nport->ref)) {
97362306a36Sopenharmony_ci			efc_log_err(efc, "Physical nport is NULL\n");
97462306a36Sopenharmony_ci			goto out;
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* Lookup the node given the remote s_id */
97962306a36Sopenharmony_ci	node = efc_node_find(nport, s_id);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	/* If not found, then create a new node */
98262306a36Sopenharmony_ci	if (!node) {
98362306a36Sopenharmony_ci		/*
98462306a36Sopenharmony_ci		 * If this is solicited data or control based on R_CTL and
98562306a36Sopenharmony_ci		 * there is no node context, then we can drop the frame
98662306a36Sopenharmony_ci		 */
98762306a36Sopenharmony_ci		if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||
98862306a36Sopenharmony_ci		    (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {
98962306a36Sopenharmony_ci			efc_log_debug(efc, "sol data/ctrl frame without node\n");
99062306a36Sopenharmony_ci			goto out_release;
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		node = efc_node_alloc(nport, s_id, false, false);
99462306a36Sopenharmony_ci		if (!node) {
99562306a36Sopenharmony_ci			efc_log_err(efc, "efc_node_alloc() failed\n");
99662306a36Sopenharmony_ci			goto out_release;
99762306a36Sopenharmony_ci		}
99862306a36Sopenharmony_ci		/* don't send PLOGI on efc_d_init entry */
99962306a36Sopenharmony_ci		efc_node_init_device(node, false);
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (node->hold_frames || !list_empty(&node->pend_frames)) {
100362306a36Sopenharmony_ci		/* add frame to node's pending list */
100462306a36Sopenharmony_ci		spin_lock(&node->pend_frames_lock);
100562306a36Sopenharmony_ci		INIT_LIST_HEAD(&seq->list_entry);
100662306a36Sopenharmony_ci		list_add_tail(&seq->list_entry, &node->pend_frames);
100762306a36Sopenharmony_ci		spin_unlock(&node->pend_frames_lock);
100862306a36Sopenharmony_ci		rc = EFC_HW_SEQ_HOLD;
100962306a36Sopenharmony_ci		goto out_release;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/* now dispatch frame to the node frame handler */
101362306a36Sopenharmony_ci	efc_node_dispatch_frame(node, seq);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciout_release:
101662306a36Sopenharmony_ci	kref_put(&nport->ref, nport->release);
101762306a36Sopenharmony_ciout:
101862306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
101962306a36Sopenharmony_ci	return rc;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_civoid
102362306a36Sopenharmony_ciefc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct fc_frame_header *hdr = seq->header->dma.virt;
102662306a36Sopenharmony_ci	u32 port_id;
102762306a36Sopenharmony_ci	struct efc_node *node = (struct efc_node *)arg;
102862306a36Sopenharmony_ci	struct efc *efc = node->efc;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	port_id = ntoh24(hdr->fh_s_id);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (WARN_ON(port_id != node->rnode.fc_id))
103362306a36Sopenharmony_ci		return;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||
103662306a36Sopenharmony_ci	    !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {
103762306a36Sopenharmony_ci		node_printf(node,
103862306a36Sopenharmony_ci			    "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",
103962306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[0]),
104062306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[1]),
104162306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[2]),
104262306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[3]),
104362306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[4]),
104462306a36Sopenharmony_ci			    cpu_to_be32(((u32 *)hdr)[5]));
104562306a36Sopenharmony_ci		return;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	switch (hdr->fh_r_ctl) {
104962306a36Sopenharmony_ci	case FC_RCTL_ELS_REQ:
105062306a36Sopenharmony_ci	case FC_RCTL_ELS_REP:
105162306a36Sopenharmony_ci		efc_node_recv_els_frame(node, seq);
105262306a36Sopenharmony_ci		break;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	case FC_RCTL_BA_ABTS:
105562306a36Sopenharmony_ci	case FC_RCTL_BA_ACC:
105662306a36Sopenharmony_ci	case FC_RCTL_BA_RJT:
105762306a36Sopenharmony_ci	case FC_RCTL_BA_NOP:
105862306a36Sopenharmony_ci		efc_log_err(efc, "Received ABTS:\n");
105962306a36Sopenharmony_ci		break;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	case FC_RCTL_DD_UNSOL_CMD:
106262306a36Sopenharmony_ci	case FC_RCTL_DD_UNSOL_CTL:
106362306a36Sopenharmony_ci		switch (hdr->fh_type) {
106462306a36Sopenharmony_ci		case FC_TYPE_FCP:
106562306a36Sopenharmony_ci			if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {
106662306a36Sopenharmony_ci				if (!node->fcp_enabled) {
106762306a36Sopenharmony_ci					efc_node_recv_fcp_cmd(node, seq);
106862306a36Sopenharmony_ci					break;
106962306a36Sopenharmony_ci				}
107062306a36Sopenharmony_ci				efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");
107162306a36Sopenharmony_ci			} else if ((hdr->fh_r_ctl & 0xf) ==
107262306a36Sopenharmony_ci							FC_RCTL_DD_SOL_DATA) {
107362306a36Sopenharmony_ci				node_printf(node,
107462306a36Sopenharmony_ci					    "solicited data recvd. Drop IO\n");
107562306a36Sopenharmony_ci			}
107662306a36Sopenharmony_ci			break;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		case FC_TYPE_CT:
107962306a36Sopenharmony_ci			efc_node_recv_ct_frame(node, seq);
108062306a36Sopenharmony_ci			break;
108162306a36Sopenharmony_ci		default:
108262306a36Sopenharmony_ci			break;
108362306a36Sopenharmony_ci		}
108462306a36Sopenharmony_ci		break;
108562306a36Sopenharmony_ci	default:
108662306a36Sopenharmony_ci		efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci}
1089