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#include "efc.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ciint
1062306a36Sopenharmony_ciefc_remote_node_cb(void *arg, int event, void *data)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	struct efc *efc = arg;
1362306a36Sopenharmony_ci	struct efc_remote_node *rnode = data;
1462306a36Sopenharmony_ci	struct efc_node *node = rnode->node;
1562306a36Sopenharmony_ci	unsigned long flags = 0;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
1862306a36Sopenharmony_ci	efc_node_post_event(node, event, NULL);
1962306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	return 0;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct efc_node *
2562306a36Sopenharmony_ciefc_node_find(struct efc_nport *nport, u32 port_id)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	/* Find an FC node structure given the FC port ID */
2862306a36Sopenharmony_ci	return xa_load(&nport->lookup, port_id);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void
3262306a36Sopenharmony_ci_efc_node_free(struct kref *arg)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct efc_node *node = container_of(arg, struct efc_node, ref);
3562306a36Sopenharmony_ci	struct efc *efc = node->efc;
3662306a36Sopenharmony_ci	struct efc_dma *dma;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	dma = &node->sparm_dma_buf;
3962306a36Sopenharmony_ci	dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);
4062306a36Sopenharmony_ci	memset(dma, 0, sizeof(struct efc_dma));
4162306a36Sopenharmony_ci	mempool_free(node, efc->node_pool);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct efc_node *efc_node_alloc(struct efc_nport *nport,
4562306a36Sopenharmony_ci				u32 port_id, bool init, bool targ)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int rc;
4862306a36Sopenharmony_ci	struct efc_node *node = NULL;
4962306a36Sopenharmony_ci	struct efc *efc = nport->efc;
5062306a36Sopenharmony_ci	struct efc_dma *dma;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (nport->shutting_down) {
5362306a36Sopenharmony_ci		efc_log_debug(efc, "node allocation when shutting down %06x",
5462306a36Sopenharmony_ci			      port_id);
5562306a36Sopenharmony_ci		return NULL;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	node = mempool_alloc(efc->node_pool, GFP_ATOMIC);
5962306a36Sopenharmony_ci	if (!node) {
6062306a36Sopenharmony_ci		efc_log_err(efc, "node allocation failed %06x", port_id);
6162306a36Sopenharmony_ci		return NULL;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	memset(node, 0, sizeof(*node));
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	dma = &node->sparm_dma_buf;
6662306a36Sopenharmony_ci	dma->size = NODE_SPARAMS_SIZE;
6762306a36Sopenharmony_ci	dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys);
6862306a36Sopenharmony_ci	if (!dma->virt) {
6962306a36Sopenharmony_ci		efc_log_err(efc, "node dma alloc failed\n");
7062306a36Sopenharmony_ci		goto dma_fail;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	node->rnode.indicator = U32_MAX;
7362306a36Sopenharmony_ci	node->nport = nport;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	node->efc = efc;
7662306a36Sopenharmony_ci	node->init = init;
7762306a36Sopenharmony_ci	node->targ = targ;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_lock_init(&node->pend_frames_lock);
8062306a36Sopenharmony_ci	INIT_LIST_HEAD(&node->pend_frames);
8162306a36Sopenharmony_ci	spin_lock_init(&node->els_ios_lock);
8262306a36Sopenharmony_ci	INIT_LIST_HEAD(&node->els_ios_list);
8362306a36Sopenharmony_ci	node->els_io_enabled = true;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport);
8662306a36Sopenharmony_ci	if (rc) {
8762306a36Sopenharmony_ci		efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc);
8862306a36Sopenharmony_ci		goto hw_alloc_fail;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	node->rnode.node = node;
9262306a36Sopenharmony_ci	node->sm.app = node;
9362306a36Sopenharmony_ci	node->evtdepth = 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	efc_node_update_display_name(node);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC));
9862306a36Sopenharmony_ci	if (rc) {
9962306a36Sopenharmony_ci		efc_log_err(efc, "Node lookup store failed: %d\n", rc);
10062306a36Sopenharmony_ci		goto xa_fail;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* initialize refcount */
10462306a36Sopenharmony_ci	kref_init(&node->ref);
10562306a36Sopenharmony_ci	node->release = _efc_node_free;
10662306a36Sopenharmony_ci	kref_get(&nport->ref);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return node;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cixa_fail:
11162306a36Sopenharmony_ci	efc_node_free_resources(efc, &node->rnode);
11262306a36Sopenharmony_cihw_alloc_fail:
11362306a36Sopenharmony_ci	dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);
11462306a36Sopenharmony_cidma_fail:
11562306a36Sopenharmony_ci	mempool_free(node, efc->node_pool);
11662306a36Sopenharmony_ci	return NULL;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid
12062306a36Sopenharmony_ciefc_node_free(struct efc_node *node)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct efc_nport *nport;
12362306a36Sopenharmony_ci	struct efc *efc;
12462306a36Sopenharmony_ci	int rc = 0;
12562306a36Sopenharmony_ci	struct efc_node *ns = NULL;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	nport = node->nport;
12862306a36Sopenharmony_ci	efc = node->efc;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	node_printf(node, "Free'd\n");
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (node->refound) {
13362306a36Sopenharmony_ci		/*
13462306a36Sopenharmony_ci		 * Save the name server node. We will send fake RSCN event at
13562306a36Sopenharmony_ci		 * the end to handle ignored RSCN event during node deletion
13662306a36Sopenharmony_ci		 */
13762306a36Sopenharmony_ci		ns = efc_node_find(node->nport, FC_FID_DIR_SERV);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!node->nport) {
14162306a36Sopenharmony_ci		efc_log_err(efc, "Node already Freed\n");
14262306a36Sopenharmony_ci		return;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Free HW resources */
14662306a36Sopenharmony_ci	rc = efc_node_free_resources(efc, &node->rnode);
14762306a36Sopenharmony_ci	if (rc < 0)
14862306a36Sopenharmony_ci		efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* if the gidpt_delay_timer is still running, then delete it */
15162306a36Sopenharmony_ci	if (timer_pending(&node->gidpt_delay_timer))
15262306a36Sopenharmony_ci		del_timer(&node->gidpt_delay_timer);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	xa_erase(&nport->lookup, node->rnode.fc_id);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * If the node_list is empty,
15862306a36Sopenharmony_ci	 * then post a ALL_CHILD_NODES_FREE event to the nport,
15962306a36Sopenharmony_ci	 * after the lock is released.
16062306a36Sopenharmony_ci	 * The nport may be free'd as a result of the event.
16162306a36Sopenharmony_ci	 */
16262306a36Sopenharmony_ci	if (xa_empty(&nport->lookup))
16362306a36Sopenharmony_ci		efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE,
16462306a36Sopenharmony_ci				  NULL);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	node->nport = NULL;
16762306a36Sopenharmony_ci	node->sm.current_state = NULL;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	kref_put(&nport->ref, nport->release);
17062306a36Sopenharmony_ci	kref_put(&node->ref, node->release);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (ns) {
17362306a36Sopenharmony_ci		/* sending fake RSCN event to name server node */
17462306a36Sopenharmony_ci		efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void
17962306a36Sopenharmony_ciefc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	if (!dma || !buffer || !buffer_length)
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (buffer_length > dma->size)
18562306a36Sopenharmony_ci		buffer_length = dma->size;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	memcpy(dma->virt, buffer, buffer_length);
18862306a36Sopenharmony_ci	dma->len = buffer_length;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciint
19262306a36Sopenharmony_ciefc_node_attach(struct efc_node *node)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int rc = 0;
19562306a36Sopenharmony_ci	struct efc_nport *nport = node->nport;
19662306a36Sopenharmony_ci	struct efc_domain *domain = nport->domain;
19762306a36Sopenharmony_ci	struct efc *efc = node->efc;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!domain->attached) {
20062306a36Sopenharmony_ci		efc_log_err(efc, "Warning: unattached domain\n");
20162306a36Sopenharmony_ci		return -EIO;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	/* Update node->wwpn/wwnn */
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn),
20662306a36Sopenharmony_ci				efc_node_get_wwpn(node));
20762306a36Sopenharmony_ci	efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn),
20862306a36Sopenharmony_ci				efc_node_get_wwnn(node));
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4,
21162306a36Sopenharmony_ci			sizeof(node->service_params) - 4);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* take lock to protect node->rnode.attached */
21462306a36Sopenharmony_ci	rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf);
21562306a36Sopenharmony_ci	if (rc < 0)
21662306a36Sopenharmony_ci		efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return rc;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_civoid
22262306a36Sopenharmony_ciefc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	switch (fc_id) {
22562306a36Sopenharmony_ci	case FC_FID_FLOGI:
22662306a36Sopenharmony_ci		snprintf(buffer, buffer_length, "fabric");
22762306a36Sopenharmony_ci		break;
22862306a36Sopenharmony_ci	case FC_FID_FCTRL:
22962306a36Sopenharmony_ci		snprintf(buffer, buffer_length, "fabctl");
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case FC_FID_DIR_SERV:
23262306a36Sopenharmony_ci		snprintf(buffer, buffer_length, "nserve");
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	default:
23562306a36Sopenharmony_ci		if (fc_id == FC_FID_DOM_MGR) {
23662306a36Sopenharmony_ci			snprintf(buffer, buffer_length, "dctl%02x",
23762306a36Sopenharmony_ci				 (fc_id & 0x0000ff));
23862306a36Sopenharmony_ci		} else {
23962306a36Sopenharmony_ci			snprintf(buffer, buffer_length, "%06x", fc_id);
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci		break;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_civoid
24662306a36Sopenharmony_ciefc_node_update_display_name(struct efc_node *node)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	u32 port_id = node->rnode.fc_id;
24962306a36Sopenharmony_ci	struct efc_nport *nport = node->nport;
25062306a36Sopenharmony_ci	char portid_display[16];
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	efc_node_fcid_display(port_id, portid_display, sizeof(portid_display));
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	snprintf(node->display_name, sizeof(node->display_name), "%s.%s",
25562306a36Sopenharmony_ci		 nport->display_name, portid_display);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_civoid
25962306a36Sopenharmony_ciefc_node_send_ls_io_cleanup(struct efc_node *node)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) {
26262306a36Sopenharmony_ci		efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n",
26362306a36Sopenharmony_ci			      node->display_name, node->ls_acc_oxid);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
26662306a36Sopenharmony_ci		node->ls_acc_io = NULL;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void efc_node_handle_implicit_logo(struct efc_node *node)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	int rc;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/*
27562306a36Sopenharmony_ci	 * currently, only case for implicit logo is PLOGI
27662306a36Sopenharmony_ci	 * recvd. Thus, node's ELS IO pending list won't be
27762306a36Sopenharmony_ci	 * empty (PLOGI will be on it)
27862306a36Sopenharmony_ci	 */
27962306a36Sopenharmony_ci	WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI);
28062306a36Sopenharmony_ci	node_printf(node, "Reason: implicit logout, re-authenticate\n");
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Re-attach node with the same HW node resources */
28362306a36Sopenharmony_ci	node->req_free = false;
28462306a36Sopenharmony_ci	rc = efc_node_attach(node);
28562306a36Sopenharmony_ci	efc_node_transition(node, __efc_d_wait_node_attach, NULL);
28662306a36Sopenharmony_ci	node->els_io_enabled = true;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (rc < 0)
28962306a36Sopenharmony_ci		efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void efc_node_handle_explicit_logo(struct efc_node *node)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	s8 pend_frames_empty;
29562306a36Sopenharmony_ci	unsigned long flags = 0;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* cleanup any pending LS_ACC ELSs */
29862306a36Sopenharmony_ci	efc_node_send_ls_io_cleanup(node);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	spin_lock_irqsave(&node->pend_frames_lock, flags);
30162306a36Sopenharmony_ci	pend_frames_empty = list_empty(&node->pend_frames);
30262306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->pend_frames_lock, flags);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/*
30562306a36Sopenharmony_ci	 * there are two scenarios where we want to keep
30662306a36Sopenharmony_ci	 * this node alive:
30762306a36Sopenharmony_ci	 * 1. there are pending frames that need to be
30862306a36Sopenharmony_ci	 *    processed or
30962306a36Sopenharmony_ci	 * 2. we're an initiator and the remote node is
31062306a36Sopenharmony_ci	 *    a target and we need to re-authenticate
31162306a36Sopenharmony_ci	 */
31262306a36Sopenharmony_ci	node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty);
31362306a36Sopenharmony_ci	node_printf(node, "nport.ini=%d node.tgt=%d\n",
31462306a36Sopenharmony_ci		    node->nport->enable_ini, node->targ);
31562306a36Sopenharmony_ci	if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) {
31662306a36Sopenharmony_ci		u8 send_plogi = false;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		if (node->nport->enable_ini && node->targ) {
31962306a36Sopenharmony_ci			/*
32062306a36Sopenharmony_ci			 * we're an initiator and
32162306a36Sopenharmony_ci			 * node shutting down is a target;
32262306a36Sopenharmony_ci			 * we'll need to re-authenticate in
32362306a36Sopenharmony_ci			 * initial state
32462306a36Sopenharmony_ci			 */
32562306a36Sopenharmony_ci			send_plogi = true;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		/*
32962306a36Sopenharmony_ci		 * transition to __efc_d_init
33062306a36Sopenharmony_ci		 * (will retain HW node resources)
33162306a36Sopenharmony_ci		 */
33262306a36Sopenharmony_ci		node->els_io_enabled = true;
33362306a36Sopenharmony_ci		node->req_free = false;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		/*
33662306a36Sopenharmony_ci		 * either pending frames exist or we are re-authenticating
33762306a36Sopenharmony_ci		 * with PLOGI (or both); in either case, return to initial
33862306a36Sopenharmony_ci		 * state
33962306a36Sopenharmony_ci		 */
34062306a36Sopenharmony_ci		efc_node_init_device(node, send_plogi);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	/* else: let node shutdown occur */
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void
34662306a36Sopenharmony_ciefc_node_purge_pending(struct efc_node *node)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct efc *efc = node->efc;
34962306a36Sopenharmony_ci	struct efc_hw_sequence *frame, *next;
35062306a36Sopenharmony_ci	unsigned long flags = 0;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	spin_lock_irqsave(&node->pend_frames_lock, flags);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) {
35562306a36Sopenharmony_ci		list_del(&frame->list_entry);
35662306a36Sopenharmony_ci		efc->tt.hw_seq_free(efc, frame);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->pend_frames_lock, flags);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_civoid
36362306a36Sopenharmony_ci__efc_node_shutdown(struct efc_sm_ctx *ctx,
36462306a36Sopenharmony_ci		    enum efc_sm_event evt, void *arg)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct efc_node *node = ctx->app;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	efc_node_evt_set(ctx, evt, __func__);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	node_sm_trace();
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	switch (evt) {
37362306a36Sopenharmony_ci	case EFC_EVT_ENTER: {
37462306a36Sopenharmony_ci		efc_node_hold_frames(node);
37562306a36Sopenharmony_ci		WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));
37662306a36Sopenharmony_ci		/* by default, we will be freeing node after we unwind */
37762306a36Sopenharmony_ci		node->req_free = true;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		switch (node->shutdown_reason) {
38062306a36Sopenharmony_ci		case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO:
38162306a36Sopenharmony_ci			/* Node shutdown b/c of PLOGI received when node
38262306a36Sopenharmony_ci			 * already logged in. We have PLOGI service
38362306a36Sopenharmony_ci			 * parameters, so submit node attach; we won't be
38462306a36Sopenharmony_ci			 * freeing this node
38562306a36Sopenharmony_ci			 */
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci			efc_node_handle_implicit_logo(node);
38862306a36Sopenharmony_ci			break;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO:
39162306a36Sopenharmony_ci			efc_node_handle_explicit_logo(node);
39262306a36Sopenharmony_ci			break;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		case EFC_NODE_SHUTDOWN_DEFAULT:
39562306a36Sopenharmony_ci		default: {
39662306a36Sopenharmony_ci			/*
39762306a36Sopenharmony_ci			 * shutdown due to link down,
39862306a36Sopenharmony_ci			 * node going away (xport event) or
39962306a36Sopenharmony_ci			 * nport shutdown, purge pending and
40062306a36Sopenharmony_ci			 * proceed to cleanup node
40162306a36Sopenharmony_ci			 */
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci			/* cleanup any pending LS_ACC ELSs */
40462306a36Sopenharmony_ci			efc_node_send_ls_io_cleanup(node);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci			node_printf(node,
40762306a36Sopenharmony_ci				    "Shutdown reason: default, purge pending\n");
40862306a36Sopenharmony_ci			efc_node_purge_pending(node);
40962306a36Sopenharmony_ci			break;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	case EFC_EVT_EXIT:
41662306a36Sopenharmony_ci		efc_node_accept_frames(node);
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	default:
42062306a36Sopenharmony_ci		__efc_node_common(__func__, ctx, evt, arg);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic bool
42562306a36Sopenharmony_ciefc_node_check_els_quiesced(struct efc_node *node)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	/* check to see if ELS requests, completions are quiesced */
42862306a36Sopenharmony_ci	if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 &&
42962306a36Sopenharmony_ci	    efc_els_io_list_empty(node, &node->els_ios_list)) {
43062306a36Sopenharmony_ci		if (!node->attached) {
43162306a36Sopenharmony_ci			/* hw node detach already completed, proceed */
43262306a36Sopenharmony_ci			node_printf(node, "HW node not attached\n");
43362306a36Sopenharmony_ci			efc_node_transition(node,
43462306a36Sopenharmony_ci					    __efc_node_wait_ios_shutdown,
43562306a36Sopenharmony_ci					     NULL);
43662306a36Sopenharmony_ci		} else {
43762306a36Sopenharmony_ci			/*
43862306a36Sopenharmony_ci			 * hw node detach hasn't completed,
43962306a36Sopenharmony_ci			 * transition and wait
44062306a36Sopenharmony_ci			 */
44162306a36Sopenharmony_ci			node_printf(node, "HW node still attached\n");
44262306a36Sopenharmony_ci			efc_node_transition(node, __efc_node_wait_node_free,
44362306a36Sopenharmony_ci					    NULL);
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci		return true;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci	return false;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_civoid
45162306a36Sopenharmony_ciefc_node_initiate_cleanup(struct efc_node *node)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * if ELS's have already been quiesced, will move to next state
45562306a36Sopenharmony_ci	 * if ELS's have not been quiesced, abort them
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	if (!efc_node_check_els_quiesced(node)) {
45862306a36Sopenharmony_ci		efc_node_hold_frames(node);
45962306a36Sopenharmony_ci		efc_node_transition(node, __efc_node_wait_els_shutdown, NULL);
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_civoid
46462306a36Sopenharmony_ci__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx,
46562306a36Sopenharmony_ci			     enum efc_sm_event evt, void *arg)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	bool check_quiesce = false;
46862306a36Sopenharmony_ci	struct efc_node *node = ctx->app;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	efc_node_evt_set(ctx, evt, __func__);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	node_sm_trace();
47362306a36Sopenharmony_ci	/* Node state machine: Wait for all ELSs to complete */
47462306a36Sopenharmony_ci	switch (evt) {
47562306a36Sopenharmony_ci	case EFC_EVT_ENTER:
47662306a36Sopenharmony_ci		efc_node_hold_frames(node);
47762306a36Sopenharmony_ci		if (efc_els_io_list_empty(node, &node->els_ios_list)) {
47862306a36Sopenharmony_ci			node_printf(node, "All ELS IOs complete\n");
47962306a36Sopenharmony_ci			check_quiesce = true;
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci		break;
48262306a36Sopenharmony_ci	case EFC_EVT_EXIT:
48362306a36Sopenharmony_ci		efc_node_accept_frames(node);
48462306a36Sopenharmony_ci		break;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_OK:
48762306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_FAIL:
48862306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_RJT:
48962306a36Sopenharmony_ci	case EFC_EVT_ELS_REQ_ABORTED:
49062306a36Sopenharmony_ci		if (WARN_ON(!node->els_req_cnt))
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci		node->els_req_cnt--;
49362306a36Sopenharmony_ci		check_quiesce = true;
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_CMPL_OK:
49762306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_CMPL_FAIL:
49862306a36Sopenharmony_ci		if (WARN_ON(!node->els_cmpl_cnt))
49962306a36Sopenharmony_ci			break;
50062306a36Sopenharmony_ci		node->els_cmpl_cnt--;
50162306a36Sopenharmony_ci		check_quiesce = true;
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE:
50562306a36Sopenharmony_ci		/* all ELS IO's complete */
50662306a36Sopenharmony_ci		node_printf(node, "All ELS IOs complete\n");
50762306a36Sopenharmony_ci		WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));
50862306a36Sopenharmony_ci		check_quiesce = true;
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
51262306a36Sopenharmony_ci		check_quiesce = true;
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK:
51662306a36Sopenharmony_ci		/* don't care about domain_attach_ok */
51762306a36Sopenharmony_ci		break;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* ignore shutdown events as we're already in shutdown path */
52062306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN:
52162306a36Sopenharmony_ci		/* have default shutdown event take precedence */
52262306a36Sopenharmony_ci		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
52362306a36Sopenharmony_ci		fallthrough;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
52662306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
52762306a36Sopenharmony_ci		node_printf(node, "%s received\n", efc_sm_event_name(evt));
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	default:
53162306a36Sopenharmony_ci		__efc_node_common(__func__, ctx, evt, arg);
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (check_quiesce)
53562306a36Sopenharmony_ci		efc_node_check_els_quiesced(node);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_civoid
53962306a36Sopenharmony_ci__efc_node_wait_node_free(struct efc_sm_ctx *ctx,
54062306a36Sopenharmony_ci			  enum efc_sm_event evt, void *arg)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct efc_node *node = ctx->app;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	efc_node_evt_set(ctx, evt, __func__);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	node_sm_trace();
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	switch (evt) {
54962306a36Sopenharmony_ci	case EFC_EVT_ENTER:
55062306a36Sopenharmony_ci		efc_node_hold_frames(node);
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	case EFC_EVT_EXIT:
55462306a36Sopenharmony_ci		efc_node_accept_frames(node);
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	case EFC_EVT_NODE_FREE_OK:
55862306a36Sopenharmony_ci		/* node is officially no longer attached */
55962306a36Sopenharmony_ci		node->attached = false;
56062306a36Sopenharmony_ci		efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL);
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE:
56462306a36Sopenharmony_ci	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
56562306a36Sopenharmony_ci		/* As IOs and ELS IO's complete we expect to get these events */
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK:
56962306a36Sopenharmony_ci		/* don't care about domain_attach_ok */
57062306a36Sopenharmony_ci		break;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* ignore shutdown events as we're already in shutdown path */
57362306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN:
57462306a36Sopenharmony_ci		/* have default shutdown event take precedence */
57562306a36Sopenharmony_ci		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
57662306a36Sopenharmony_ci		fallthrough;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
57962306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
58062306a36Sopenharmony_ci		node_printf(node, "%s received\n", efc_sm_event_name(evt));
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	default:
58362306a36Sopenharmony_ci		__efc_node_common(__func__, ctx, evt, arg);
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_civoid
58862306a36Sopenharmony_ci__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx,
58962306a36Sopenharmony_ci			     enum efc_sm_event evt, void *arg)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct efc_node *node = ctx->app;
59262306a36Sopenharmony_ci	struct efc *efc = node->efc;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	efc_node_evt_set(ctx, evt, __func__);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	node_sm_trace();
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	switch (evt) {
59962306a36Sopenharmony_ci	case EFC_EVT_ENTER:
60062306a36Sopenharmony_ci		efc_node_hold_frames(node);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		/* first check to see if no ELS IOs are outstanding */
60362306a36Sopenharmony_ci		if (efc_els_io_list_empty(node, &node->els_ios_list))
60462306a36Sopenharmony_ci			/* If there are any active IOS, Free them. */
60562306a36Sopenharmony_ci			efc_node_transition(node, __efc_node_shutdown, NULL);
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
60962306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE:
61062306a36Sopenharmony_ci		if (efc_els_io_list_empty(node, &node->els_ios_list))
61162306a36Sopenharmony_ci			efc_node_transition(node, __efc_node_shutdown, NULL);
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	case EFC_EVT_EXIT:
61562306a36Sopenharmony_ci		efc_node_accept_frames(node);
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_FAIL:
61962306a36Sopenharmony_ci		/* Can happen as ELS IO IO's complete */
62062306a36Sopenharmony_ci		if (WARN_ON(!node->els_req_cnt))
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		node->els_req_cnt--;
62362306a36Sopenharmony_ci		break;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* ignore shutdown events as we're already in shutdown path */
62662306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN:
62762306a36Sopenharmony_ci		/* have default shutdown event take precedence */
62862306a36Sopenharmony_ci		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
62962306a36Sopenharmony_ci		fallthrough;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
63262306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
63362306a36Sopenharmony_ci		efc_log_debug(efc, "[%s] %-20s\n", node->display_name,
63462306a36Sopenharmony_ci			      efc_sm_event_name(evt));
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK:
63762306a36Sopenharmony_ci		/* don't care about domain_attach_ok */
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci	default:
64062306a36Sopenharmony_ci		__efc_node_common(__func__, ctx, evt, arg);
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_civoid
64562306a36Sopenharmony_ci__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx,
64662306a36Sopenharmony_ci		  enum efc_sm_event evt, void *arg)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct efc_node *node = NULL;
64962306a36Sopenharmony_ci	struct efc *efc = NULL;
65062306a36Sopenharmony_ci	struct efc_node_cb *cbdata = arg;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	node = ctx->app;
65362306a36Sopenharmony_ci	efc = node->efc;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	switch (evt) {
65662306a36Sopenharmony_ci	case EFC_EVT_ENTER:
65762306a36Sopenharmony_ci	case EFC_EVT_REENTER:
65862306a36Sopenharmony_ci	case EFC_EVT_EXIT:
65962306a36Sopenharmony_ci	case EFC_EVT_NPORT_TOPOLOGY_NOTIFY:
66062306a36Sopenharmony_ci	case EFC_EVT_NODE_MISSING:
66162306a36Sopenharmony_ci	case EFC_EVT_FCP_CMD_RCVD:
66262306a36Sopenharmony_ci		break;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	case EFC_EVT_NODE_REFOUND:
66562306a36Sopenharmony_ci		node->refound = true;
66662306a36Sopenharmony_ci		break;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/*
66962306a36Sopenharmony_ci	 * node->attached must be set appropriately
67062306a36Sopenharmony_ci	 * for all node attach/detach events
67162306a36Sopenharmony_ci	 */
67262306a36Sopenharmony_ci	case EFC_EVT_NODE_ATTACH_OK:
67362306a36Sopenharmony_ci		node->attached = true;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	case EFC_EVT_NODE_FREE_OK:
67762306a36Sopenharmony_ci	case EFC_EVT_NODE_ATTACH_FAIL:
67862306a36Sopenharmony_ci		node->attached = false;
67962306a36Sopenharmony_ci		break;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/*
68262306a36Sopenharmony_ci	 * handle any ELS completions that
68362306a36Sopenharmony_ci	 * other states either didn't care about
68462306a36Sopenharmony_ci	 * or forgot about
68562306a36Sopenharmony_ci	 */
68662306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_CMPL_OK:
68762306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_CMPL_FAIL:
68862306a36Sopenharmony_ci		if (WARN_ON(!node->els_cmpl_cnt))
68962306a36Sopenharmony_ci			break;
69062306a36Sopenharmony_ci		node->els_cmpl_cnt--;
69162306a36Sopenharmony_ci		break;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/*
69462306a36Sopenharmony_ci	 * handle any ELS request completions that
69562306a36Sopenharmony_ci	 * other states either didn't care about
69662306a36Sopenharmony_ci	 * or forgot about
69762306a36Sopenharmony_ci	 */
69862306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_OK:
69962306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_FAIL:
70062306a36Sopenharmony_ci	case EFC_EVT_SRRS_ELS_REQ_RJT:
70162306a36Sopenharmony_ci	case EFC_EVT_ELS_REQ_ABORTED:
70262306a36Sopenharmony_ci		if (WARN_ON(!node->els_req_cnt))
70362306a36Sopenharmony_ci			break;
70462306a36Sopenharmony_ci		node->els_req_cnt--;
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	case EFC_EVT_ELS_RCVD: {
70862306a36Sopenharmony_ci		struct fc_frame_header *hdr = cbdata->header->dma.virt;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		/*
71162306a36Sopenharmony_ci		 * Unsupported ELS was received,
71262306a36Sopenharmony_ci		 * send LS_RJT, command not supported
71362306a36Sopenharmony_ci		 */
71462306a36Sopenharmony_ci		efc_log_debug(efc,
71562306a36Sopenharmony_ci			      "[%s] (%s) ELS x%02x, LS_RJT not supported\n",
71662306a36Sopenharmony_ci			      node->display_name, funcname,
71762306a36Sopenharmony_ci			      ((u8 *)cbdata->payload->dma.virt)[0]);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
72062306a36Sopenharmony_ci				ELS_RJT_UNSUP, ELS_EXPL_NONE, 0);
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	case EFC_EVT_PLOGI_RCVD:
72562306a36Sopenharmony_ci	case EFC_EVT_FLOGI_RCVD:
72662306a36Sopenharmony_ci	case EFC_EVT_LOGO_RCVD:
72762306a36Sopenharmony_ci	case EFC_EVT_PRLI_RCVD:
72862306a36Sopenharmony_ci	case EFC_EVT_PRLO_RCVD:
72962306a36Sopenharmony_ci	case EFC_EVT_PDISC_RCVD:
73062306a36Sopenharmony_ci	case EFC_EVT_FDISC_RCVD:
73162306a36Sopenharmony_ci	case EFC_EVT_ADISC_RCVD:
73262306a36Sopenharmony_ci	case EFC_EVT_RSCN_RCVD:
73362306a36Sopenharmony_ci	case EFC_EVT_SCR_RCVD: {
73462306a36Sopenharmony_ci		struct fc_frame_header *hdr = cbdata->header->dma.virt;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		/* sm: / send ELS_RJT */
73762306a36Sopenharmony_ci		efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n",
73862306a36Sopenharmony_ci			      node->display_name, funcname,
73962306a36Sopenharmony_ci			      efc_sm_event_name(evt));
74062306a36Sopenharmony_ci		/* if we didn't catch this in a state, send generic LS_RJT */
74162306a36Sopenharmony_ci		efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
74262306a36Sopenharmony_ci				ELS_RJT_UNAB, ELS_EXPL_NONE, 0);
74362306a36Sopenharmony_ci		break;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci	case EFC_EVT_ABTS_RCVD: {
74662306a36Sopenharmony_ci		efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n",
74762306a36Sopenharmony_ci			      node->display_name, funcname,
74862306a36Sopenharmony_ci			      efc_sm_event_name(evt));
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		/* sm: / send BA_ACC */
75162306a36Sopenharmony_ci		efc_send_bls_acc(node, cbdata->header->dma.virt);
75262306a36Sopenharmony_ci		break;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	default:
75662306a36Sopenharmony_ci		efc_log_debug(node->efc, "[%s] %-20s %-20s not handled\n",
75762306a36Sopenharmony_ci			      node->display_name, funcname,
75862306a36Sopenharmony_ci			      efc_sm_event_name(evt));
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_civoid
76362306a36Sopenharmony_ciefc_node_save_sparms(struct efc_node *node, void *payload)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	memcpy(node->service_params, payload, sizeof(node->service_params));
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_civoid
76962306a36Sopenharmony_ciefc_node_post_event(struct efc_node *node,
77062306a36Sopenharmony_ci		    enum efc_sm_event evt, void *arg)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	bool free_node = false;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	node->evtdepth++;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	efc_sm_post_event(&node->sm, evt, arg);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* If our event call depth is one and
77962306a36Sopenharmony_ci	 * we're not holding frames
78062306a36Sopenharmony_ci	 * then we can dispatch any pending frames.
78162306a36Sopenharmony_ci	 * We don't want to allow the efc_process_node_pending()
78262306a36Sopenharmony_ci	 * call to recurse.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	if (!node->hold_frames && node->evtdepth == 1)
78562306a36Sopenharmony_ci		efc_process_node_pending(node);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	node->evtdepth--;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/*
79062306a36Sopenharmony_ci	 * Free the node object if so requested,
79162306a36Sopenharmony_ci	 * and we're at an event call depth of zero
79262306a36Sopenharmony_ci	 */
79362306a36Sopenharmony_ci	if (node->evtdepth == 0 && node->req_free)
79462306a36Sopenharmony_ci		free_node = true;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (free_node)
79762306a36Sopenharmony_ci		efc_node_free(node);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_civoid
80162306a36Sopenharmony_ciefc_node_transition(struct efc_node *node,
80262306a36Sopenharmony_ci		    void (*state)(struct efc_sm_ctx *,
80362306a36Sopenharmony_ci				  enum efc_sm_event, void *), void *data)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct efc_sm_ctx *ctx = &node->sm;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (ctx->current_state == state) {
80862306a36Sopenharmony_ci		efc_node_post_event(node, EFC_EVT_REENTER, data);
80962306a36Sopenharmony_ci	} else {
81062306a36Sopenharmony_ci		efc_node_post_event(node, EFC_EVT_EXIT, data);
81162306a36Sopenharmony_ci		ctx->current_state = state;
81262306a36Sopenharmony_ci		efc_node_post_event(node, EFC_EVT_ENTER, data);
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_civoid
81762306a36Sopenharmony_ciefc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	memset(buf, 0, buf_len);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ciu64
82562306a36Sopenharmony_ciefc_node_get_wwpn(struct efc_node *node)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct fc_els_flogi *sp =
82862306a36Sopenharmony_ci			(struct fc_els_flogi *)node->service_params;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	return be64_to_cpu(sp->fl_wwpn);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ciu64
83462306a36Sopenharmony_ciefc_node_get_wwnn(struct efc_node *node)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct fc_els_flogi *sp =
83762306a36Sopenharmony_ci			(struct fc_els_flogi *)node->service_params;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return be64_to_cpu(sp->fl_wwnn);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ciint
84362306a36Sopenharmony_ciefc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
84462306a36Sopenharmony_ci		u8 cmd, void (*efc_node_common_func)(const char *,
84562306a36Sopenharmony_ci				struct efc_sm_ctx *, enum efc_sm_event, void *),
84662306a36Sopenharmony_ci		const char *funcname)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ciint
85262306a36Sopenharmony_ciefc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
85362306a36Sopenharmony_ci		u16 cmd, void (*efc_node_common_func)(const char *,
85462306a36Sopenharmony_ci				struct efc_sm_ctx *, enum efc_sm_event, void *),
85562306a36Sopenharmony_ci		const char *funcname)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	return 0;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciint
86162306a36Sopenharmony_ciefc_els_io_list_empty(struct efc_node *node, struct list_head *list)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	int empty;
86462306a36Sopenharmony_ci	unsigned long flags = 0;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	spin_lock_irqsave(&node->els_ios_lock, flags);
86762306a36Sopenharmony_ci	empty = list_empty(list);
86862306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->els_ios_lock, flags);
86962306a36Sopenharmony_ci	return empty;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_civoid
87362306a36Sopenharmony_ciefc_node_pause(struct efc_node *node,
87462306a36Sopenharmony_ci	       void (*state)(struct efc_sm_ctx *,
87562306a36Sopenharmony_ci			     enum efc_sm_event, void *))
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	node->nodedb_state = state;
87962306a36Sopenharmony_ci	efc_node_transition(node, __efc_node_paused, NULL);
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_civoid
88362306a36Sopenharmony_ci__efc_node_paused(struct efc_sm_ctx *ctx,
88462306a36Sopenharmony_ci		  enum efc_sm_event evt, void *arg)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct efc_node *node = ctx->app;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	efc_node_evt_set(ctx, evt, __func__);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	node_sm_trace();
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/*
89362306a36Sopenharmony_ci	 * This state is entered when a state is "paused". When resumed, the
89462306a36Sopenharmony_ci	 * node is transitioned to a previously saved state (node->ndoedb_state)
89562306a36Sopenharmony_ci	 */
89662306a36Sopenharmony_ci	switch (evt) {
89762306a36Sopenharmony_ci	case EFC_EVT_ENTER:
89862306a36Sopenharmony_ci		node_printf(node, "Paused\n");
89962306a36Sopenharmony_ci		break;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	case EFC_EVT_RESUME: {
90262306a36Sopenharmony_ci		void (*pf)(struct efc_sm_ctx *ctx,
90362306a36Sopenharmony_ci			   enum efc_sm_event evt, void *arg);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		pf = node->nodedb_state;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		node->nodedb_state = NULL;
90862306a36Sopenharmony_ci		efc_node_transition(node, pf, NULL);
90962306a36Sopenharmony_ci		break;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	case EFC_EVT_DOMAIN_ATTACH_OK:
91362306a36Sopenharmony_ci		break;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN:
91662306a36Sopenharmony_ci		node->req_free = true;
91762306a36Sopenharmony_ci		break;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	default:
92062306a36Sopenharmony_ci		__efc_node_common(__func__, ctx, evt, arg);
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_civoid
92562306a36Sopenharmony_ciefc_node_recv_els_frame(struct efc_node *node,
92662306a36Sopenharmony_ci			struct efc_hw_sequence *seq)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp);
92962306a36Sopenharmony_ci	struct {
93062306a36Sopenharmony_ci		u32 cmd;
93162306a36Sopenharmony_ci		enum efc_sm_event evt;
93262306a36Sopenharmony_ci		u32 payload_size;
93362306a36Sopenharmony_ci	} els_cmd_list[] = {
93462306a36Sopenharmony_ci		{ELS_PLOGI, EFC_EVT_PLOGI_RCVD,	sizeof(struct fc_els_flogi)},
93562306a36Sopenharmony_ci		{ELS_FLOGI, EFC_EVT_FLOGI_RCVD,	sizeof(struct fc_els_flogi)},
93662306a36Sopenharmony_ci		{ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)},
93762306a36Sopenharmony_ci		{ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size},
93862306a36Sopenharmony_ci		{ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size},
93962306a36Sopenharmony_ci		{ELS_PDISC, EFC_EVT_PDISC_RCVD,	MAX_ACC_REJECT_PAYLOAD},
94062306a36Sopenharmony_ci		{ELS_FDISC, EFC_EVT_FDISC_RCVD,	MAX_ACC_REJECT_PAYLOAD},
94162306a36Sopenharmony_ci		{ELS_ADISC, EFC_EVT_ADISC_RCVD,	sizeof(struct fc_els_adisc)},
94262306a36Sopenharmony_ci		{ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD},
94362306a36Sopenharmony_ci		{ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD},
94462306a36Sopenharmony_ci	};
94562306a36Sopenharmony_ci	struct efc_node_cb cbdata;
94662306a36Sopenharmony_ci	u8 *buf = seq->payload->dma.virt;
94762306a36Sopenharmony_ci	enum efc_sm_event evt = EFC_EVT_ELS_RCVD;
94862306a36Sopenharmony_ci	u32 i;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	memset(&cbdata, 0, sizeof(cbdata));
95162306a36Sopenharmony_ci	cbdata.header = seq->header;
95262306a36Sopenharmony_ci	cbdata.payload = seq->payload;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	/* find a matching event for the ELS command */
95562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) {
95662306a36Sopenharmony_ci		if (els_cmd_list[i].cmd == buf[0]) {
95762306a36Sopenharmony_ci			evt = els_cmd_list[i].evt;
95862306a36Sopenharmony_ci			break;
95962306a36Sopenharmony_ci		}
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	efc_node_post_event(node, evt, &cbdata);
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_civoid
96662306a36Sopenharmony_ciefc_node_recv_ct_frame(struct efc_node *node,
96762306a36Sopenharmony_ci		       struct efc_hw_sequence *seq)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	struct fc_ct_hdr *iu = seq->payload->dma.virt;
97062306a36Sopenharmony_ci	struct fc_frame_header *hdr = seq->header->dma.virt;
97162306a36Sopenharmony_ci	struct efc *efc = node->efc;
97262306a36Sopenharmony_ci	u16 gscmd = be16_to_cpu(iu->ct_cmd);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n",
97562306a36Sopenharmony_ci		    node->display_name, gscmd);
97662306a36Sopenharmony_ci	efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu,
97762306a36Sopenharmony_ci			FC_FS_RJT, FC_FS_RJT_UNSUP, 0);
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_civoid
98162306a36Sopenharmony_ciefc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	struct efc_node_cb cbdata;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	memset(&cbdata, 0, sizeof(cbdata));
98662306a36Sopenharmony_ci	cbdata.header = seq->header;
98762306a36Sopenharmony_ci	cbdata.payload = seq->payload;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_civoid
99362306a36Sopenharmony_ciefc_process_node_pending(struct efc_node *node)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct efc *efc = node->efc;
99662306a36Sopenharmony_ci	struct efc_hw_sequence *seq = NULL;
99762306a36Sopenharmony_ci	u32 pend_frames_processed = 0;
99862306a36Sopenharmony_ci	unsigned long flags = 0;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	for (;;) {
100162306a36Sopenharmony_ci		/* need to check for hold frames condition after each frame
100262306a36Sopenharmony_ci		 * processed because any given frame could cause a transition
100362306a36Sopenharmony_ci		 * to a state that holds frames
100462306a36Sopenharmony_ci		 */
100562306a36Sopenharmony_ci		if (node->hold_frames)
100662306a36Sopenharmony_ci			break;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		seq = NULL;
100962306a36Sopenharmony_ci		/* Get next frame/sequence */
101062306a36Sopenharmony_ci		spin_lock_irqsave(&node->pend_frames_lock, flags);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		if (!list_empty(&node->pend_frames)) {
101362306a36Sopenharmony_ci			seq = list_first_entry(&node->pend_frames,
101462306a36Sopenharmony_ci					struct efc_hw_sequence, list_entry);
101562306a36Sopenharmony_ci			list_del(&seq->list_entry);
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci		spin_unlock_irqrestore(&node->pend_frames_lock, flags);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		if (!seq) {
102062306a36Sopenharmony_ci			pend_frames_processed =	node->pend_frames_processed;
102162306a36Sopenharmony_ci			node->pend_frames_processed = 0;
102262306a36Sopenharmony_ci			break;
102362306a36Sopenharmony_ci		}
102462306a36Sopenharmony_ci		node->pend_frames_processed++;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		/* now dispatch frame(s) to dispatch function */
102762306a36Sopenharmony_ci		efc_node_dispatch_frame(node, seq);
102862306a36Sopenharmony_ci		efc->tt.hw_seq_free(efc, seq);
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	if (pend_frames_processed != 0)
103262306a36Sopenharmony_ci		efc_log_debug(efc, "%u node frames held and processed\n",
103362306a36Sopenharmony_ci			      pend_frames_processed);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_civoid
103762306a36Sopenharmony_ciefc_scsi_sess_reg_complete(struct efc_node *node, u32 status)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	unsigned long flags = 0;
104062306a36Sopenharmony_ci	enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK;
104162306a36Sopenharmony_ci	struct efc *efc = node->efc;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (status)
104462306a36Sopenharmony_ci		evt = EFC_EVT_NODE_SESS_REG_FAIL;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
104762306a36Sopenharmony_ci	/* Notify the node to resume */
104862306a36Sopenharmony_ci	efc_node_post_event(node, evt, NULL);
104962306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_civoid
105362306a36Sopenharmony_ciefc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	unsigned long flags = 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
105862306a36Sopenharmony_ci	/* Notify the node to resume */
105962306a36Sopenharmony_ci	efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
106062306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_civoid
106462306a36Sopenharmony_ciefc_scsi_del_target_complete(struct efc *efc, struct efc_node *node)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	unsigned long flags = 0;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
106962306a36Sopenharmony_ci	/* Notify the node to resume */
107062306a36Sopenharmony_ci	efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
107162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_civoid
107562306a36Sopenharmony_ciefc_scsi_io_list_empty(struct efc *efc, struct efc_node *node)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	unsigned long flags = 0;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
108062306a36Sopenharmony_ci	efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
108162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_civoid efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct efc *efc = node->efc;
108762306a36Sopenharmony_ci	unsigned long flags = 0;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
109062306a36Sopenharmony_ci	efc_node_post_event(node, evt, arg);
109162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_civoid efc_node_post_shutdown(struct efc_node *node, void *arg)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	unsigned long flags = 0;
109762306a36Sopenharmony_ci	struct efc *efc = node->efc;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
110062306a36Sopenharmony_ci	efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg);
110162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
110262306a36Sopenharmony_ci}
1103