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 "efct_driver.h"
862306a36Sopenharmony_ci#include "efct_hw.h"
962306a36Sopenharmony_ci#include "efct_unsol.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct efct_hw_link_stat_cb_arg {
1262306a36Sopenharmony_ci	void (*cb)(int status, u32 num_counters,
1362306a36Sopenharmony_ci		   struct efct_hw_link_stat_counts *counters, void *arg);
1462306a36Sopenharmony_ci	void *arg;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct efct_hw_host_stat_cb_arg {
1862306a36Sopenharmony_ci	void (*cb)(int status, u32 num_counters,
1962306a36Sopenharmony_ci		   struct efct_hw_host_stat_counts *counters, void *arg);
2062306a36Sopenharmony_ci	void *arg;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct efct_hw_fw_wr_cb_arg {
2462306a36Sopenharmony_ci	void (*cb)(int status, u32 bytes_written, u32 change_status, void *arg);
2562306a36Sopenharmony_ci	void *arg;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct efct_mbox_rqst_ctx {
2962306a36Sopenharmony_ci	int (*callback)(struct efc *efc, int status, u8 *mqe, void *arg);
3062306a36Sopenharmony_ci	void *arg;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int
3462306a36Sopenharmony_ciefct_hw_link_event_init(struct efct_hw *hw)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	hw->link.status = SLI4_LINK_STATUS_MAX;
3762306a36Sopenharmony_ci	hw->link.topology = SLI4_LINK_TOPO_NONE;
3862306a36Sopenharmony_ci	hw->link.medium = SLI4_LINK_MEDIUM_MAX;
3962306a36Sopenharmony_ci	hw->link.speed = 0;
4062306a36Sopenharmony_ci	hw->link.loop_map = NULL;
4162306a36Sopenharmony_ci	hw->link.fc_id = U32_MAX;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int
4762306a36Sopenharmony_ciefct_hw_read_max_dump_size(struct efct_hw *hw)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	u8 buf[SLI4_BMBX_SIZE];
5062306a36Sopenharmony_ci	struct efct *efct = hw->os;
5162306a36Sopenharmony_ci	int rc = 0;
5262306a36Sopenharmony_ci	struct sli4_rsp_cmn_set_dump_location *rsp;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* attempt to detemine the dump size for function 0 only. */
5562306a36Sopenharmony_ci	if (PCI_FUNC(efct->pci->devfn) != 0)
5662306a36Sopenharmony_ci		return rc;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (sli_cmd_common_set_dump_location(&hw->sli, buf, 1, 0, NULL, 0))
5962306a36Sopenharmony_ci		return -EIO;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	rsp = (struct sli4_rsp_cmn_set_dump_location *)
6262306a36Sopenharmony_ci	      (buf + offsetof(struct sli4_cmd_sli_config, payload.embed));
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
6562306a36Sopenharmony_ci	if (rc != 0) {
6662306a36Sopenharmony_ci		efc_log_debug(hw->os, "set dump location cmd failed\n");
6762306a36Sopenharmony_ci		return rc;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	hw->dump_size =
7162306a36Sopenharmony_ci	  le32_to_cpu(rsp->buffer_length_dword) & SLI4_CMN_SET_DUMP_BUFFER_LEN;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	efc_log_debug(hw->os, "Dump size %x\n",	hw->dump_size);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return rc;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int
7962306a36Sopenharmony_ci__efct_read_topology_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct sli4_cmd_read_topology *read_topo =
8262306a36Sopenharmony_ci				(struct sli4_cmd_read_topology *)mqe;
8362306a36Sopenharmony_ci	u8 speed;
8462306a36Sopenharmony_ci	struct efc_domain_record drec = {0};
8562306a36Sopenharmony_ci	struct efct *efct = hw->os;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (status || le16_to_cpu(read_topo->hdr.status)) {
8862306a36Sopenharmony_ci		efc_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
8962306a36Sopenharmony_ci			      le16_to_cpu(read_topo->hdr.status));
9062306a36Sopenharmony_ci		return -EIO;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	switch (le32_to_cpu(read_topo->dw2_attentype) &
9462306a36Sopenharmony_ci		SLI4_READTOPO_ATTEN_TYPE) {
9562306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_LINK_UP:
9662306a36Sopenharmony_ci		hw->link.status = SLI4_LINK_STATUS_UP;
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_LINK_DOWN:
9962306a36Sopenharmony_ci		hw->link.status = SLI4_LINK_STATUS_DOWN;
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
10262306a36Sopenharmony_ci		hw->link.status = SLI4_LINK_STATUS_NO_ALPA;
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci	default:
10562306a36Sopenharmony_ci		hw->link.status = SLI4_LINK_STATUS_MAX;
10662306a36Sopenharmony_ci		break;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	switch (read_topo->topology) {
11062306a36Sopenharmony_ci	case SLI4_READ_TOPO_NON_FC_AL:
11162306a36Sopenharmony_ci		hw->link.topology = SLI4_LINK_TOPO_NON_FC_AL;
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case SLI4_READ_TOPO_FC_AL:
11462306a36Sopenharmony_ci		hw->link.topology = SLI4_LINK_TOPO_FC_AL;
11562306a36Sopenharmony_ci		if (hw->link.status == SLI4_LINK_STATUS_UP)
11662306a36Sopenharmony_ci			hw->link.loop_map = hw->loop_map.virt;
11762306a36Sopenharmony_ci		hw->link.fc_id = read_topo->acquired_al_pa;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	default:
12062306a36Sopenharmony_ci		hw->link.topology = SLI4_LINK_TOPO_MAX;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	hw->link.medium = SLI4_LINK_MEDIUM_FC;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	speed = (le32_to_cpu(read_topo->currlink_state) &
12762306a36Sopenharmony_ci		 SLI4_READTOPO_LINKSTATE_SPEED) >> 8;
12862306a36Sopenharmony_ci	switch (speed) {
12962306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_1G:
13062306a36Sopenharmony_ci		hw->link.speed =  1 * 1000;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_2G:
13362306a36Sopenharmony_ci		hw->link.speed =  2 * 1000;
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_4G:
13662306a36Sopenharmony_ci		hw->link.speed =  4 * 1000;
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_8G:
13962306a36Sopenharmony_ci		hw->link.speed =  8 * 1000;
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_16G:
14262306a36Sopenharmony_ci		hw->link.speed = 16 * 1000;
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_32G:
14562306a36Sopenharmony_ci		hw->link.speed = 32 * 1000;
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_64G:
14862306a36Sopenharmony_ci		hw->link.speed = 64 * 1000;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case SLI4_READ_TOPOLOGY_SPEED_128G:
15162306a36Sopenharmony_ci		hw->link.speed = 128 * 1000;
15262306a36Sopenharmony_ci		break;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	drec.speed = hw->link.speed;
15662306a36Sopenharmony_ci	drec.fc_id = hw->link.fc_id;
15762306a36Sopenharmony_ci	drec.is_nport = true;
15862306a36Sopenharmony_ci	efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, &drec);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int
16462306a36Sopenharmony_ciefct_hw_cb_link(void *ctx, void *e)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct efct_hw *hw = ctx;
16762306a36Sopenharmony_ci	struct sli4_link_event *event = e;
16862306a36Sopenharmony_ci	struct efc_domain *d = NULL;
16962306a36Sopenharmony_ci	int rc = 0;
17062306a36Sopenharmony_ci	struct efct *efct = hw->os;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	efct_hw_link_event_init(hw);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	switch (event->status) {
17562306a36Sopenharmony_ci	case SLI4_LINK_STATUS_UP:
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		hw->link = *event;
17862306a36Sopenharmony_ci		efct->efcport->link_status = EFC_LINK_STATUS_UP;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		if (event->topology == SLI4_LINK_TOPO_NON_FC_AL) {
18162306a36Sopenharmony_ci			struct efc_domain_record drec = {0};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci			efc_log_info(hw->os, "Link Up, NPORT, speed is %d\n",
18462306a36Sopenharmony_ci				     event->speed);
18562306a36Sopenharmony_ci			drec.speed = event->speed;
18662306a36Sopenharmony_ci			drec.fc_id = event->fc_id;
18762306a36Sopenharmony_ci			drec.is_nport = true;
18862306a36Sopenharmony_ci			efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND,
18962306a36Sopenharmony_ci				      &drec);
19062306a36Sopenharmony_ci		} else if (event->topology == SLI4_LINK_TOPO_FC_AL) {
19162306a36Sopenharmony_ci			u8 buf[SLI4_BMBX_SIZE];
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci			efc_log_info(hw->os, "Link Up, LOOP, speed is %d\n",
19462306a36Sopenharmony_ci				     event->speed);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci			if (!sli_cmd_read_topology(&hw->sli, buf,
19762306a36Sopenharmony_ci						   &hw->loop_map)) {
19862306a36Sopenharmony_ci				rc = efct_hw_command(hw, buf, EFCT_CMD_NOWAIT,
19962306a36Sopenharmony_ci						__efct_read_topology_cb, NULL);
20062306a36Sopenharmony_ci			}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci			if (rc)
20362306a36Sopenharmony_ci				efc_log_debug(hw->os, "READ_TOPOLOGY failed\n");
20462306a36Sopenharmony_ci		} else {
20562306a36Sopenharmony_ci			efc_log_info(hw->os, "%s(%#x), speed is %d\n",
20662306a36Sopenharmony_ci				     "Link Up, unsupported topology ",
20762306a36Sopenharmony_ci				     event->topology, event->speed);
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci		break;
21062306a36Sopenharmony_ci	case SLI4_LINK_STATUS_DOWN:
21162306a36Sopenharmony_ci		efc_log_info(hw->os, "Link down\n");
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		hw->link.status = event->status;
21462306a36Sopenharmony_ci		efct->efcport->link_status = EFC_LINK_STATUS_DOWN;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		d = efct->efcport->domain;
21762306a36Sopenharmony_ci		if (d)
21862306a36Sopenharmony_ci			efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST, d);
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	default:
22162306a36Sopenharmony_ci		efc_log_debug(hw->os, "unhandled link status %#x\n",
22262306a36Sopenharmony_ci			      event->status);
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciint
23062306a36Sopenharmony_ciefct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	u32 i, max_sgl, cpus;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (hw->hw_setup_called)
23562306a36Sopenharmony_ci		return 0;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/*
23862306a36Sopenharmony_ci	 * efct_hw_init() relies on NULL pointers indicating that a structure
23962306a36Sopenharmony_ci	 * needs allocation. If a structure is non-NULL, efct_hw_init() won't
24062306a36Sopenharmony_ci	 * free/realloc that memory
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	memset(hw, 0, sizeof(struct efct_hw));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	hw->hw_setup_called = true;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	hw->os = os;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	mutex_init(&hw->bmbx_lock);
24962306a36Sopenharmony_ci	spin_lock_init(&hw->cmd_lock);
25062306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->cmd_head);
25162306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->cmd_pending);
25262306a36Sopenharmony_ci	hw->cmd_head_count = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Create mailbox command ctx pool */
25562306a36Sopenharmony_ci	hw->cmd_ctx_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ,
25662306a36Sopenharmony_ci					sizeof(struct efct_command_ctx));
25762306a36Sopenharmony_ci	if (!hw->cmd_ctx_pool) {
25862306a36Sopenharmony_ci		efc_log_err(hw->os, "failed to allocate mailbox buffer pool\n");
25962306a36Sopenharmony_ci		return -EIO;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Create mailbox request ctx pool for library callback */
26362306a36Sopenharmony_ci	hw->mbox_rqst_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ,
26462306a36Sopenharmony_ci					sizeof(struct efct_mbox_rqst_ctx));
26562306a36Sopenharmony_ci	if (!hw->mbox_rqst_pool) {
26662306a36Sopenharmony_ci		efc_log_err(hw->os, "failed to allocate mbox request pool\n");
26762306a36Sopenharmony_ci		return -EIO;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	spin_lock_init(&hw->io_lock);
27162306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->io_inuse);
27262306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->io_free);
27362306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->io_wait_free);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	atomic_set(&hw->io_alloc_failed_count, 0);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	hw->config.speed = SLI4_LINK_SPEED_AUTO_16_8_4;
27862306a36Sopenharmony_ci	if (sli_setup(&hw->sli, hw->os, pdev, ((struct efct *)os)->reg)) {
27962306a36Sopenharmony_ci		efc_log_err(hw->os, "SLI setup failed\n");
28062306a36Sopenharmony_ci		return -EIO;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	efct_hw_link_event_init(hw);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	sli_callback(&hw->sli, SLI4_CB_LINK, efct_hw_cb_link, hw);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * Set all the queue sizes to the maximum allowed.
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++)
29162306a36Sopenharmony_ci		hw->num_qentries[i] = hw->sli.qinfo.max_qentries[i];
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * Adjust the size of the WQs so that the CQ is twice as big as
29462306a36Sopenharmony_ci	 * the WQ to allow for 2 completions per IO. This allows us to
29562306a36Sopenharmony_ci	 * handle multi-phase as well as aborts.
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	hw->num_qentries[SLI4_QTYPE_WQ] = hw->num_qentries[SLI4_QTYPE_CQ] / 2;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * The RQ assignment for RQ pair mode.
30162306a36Sopenharmony_ci	 */
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	hw->config.rq_default_buffer_size = EFCT_HW_RQ_SIZE_PAYLOAD;
30462306a36Sopenharmony_ci	hw->config.n_io = hw->sli.ext[SLI4_RSRC_XRI].size;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	cpus = num_possible_cpus();
30762306a36Sopenharmony_ci	hw->config.n_eq = cpus > EFCT_HW_MAX_NUM_EQ ? EFCT_HW_MAX_NUM_EQ : cpus;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	max_sgl = sli_get_max_sgl(&hw->sli) - SLI4_SGE_MAX_RESERVED;
31062306a36Sopenharmony_ci	max_sgl = (max_sgl > EFCT_FC_MAX_SGL) ? EFCT_FC_MAX_SGL : max_sgl;
31162306a36Sopenharmony_ci	hw->config.n_sgl = max_sgl;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	(void)efct_hw_read_max_dump_size(hw);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void
31962306a36Sopenharmony_ciefct_logfcfi(struct efct_hw *hw, u32 j, u32 i, u32 id)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	efc_log_info(hw->os,
32262306a36Sopenharmony_ci		     "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
32362306a36Sopenharmony_ci		     j, hw->config.filter_def[j], i, id);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic inline void
32762306a36Sopenharmony_ciefct_hw_init_free_io(struct efct_hw_io *io)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	/*
33062306a36Sopenharmony_ci	 * Set io->done to NULL, to avoid any callbacks, should
33162306a36Sopenharmony_ci	 * a completion be received for one of these IOs
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	io->done = NULL;
33462306a36Sopenharmony_ci	io->abort_done = NULL;
33562306a36Sopenharmony_ci	io->status_saved = false;
33662306a36Sopenharmony_ci	io->abort_in_progress = false;
33762306a36Sopenharmony_ci	io->type = 0xFFFF;
33862306a36Sopenharmony_ci	io->wq = NULL;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic bool efct_hw_iotype_is_originator(u16 io_type)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	switch (io_type) {
34462306a36Sopenharmony_ci	case EFCT_HW_FC_CT:
34562306a36Sopenharmony_ci	case EFCT_HW_ELS_REQ:
34662306a36Sopenharmony_ci		return true;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		return false;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void
35362306a36Sopenharmony_ciefct_hw_io_restore_sgl(struct efct_hw *hw, struct efct_hw_io *io)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	/* Restore the default */
35662306a36Sopenharmony_ci	io->sgl = &io->def_sgl;
35762306a36Sopenharmony_ci	io->sgl_count = io->def_sgl_count;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void
36162306a36Sopenharmony_ciefct_hw_wq_process_io(void *arg, u8 *cqe, int status)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct efct_hw_io *io = arg;
36462306a36Sopenharmony_ci	struct efct_hw *hw = io->hw;
36562306a36Sopenharmony_ci	struct sli4_fc_wcqe *wcqe = (void *)cqe;
36662306a36Sopenharmony_ci	u32	len = 0;
36762306a36Sopenharmony_ci	u32 ext = 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* clear xbusy flag if WCQE[XB] is clear */
37062306a36Sopenharmony_ci	if (io->xbusy && (wcqe->flags & SLI4_WCQE_XB) == 0)
37162306a36Sopenharmony_ci		io->xbusy = false;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* get extended CQE status */
37462306a36Sopenharmony_ci	switch (io->type) {
37562306a36Sopenharmony_ci	case EFCT_HW_BLS_ACC:
37662306a36Sopenharmony_ci	case EFCT_HW_BLS_RJT:
37762306a36Sopenharmony_ci		break;
37862306a36Sopenharmony_ci	case EFCT_HW_ELS_REQ:
37962306a36Sopenharmony_ci		sli_fc_els_did(&hw->sli, cqe, &ext);
38062306a36Sopenharmony_ci		len = sli_fc_response_length(&hw->sli, cqe);
38162306a36Sopenharmony_ci		break;
38262306a36Sopenharmony_ci	case EFCT_HW_ELS_RSP:
38362306a36Sopenharmony_ci	case EFCT_HW_FC_CT_RSP:
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci	case EFCT_HW_FC_CT:
38662306a36Sopenharmony_ci		len = sli_fc_response_length(&hw->sli, cqe);
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_WRITE:
38962306a36Sopenharmony_ci		len = sli_fc_io_length(&hw->sli, cqe);
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_READ:
39262306a36Sopenharmony_ci		len = sli_fc_io_length(&hw->sli, cqe);
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_RSP:
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci	case EFCT_HW_IO_DNRX_REQUEUE:
39762306a36Sopenharmony_ci		/* release the count for re-posting the buffer */
39862306a36Sopenharmony_ci		/* efct_hw_io_free(hw, io); */
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	default:
40162306a36Sopenharmony_ci		efc_log_err(hw->os, "unhandled io type %#x for XRI 0x%x\n",
40262306a36Sopenharmony_ci			    io->type, io->indicator);
40362306a36Sopenharmony_ci		break;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	if (status) {
40662306a36Sopenharmony_ci		ext = sli_fc_ext_status(&hw->sli, cqe);
40762306a36Sopenharmony_ci		/*
40862306a36Sopenharmony_ci		 * If we're not an originator IO, and XB is set, then issue
40962306a36Sopenharmony_ci		 * abort for the IO from within the HW
41062306a36Sopenharmony_ci		 */
41162306a36Sopenharmony_ci		if (efct_hw_iotype_is_originator(io->type) &&
41262306a36Sopenharmony_ci		    wcqe->flags & SLI4_WCQE_XB) {
41362306a36Sopenharmony_ci			int rc;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci			efc_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
41662306a36Sopenharmony_ci				      io->indicator, io->reqtag);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci			/*
41962306a36Sopenharmony_ci			 * Because targets may send a response when the IO
42062306a36Sopenharmony_ci			 * completes using the same XRI, we must wait for the
42162306a36Sopenharmony_ci			 * XRI_ABORTED CQE to issue the IO callback
42262306a36Sopenharmony_ci			 */
42362306a36Sopenharmony_ci			rc = efct_hw_io_abort(hw, io, false, NULL, NULL);
42462306a36Sopenharmony_ci			if (rc == 0) {
42562306a36Sopenharmony_ci				/*
42662306a36Sopenharmony_ci				 * latch status to return after abort is
42762306a36Sopenharmony_ci				 * complete
42862306a36Sopenharmony_ci				 */
42962306a36Sopenharmony_ci				io->status_saved = true;
43062306a36Sopenharmony_ci				io->saved_status = status;
43162306a36Sopenharmony_ci				io->saved_ext = ext;
43262306a36Sopenharmony_ci				io->saved_len = len;
43362306a36Sopenharmony_ci				goto exit_efct_hw_wq_process_io;
43462306a36Sopenharmony_ci			} else if (rc == -EINPROGRESS) {
43562306a36Sopenharmony_ci				/*
43662306a36Sopenharmony_ci				 * Already being aborted by someone else (ABTS
43762306a36Sopenharmony_ci				 * perhaps). Just return original
43862306a36Sopenharmony_ci				 * error.
43962306a36Sopenharmony_ci				 */
44062306a36Sopenharmony_ci				efc_log_debug(hw->os, "%s%#x tag=%#x\n",
44162306a36Sopenharmony_ci					      "abort in progress xri=",
44262306a36Sopenharmony_ci					      io->indicator, io->reqtag);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci			} else {
44562306a36Sopenharmony_ci				/* Failed to abort for some other reason, log
44662306a36Sopenharmony_ci				 * error
44762306a36Sopenharmony_ci				 */
44862306a36Sopenharmony_ci				efc_log_debug(hw->os, "%s%#x tag=%#x rc=%d\n",
44962306a36Sopenharmony_ci					      "Failed to abort xri=",
45062306a36Sopenharmony_ci					      io->indicator, io->reqtag, rc);
45162306a36Sopenharmony_ci			}
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (io->done) {
45662306a36Sopenharmony_ci		efct_hw_done_t done = io->done;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		io->done = NULL;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		if (io->status_saved) {
46162306a36Sopenharmony_ci			/* use latched status if exists */
46262306a36Sopenharmony_ci			status = io->saved_status;
46362306a36Sopenharmony_ci			len = io->saved_len;
46462306a36Sopenharmony_ci			ext = io->saved_ext;
46562306a36Sopenharmony_ci			io->status_saved = false;
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		/* Restore default SGL */
46962306a36Sopenharmony_ci		efct_hw_io_restore_sgl(hw, io);
47062306a36Sopenharmony_ci		done(io, len, status, ext, io->arg);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ciexit_efct_hw_wq_process_io:
47462306a36Sopenharmony_ci	return;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int
47862306a36Sopenharmony_ciefct_hw_setup_io(struct efct_hw *hw)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	u32	i = 0;
48162306a36Sopenharmony_ci	struct efct_hw_io	*io = NULL;
48262306a36Sopenharmony_ci	uintptr_t	xfer_virt = 0;
48362306a36Sopenharmony_ci	uintptr_t	xfer_phys = 0;
48462306a36Sopenharmony_ci	u32	index;
48562306a36Sopenharmony_ci	bool new_alloc = true;
48662306a36Sopenharmony_ci	struct efc_dma *dma;
48762306a36Sopenharmony_ci	struct efct *efct = hw->os;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (!hw->io) {
49062306a36Sopenharmony_ci		hw->io = kmalloc_array(hw->config.n_io, sizeof(io), GFP_KERNEL);
49162306a36Sopenharmony_ci		if (!hw->io)
49262306a36Sopenharmony_ci			return -ENOMEM;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		memset(hw->io, 0, hw->config.n_io * sizeof(io));
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		for (i = 0; i < hw->config.n_io; i++) {
49762306a36Sopenharmony_ci			hw->io[i] = kzalloc(sizeof(*io), GFP_KERNEL);
49862306a36Sopenharmony_ci			if (!hw->io[i])
49962306a36Sopenharmony_ci				goto error;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		/* Create WQE buffs for IO */
50362306a36Sopenharmony_ci		hw->wqe_buffs = kzalloc((hw->config.n_io * hw->sli.wqe_size),
50462306a36Sopenharmony_ci					GFP_KERNEL);
50562306a36Sopenharmony_ci		if (!hw->wqe_buffs) {
50662306a36Sopenharmony_ci			kfree(hw->io);
50762306a36Sopenharmony_ci			return -ENOMEM;
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	} else {
51162306a36Sopenharmony_ci		/* re-use existing IOs, including SGLs */
51262306a36Sopenharmony_ci		new_alloc = false;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (new_alloc) {
51662306a36Sopenharmony_ci		dma = &hw->xfer_rdy;
51762306a36Sopenharmony_ci		dma->size = sizeof(struct fcp_txrdy) * hw->config.n_io;
51862306a36Sopenharmony_ci		dma->virt = dma_alloc_coherent(&efct->pci->dev,
51962306a36Sopenharmony_ci					       dma->size, &dma->phys, GFP_KERNEL);
52062306a36Sopenharmony_ci		if (!dma->virt)
52162306a36Sopenharmony_ci			return -ENOMEM;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
52462306a36Sopenharmony_ci	xfer_phys = hw->xfer_rdy.phys;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Initialize the pool of HW IO objects */
52762306a36Sopenharmony_ci	for (i = 0; i < hw->config.n_io; i++) {
52862306a36Sopenharmony_ci		struct hw_wq_callback *wqcb;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		io = hw->io[i];
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		/* initialize IO fields */
53362306a36Sopenharmony_ci		io->hw = hw;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/* Assign a WQE buff */
53662306a36Sopenharmony_ci		io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.wqe_size];
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		/* Allocate the request tag for this IO */
53962306a36Sopenharmony_ci		wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_io, io);
54062306a36Sopenharmony_ci		if (!wqcb) {
54162306a36Sopenharmony_ci			efc_log_err(hw->os, "can't allocate request tag\n");
54262306a36Sopenharmony_ci			return -ENOSPC;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci		io->reqtag = wqcb->instance_index;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		/* Now for the fields that are initialized on each free */
54762306a36Sopenharmony_ci		efct_hw_init_free_io(io);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* The XB flag isn't cleared on IO free, so init to zero */
55062306a36Sopenharmony_ci		io->xbusy = 0;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		if (sli_resource_alloc(&hw->sli, SLI4_RSRC_XRI,
55362306a36Sopenharmony_ci				       &io->indicator, &index)) {
55462306a36Sopenharmony_ci			efc_log_err(hw->os,
55562306a36Sopenharmony_ci				    "sli_resource_alloc failed @ %d\n", i);
55662306a36Sopenharmony_ci			return -ENOMEM;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		if (new_alloc) {
56062306a36Sopenharmony_ci			dma = &io->def_sgl;
56162306a36Sopenharmony_ci			dma->size = hw->config.n_sgl *
56262306a36Sopenharmony_ci					sizeof(struct sli4_sge);
56362306a36Sopenharmony_ci			dma->virt = dma_alloc_coherent(&efct->pci->dev,
56462306a36Sopenharmony_ci						       dma->size, &dma->phys,
56562306a36Sopenharmony_ci						       GFP_KERNEL);
56662306a36Sopenharmony_ci			if (!dma->virt) {
56762306a36Sopenharmony_ci				efc_log_err(hw->os, "dma_alloc fail %d\n", i);
56862306a36Sopenharmony_ci				memset(&io->def_sgl, 0,
56962306a36Sopenharmony_ci				       sizeof(struct efc_dma));
57062306a36Sopenharmony_ci				return -ENOMEM;
57162306a36Sopenharmony_ci			}
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci		io->def_sgl_count = hw->config.n_sgl;
57462306a36Sopenharmony_ci		io->sgl = &io->def_sgl;
57562306a36Sopenharmony_ci		io->sgl_count = io->def_sgl_count;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		if (hw->xfer_rdy.size) {
57862306a36Sopenharmony_ci			io->xfer_rdy.virt = (void *)xfer_virt;
57962306a36Sopenharmony_ci			io->xfer_rdy.phys = xfer_phys;
58062306a36Sopenharmony_ci			io->xfer_rdy.size = sizeof(struct fcp_txrdy);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci			xfer_virt += sizeof(struct fcp_txrdy);
58362306a36Sopenharmony_ci			xfer_phys += sizeof(struct fcp_txrdy);
58462306a36Sopenharmony_ci		}
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_cierror:
58962306a36Sopenharmony_ci	for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
59062306a36Sopenharmony_ci		kfree(hw->io[i]);
59162306a36Sopenharmony_ci		hw->io[i] = NULL;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	kfree(hw->io);
59562306a36Sopenharmony_ci	hw->io = NULL;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return -ENOMEM;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int
60162306a36Sopenharmony_ciefct_hw_init_prereg_io(struct efct_hw *hw)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	u32 i, idx = 0;
60462306a36Sopenharmony_ci	struct efct_hw_io *io = NULL;
60562306a36Sopenharmony_ci	u8 cmd[SLI4_BMBX_SIZE];
60662306a36Sopenharmony_ci	int rc = 0;
60762306a36Sopenharmony_ci	u32 n_rem;
60862306a36Sopenharmony_ci	u32 n = 0;
60962306a36Sopenharmony_ci	u32 sgls_per_request = 256;
61062306a36Sopenharmony_ci	struct efc_dma **sgls = NULL;
61162306a36Sopenharmony_ci	struct efc_dma req;
61262306a36Sopenharmony_ci	struct efct *efct = hw->os;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	sgls = kmalloc_array(sgls_per_request, sizeof(*sgls), GFP_KERNEL);
61562306a36Sopenharmony_ci	if (!sgls)
61662306a36Sopenharmony_ci		return -ENOMEM;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	memset(&req, 0, sizeof(struct efc_dma));
61962306a36Sopenharmony_ci	req.size = 32 + sgls_per_request * 16;
62062306a36Sopenharmony_ci	req.virt = dma_alloc_coherent(&efct->pci->dev, req.size, &req.phys,
62162306a36Sopenharmony_ci				      GFP_KERNEL);
62262306a36Sopenharmony_ci	if (!req.virt) {
62362306a36Sopenharmony_ci		kfree(sgls);
62462306a36Sopenharmony_ci		return -ENOMEM;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	for (n_rem = hw->config.n_io; n_rem; n_rem -= n) {
62862306a36Sopenharmony_ci		/* Copy address of SGL's into local sgls[] array, break
62962306a36Sopenharmony_ci		 * out if the xri is not contiguous.
63062306a36Sopenharmony_ci		 */
63162306a36Sopenharmony_ci		u32 min = (sgls_per_request < n_rem) ? sgls_per_request : n_rem;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		for (n = 0; n < min; n++) {
63462306a36Sopenharmony_ci			/* Check that we have contiguous xri values */
63562306a36Sopenharmony_ci			if (n > 0) {
63662306a36Sopenharmony_ci				if (hw->io[idx + n]->indicator !=
63762306a36Sopenharmony_ci				    hw->io[idx + n - 1]->indicator + 1)
63862306a36Sopenharmony_ci					break;
63962306a36Sopenharmony_ci			}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			sgls[n] = hw->io[idx + n]->sgl;
64262306a36Sopenharmony_ci		}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		if (sli_cmd_post_sgl_pages(&hw->sli, cmd,
64562306a36Sopenharmony_ci				hw->io[idx]->indicator,	n, sgls, NULL, &req)) {
64662306a36Sopenharmony_ci			rc = -EIO;
64762306a36Sopenharmony_ci			break;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		rc = efct_hw_command(hw, cmd, EFCT_CMD_POLL, NULL, NULL);
65162306a36Sopenharmony_ci		if (rc) {
65262306a36Sopenharmony_ci			efc_log_err(hw->os, "SGL post failed, rc=%d\n", rc);
65362306a36Sopenharmony_ci			break;
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		/* Add to tail if successful */
65762306a36Sopenharmony_ci		for (i = 0; i < n; i++, idx++) {
65862306a36Sopenharmony_ci			io = hw->io[idx];
65962306a36Sopenharmony_ci			io->state = EFCT_HW_IO_STATE_FREE;
66062306a36Sopenharmony_ci			INIT_LIST_HEAD(&io->list_entry);
66162306a36Sopenharmony_ci			list_add_tail(&io->list_entry, &hw->io_free);
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	dma_free_coherent(&efct->pci->dev, req.size, req.virt, req.phys);
66662306a36Sopenharmony_ci	memset(&req, 0, sizeof(struct efc_dma));
66762306a36Sopenharmony_ci	kfree(sgls);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return rc;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic int
67362306a36Sopenharmony_ciefct_hw_init_io(struct efct_hw *hw)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	u32 i, idx = 0;
67662306a36Sopenharmony_ci	bool prereg = false;
67762306a36Sopenharmony_ci	struct efct_hw_io *io = NULL;
67862306a36Sopenharmony_ci	int rc = 0;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	prereg = hw->sli.params.sgl_pre_registered;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (prereg)
68362306a36Sopenharmony_ci		return efct_hw_init_prereg_io(hw);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	for (i = 0; i < hw->config.n_io; i++, idx++) {
68662306a36Sopenharmony_ci		io = hw->io[idx];
68762306a36Sopenharmony_ci		io->state = EFCT_HW_IO_STATE_FREE;
68862306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->list_entry);
68962306a36Sopenharmony_ci		list_add_tail(&io->list_entry, &hw->io_free);
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return rc;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int
69662306a36Sopenharmony_ciefct_hw_config_set_fdt_xfer_hint(struct efct_hw *hw, u32 fdt_xfer_hint)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	int rc = 0;
69962306a36Sopenharmony_ci	u8 buf[SLI4_BMBX_SIZE];
70062306a36Sopenharmony_ci	struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint param;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	memset(&param, 0, sizeof(param));
70362306a36Sopenharmony_ci	param.fdt_xfer_hint = cpu_to_le32(fdt_xfer_hint);
70462306a36Sopenharmony_ci	/* build the set_features command */
70562306a36Sopenharmony_ci	sli_cmd_common_set_features(&hw->sli, buf,
70662306a36Sopenharmony_ci		SLI4_SET_FEATURES_SET_FTD_XFER_HINT, sizeof(param), &param);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
70962306a36Sopenharmony_ci	if (rc)
71062306a36Sopenharmony_ci		efc_log_warn(hw->os, "set FDT hint %d failed: %d\n",
71162306a36Sopenharmony_ci			     fdt_xfer_hint, rc);
71262306a36Sopenharmony_ci	else
71362306a36Sopenharmony_ci		efc_log_info(hw->os, "Set FTD transfer hint to %d\n",
71462306a36Sopenharmony_ci			     le32_to_cpu(param.fdt_xfer_hint));
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return rc;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int
72062306a36Sopenharmony_ciefct_hw_config_rq(struct efct_hw *hw)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	u32 min_rq_count, i, rc;
72362306a36Sopenharmony_ci	struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
72462306a36Sopenharmony_ci	u8 buf[SLI4_BMBX_SIZE];
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	efc_log_info(hw->os, "using REG_FCFI standard\n");
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/*
72962306a36Sopenharmony_ci	 * Set the filter match/mask values from hw's
73062306a36Sopenharmony_ci	 * filter_def values
73162306a36Sopenharmony_ci	 */
73262306a36Sopenharmony_ci	for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
73362306a36Sopenharmony_ci		rq_cfg[i].rq_id = cpu_to_le16(0xffff);
73462306a36Sopenharmony_ci		rq_cfg[i].r_ctl_mask = (u8)hw->config.filter_def[i];
73562306a36Sopenharmony_ci		rq_cfg[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 8);
73662306a36Sopenharmony_ci		rq_cfg[i].type_mask = (u8)(hw->config.filter_def[i] >> 16);
73762306a36Sopenharmony_ci		rq_cfg[i].type_match = (u8)(hw->config.filter_def[i] >> 24);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/*
74162306a36Sopenharmony_ci	 * Update the rq_id's of the FCF configuration
74262306a36Sopenharmony_ci	 * (don't update more than the number of rq_cfg
74362306a36Sopenharmony_ci	 * elements)
74462306a36Sopenharmony_ci	 */
74562306a36Sopenharmony_ci	min_rq_count = (hw->hw_rq_count < SLI4_CMD_REG_FCFI_NUM_RQ_CFG)	?
74662306a36Sopenharmony_ci			hw->hw_rq_count : SLI4_CMD_REG_FCFI_NUM_RQ_CFG;
74762306a36Sopenharmony_ci	for (i = 0; i < min_rq_count; i++) {
74862306a36Sopenharmony_ci		struct hw_rq *rq = hw->hw_rq[i];
74962306a36Sopenharmony_ci		u32 j;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
75262306a36Sopenharmony_ci			u32 mask = (rq->filter_mask != 0) ?
75362306a36Sopenharmony_ci				rq->filter_mask : 1;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci			if (!(mask & (1U << j)))
75662306a36Sopenharmony_ci				continue;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci			rq_cfg[i].rq_id = cpu_to_le16(rq->hdr->id);
75962306a36Sopenharmony_ci			efct_logfcfi(hw, j, i, rq->hdr->id);
76062306a36Sopenharmony_ci		}
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	rc = -EIO;
76462306a36Sopenharmony_ci	if (!sli_cmd_reg_fcfi(&hw->sli, buf, 0,	rq_cfg))
76562306a36Sopenharmony_ci		rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (rc != 0) {
76862306a36Sopenharmony_ci		efc_log_err(hw->os, "FCFI registration failed\n");
76962306a36Sopenharmony_ci		return rc;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci	hw->fcf_indicator =
77262306a36Sopenharmony_ci		le16_to_cpu(((struct sli4_cmd_reg_fcfi *)buf)->fcfi);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return rc;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic int
77862306a36Sopenharmony_ciefct_hw_config_mrq(struct efct_hw *hw, u8 mode, u16 fcf_index)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	u8 buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
78162306a36Sopenharmony_ci	struct hw_rq *rq;
78262306a36Sopenharmony_ci	struct sli4_cmd_reg_fcfi_mrq *rsp = NULL;
78362306a36Sopenharmony_ci	struct sli4_cmd_rq_cfg rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
78462306a36Sopenharmony_ci	u32 rc, i;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE)
78762306a36Sopenharmony_ci		goto issue_cmd;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Set the filter match/mask values from hw's filter_def values */
79062306a36Sopenharmony_ci	for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
79162306a36Sopenharmony_ci		rq_filter[i].rq_id = cpu_to_le16(0xffff);
79262306a36Sopenharmony_ci		rq_filter[i].type_mask = (u8)hw->config.filter_def[i];
79362306a36Sopenharmony_ci		rq_filter[i].type_match = (u8)(hw->config.filter_def[i] >> 8);
79462306a36Sopenharmony_ci		rq_filter[i].r_ctl_mask = (u8)(hw->config.filter_def[i] >> 16);
79562306a36Sopenharmony_ci		rq_filter[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 24);
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	rq = hw->hw_rq[0];
79962306a36Sopenharmony_ci	rq_filter[0].rq_id = cpu_to_le16(rq->hdr->id);
80062306a36Sopenharmony_ci	rq_filter[1].rq_id = cpu_to_le16(rq->hdr->id);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	mrq_bitmask = 0x2;
80362306a36Sopenharmony_ciissue_cmd:
80462306a36Sopenharmony_ci	efc_log_debug(hw->os, "Issue reg_fcfi_mrq count:%d policy:%d mode:%d\n",
80562306a36Sopenharmony_ci		      hw->hw_rq_count, hw->config.rq_selection_policy, mode);
80662306a36Sopenharmony_ci	/* Invoke REG_FCFI_MRQ */
80762306a36Sopenharmony_ci	rc = sli_cmd_reg_fcfi_mrq(&hw->sli, buf, mode, fcf_index,
80862306a36Sopenharmony_ci				  hw->config.rq_selection_policy, mrq_bitmask,
80962306a36Sopenharmony_ci				  hw->hw_mrq_count, rq_filter);
81062306a36Sopenharmony_ci	if (rc) {
81162306a36Sopenharmony_ci		efc_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed\n");
81262306a36Sopenharmony_ci		return -EIO;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	rsp = (struct sli4_cmd_reg_fcfi_mrq *)buf;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if ((rc) || (le16_to_cpu(rsp->hdr.status))) {
82062306a36Sopenharmony_ci		efc_log_err(hw->os, "FCFI MRQ reg failed. cmd=%x status=%x\n",
82162306a36Sopenharmony_ci			    rsp->hdr.command, le16_to_cpu(rsp->hdr.status));
82262306a36Sopenharmony_ci		return -EIO;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE)
82662306a36Sopenharmony_ci		hw->fcf_indicator = le16_to_cpu(rsp->fcfi);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return 0;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic void
83262306a36Sopenharmony_ciefct_hw_queue_hash_add(struct efct_queue_hash *hash,
83362306a36Sopenharmony_ci		       u16 id, u16 index)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	u32 hash_index = id & (EFCT_HW_Q_HASH_SIZE - 1);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	/*
83862306a36Sopenharmony_ci	 * Since the hash is always bigger than the number of queues, then we
83962306a36Sopenharmony_ci	 * never have to worry about an infinite loop.
84062306a36Sopenharmony_ci	 */
84162306a36Sopenharmony_ci	while (hash[hash_index].in_use)
84262306a36Sopenharmony_ci		hash_index = (hash_index + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* not used, claim the entry */
84562306a36Sopenharmony_ci	hash[hash_index].id = id;
84662306a36Sopenharmony_ci	hash[hash_index].in_use = true;
84762306a36Sopenharmony_ci	hash[hash_index].index = index;
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic int
85162306a36Sopenharmony_ciefct_hw_config_sli_port_health_check(struct efct_hw *hw, u8 query, u8 enable)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	int rc = 0;
85462306a36Sopenharmony_ci	u8 buf[SLI4_BMBX_SIZE];
85562306a36Sopenharmony_ci	struct sli4_rqst_cmn_set_features_health_check param;
85662306a36Sopenharmony_ci	u32 health_check_flag = 0;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	memset(&param, 0, sizeof(param));
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (enable)
86162306a36Sopenharmony_ci		health_check_flag |= SLI4_RQ_HEALTH_CHECK_ENABLE;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (query)
86462306a36Sopenharmony_ci		health_check_flag |= SLI4_RQ_HEALTH_CHECK_QUERY;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	param.health_check_dword = cpu_to_le32(health_check_flag);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* build the set_features command */
86962306a36Sopenharmony_ci	sli_cmd_common_set_features(&hw->sli, buf,
87062306a36Sopenharmony_ci		SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK, sizeof(param), &param);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
87362306a36Sopenharmony_ci	if (rc)
87462306a36Sopenharmony_ci		efc_log_err(hw->os, "efct_hw_command returns %d\n", rc);
87562306a36Sopenharmony_ci	else
87662306a36Sopenharmony_ci		efc_log_debug(hw->os, "SLI Port Health Check is enabled\n");
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	return rc;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ciint
88262306a36Sopenharmony_ciefct_hw_init(struct efct_hw *hw)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	int rc;
88562306a36Sopenharmony_ci	u32 i = 0;
88662306a36Sopenharmony_ci	int rem_count;
88762306a36Sopenharmony_ci	unsigned long flags = 0;
88862306a36Sopenharmony_ci	struct efct_hw_io *temp;
88962306a36Sopenharmony_ci	struct efc_dma *dma;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/*
89262306a36Sopenharmony_ci	 * Make sure the command lists are empty. If this is start-of-day,
89362306a36Sopenharmony_ci	 * they'll be empty since they were just initialized in efct_hw_setup.
89462306a36Sopenharmony_ci	 * If we've just gone through a reset, the command and command pending
89562306a36Sopenharmony_ci	 * lists should have been cleaned up as part of the reset
89662306a36Sopenharmony_ci	 * (efct_hw_reset()).
89762306a36Sopenharmony_ci	 */
89862306a36Sopenharmony_ci	spin_lock_irqsave(&hw->cmd_lock, flags);
89962306a36Sopenharmony_ci	if (!list_empty(&hw->cmd_head)) {
90062306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->cmd_lock, flags);
90162306a36Sopenharmony_ci		efc_log_err(hw->os, "command found on cmd list\n");
90262306a36Sopenharmony_ci		return -EIO;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	if (!list_empty(&hw->cmd_pending)) {
90562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->cmd_lock, flags);
90662306a36Sopenharmony_ci		efc_log_err(hw->os, "command found on pending list\n");
90762306a36Sopenharmony_ci		return -EIO;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->cmd_lock, flags);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* Free RQ buffers if prevously allocated */
91262306a36Sopenharmony_ci	efct_hw_rx_free(hw);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/*
91562306a36Sopenharmony_ci	 * The IO queues must be initialized here for the reset case. The
91662306a36Sopenharmony_ci	 * efct_hw_init_io() function will re-add the IOs to the free list.
91762306a36Sopenharmony_ci	 * The cmd_head list should be OK since we free all entries in
91862306a36Sopenharmony_ci	 * efct_hw_command_cancel() that is called in the efct_hw_reset().
91962306a36Sopenharmony_ci	 */
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* If we are in this function due to a reset, there may be stale items
92262306a36Sopenharmony_ci	 * on lists that need to be removed.  Clean them up.
92362306a36Sopenharmony_ci	 */
92462306a36Sopenharmony_ci	rem_count = 0;
92562306a36Sopenharmony_ci	while ((!list_empty(&hw->io_wait_free))) {
92662306a36Sopenharmony_ci		rem_count++;
92762306a36Sopenharmony_ci		temp = list_first_entry(&hw->io_wait_free, struct efct_hw_io,
92862306a36Sopenharmony_ci					list_entry);
92962306a36Sopenharmony_ci		list_del_init(&temp->list_entry);
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci	if (rem_count > 0)
93262306a36Sopenharmony_ci		efc_log_debug(hw->os, "rmvd %d items from io_wait_free list\n",
93362306a36Sopenharmony_ci			      rem_count);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	rem_count = 0;
93662306a36Sopenharmony_ci	while ((!list_empty(&hw->io_inuse))) {
93762306a36Sopenharmony_ci		rem_count++;
93862306a36Sopenharmony_ci		temp = list_first_entry(&hw->io_inuse, struct efct_hw_io,
93962306a36Sopenharmony_ci					list_entry);
94062306a36Sopenharmony_ci		list_del_init(&temp->list_entry);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci	if (rem_count > 0)
94362306a36Sopenharmony_ci		efc_log_debug(hw->os, "rmvd %d items from io_inuse list\n",
94462306a36Sopenharmony_ci			      rem_count);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	rem_count = 0;
94762306a36Sopenharmony_ci	while ((!list_empty(&hw->io_free))) {
94862306a36Sopenharmony_ci		rem_count++;
94962306a36Sopenharmony_ci		temp = list_first_entry(&hw->io_free, struct efct_hw_io,
95062306a36Sopenharmony_ci					list_entry);
95162306a36Sopenharmony_ci		list_del_init(&temp->list_entry);
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	if (rem_count > 0)
95462306a36Sopenharmony_ci		efc_log_debug(hw->os, "rmvd %d items from io_free list\n",
95562306a36Sopenharmony_ci			      rem_count);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* If MRQ not required, Make sure we dont request feature. */
95862306a36Sopenharmony_ci	if (hw->config.n_rq == 1)
95962306a36Sopenharmony_ci		hw->sli.features &= (~SLI4_REQFEAT_MRQP);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (sli_init(&hw->sli)) {
96262306a36Sopenharmony_ci		efc_log_err(hw->os, "SLI failed to initialize\n");
96362306a36Sopenharmony_ci		return -EIO;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (hw->sliport_healthcheck) {
96762306a36Sopenharmony_ci		rc = efct_hw_config_sli_port_health_check(hw, 0, 1);
96862306a36Sopenharmony_ci		if (rc != 0) {
96962306a36Sopenharmony_ci			efc_log_err(hw->os, "Enable port Health check fail\n");
97062306a36Sopenharmony_ci			return rc;
97162306a36Sopenharmony_ci		}
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	/*
97562306a36Sopenharmony_ci	 * Set FDT transfer hint, only works on Lancer
97662306a36Sopenharmony_ci	 */
97762306a36Sopenharmony_ci	if (hw->sli.if_type == SLI4_INTF_IF_TYPE_2) {
97862306a36Sopenharmony_ci		/*
97962306a36Sopenharmony_ci		 * Non-fatal error. In particular, we can disregard failure to
98062306a36Sopenharmony_ci		 * set EFCT_HW_FDT_XFER_HINT on devices with legacy firmware
98162306a36Sopenharmony_ci		 * that do not support EFCT_HW_FDT_XFER_HINT feature.
98262306a36Sopenharmony_ci		 */
98362306a36Sopenharmony_ci		efct_hw_config_set_fdt_xfer_hint(hw, EFCT_HW_FDT_XFER_HINT);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* zero the hashes */
98762306a36Sopenharmony_ci	memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
98862306a36Sopenharmony_ci	efc_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
98962306a36Sopenharmony_ci		      EFCT_HW_MAX_NUM_CQ, EFCT_HW_Q_HASH_SIZE);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
99262306a36Sopenharmony_ci	efc_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
99362306a36Sopenharmony_ci		      EFCT_HW_MAX_NUM_RQ, EFCT_HW_Q_HASH_SIZE);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
99662306a36Sopenharmony_ci	efc_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
99762306a36Sopenharmony_ci		      EFCT_HW_MAX_NUM_WQ, EFCT_HW_Q_HASH_SIZE);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	rc = efct_hw_init_queues(hw);
100062306a36Sopenharmony_ci	if (rc)
100162306a36Sopenharmony_ci		return rc;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	rc = efct_hw_map_wq_cpu(hw);
100462306a36Sopenharmony_ci	if (rc)
100562306a36Sopenharmony_ci		return rc;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/* Allocate and p_st RQ buffers */
100862306a36Sopenharmony_ci	rc = efct_hw_rx_allocate(hw);
100962306a36Sopenharmony_ci	if (rc) {
101062306a36Sopenharmony_ci		efc_log_err(hw->os, "rx_allocate failed\n");
101162306a36Sopenharmony_ci		return rc;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	rc = efct_hw_rx_post(hw);
101562306a36Sopenharmony_ci	if (rc) {
101662306a36Sopenharmony_ci		efc_log_err(hw->os, "WARNING - error posting RQ buffers\n");
101762306a36Sopenharmony_ci		return rc;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (hw->config.n_eq == 1) {
102162306a36Sopenharmony_ci		rc = efct_hw_config_rq(hw);
102262306a36Sopenharmony_ci		if (rc) {
102362306a36Sopenharmony_ci			efc_log_err(hw->os, "config rq failed %d\n", rc);
102462306a36Sopenharmony_ci			return rc;
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	} else {
102762306a36Sopenharmony_ci		rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0);
102862306a36Sopenharmony_ci		if (rc != 0) {
102962306a36Sopenharmony_ci			efc_log_err(hw->os, "REG_FCFI_MRQ FCFI reg failed\n");
103062306a36Sopenharmony_ci			return rc;
103162306a36Sopenharmony_ci		}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0);
103462306a36Sopenharmony_ci		if (rc != 0) {
103562306a36Sopenharmony_ci			efc_log_err(hw->os, "REG_FCFI_MRQ MRQ reg failed\n");
103662306a36Sopenharmony_ci			return rc;
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/*
104162306a36Sopenharmony_ci	 * Allocate the WQ request tag pool, if not previously allocated
104262306a36Sopenharmony_ci	 * (the request tag value is 16 bits, thus the pool allocation size
104362306a36Sopenharmony_ci	 * of 64k)
104462306a36Sopenharmony_ci	 */
104562306a36Sopenharmony_ci	hw->wq_reqtag_pool = efct_hw_reqtag_pool_alloc(hw);
104662306a36Sopenharmony_ci	if (!hw->wq_reqtag_pool) {
104762306a36Sopenharmony_ci		efc_log_err(hw->os, "efct_hw_reqtag_pool_alloc failed\n");
104862306a36Sopenharmony_ci		return -ENOMEM;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	rc = efct_hw_setup_io(hw);
105262306a36Sopenharmony_ci	if (rc) {
105362306a36Sopenharmony_ci		efc_log_err(hw->os, "IO allocation failure\n");
105462306a36Sopenharmony_ci		return rc;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	rc = efct_hw_init_io(hw);
105862306a36Sopenharmony_ci	if (rc) {
105962306a36Sopenharmony_ci		efc_log_err(hw->os, "IO initialization failure\n");
106062306a36Sopenharmony_ci		return rc;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	dma = &hw->loop_map;
106462306a36Sopenharmony_ci	dma->size = SLI4_MIN_LOOP_MAP_BYTES;
106562306a36Sopenharmony_ci	dma->virt = dma_alloc_coherent(&hw->os->pci->dev, dma->size, &dma->phys,
106662306a36Sopenharmony_ci				       GFP_KERNEL);
106762306a36Sopenharmony_ci	if (!dma->virt)
106862306a36Sopenharmony_ci		return -EIO;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/*
107162306a36Sopenharmony_ci	 * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ
107262306a36Sopenharmony_ci	 * entries
107362306a36Sopenharmony_ci	 */
107462306a36Sopenharmony_ci	for (i = 0; i < hw->eq_count; i++)
107562306a36Sopenharmony_ci		sli_queue_arm(&hw->sli, &hw->eq[i], true);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/*
107862306a36Sopenharmony_ci	 * Initialize RQ hash
107962306a36Sopenharmony_ci	 */
108062306a36Sopenharmony_ci	for (i = 0; i < hw->rq_count; i++)
108162306a36Sopenharmony_ci		efct_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/*
108462306a36Sopenharmony_ci	 * Initialize WQ hash
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	for (i = 0; i < hw->wq_count; i++)
108762306a36Sopenharmony_ci		efct_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	/*
109062306a36Sopenharmony_ci	 * Arming the CQ allows (e.g.) MQ completions to write CQ entries
109162306a36Sopenharmony_ci	 */
109262306a36Sopenharmony_ci	for (i = 0; i < hw->cq_count; i++) {
109362306a36Sopenharmony_ci		efct_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
109462306a36Sopenharmony_ci		sli_queue_arm(&hw->sli, &hw->cq[i], true);
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Set RQ process limit*/
109862306a36Sopenharmony_ci	for (i = 0; i < hw->hw_rq_count; i++) {
109962306a36Sopenharmony_ci		struct hw_rq *rq = hw->hw_rq[i];
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		hw->cq[rq->cq->instance].proc_limit = hw->config.n_io / 2;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* record the fact that the queues are functional */
110562306a36Sopenharmony_ci	hw->state = EFCT_HW_STATE_ACTIVE;
110662306a36Sopenharmony_ci	/*
110762306a36Sopenharmony_ci	 * Allocate a HW IOs for send frame.
110862306a36Sopenharmony_ci	 */
110962306a36Sopenharmony_ci	hw->hw_wq[0]->send_frame_io = efct_hw_io_alloc(hw);
111062306a36Sopenharmony_ci	if (!hw->hw_wq[0]->send_frame_io)
111162306a36Sopenharmony_ci		efc_log_err(hw->os, "alloc for send_frame_io failed\n");
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* Initialize send frame sequence id */
111462306a36Sopenharmony_ci	atomic_set(&hw->send_frame_seq_id, 0);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return 0;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ciint
112062306a36Sopenharmony_ciefct_hw_parse_filter(struct efct_hw *hw, void *value)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	int rc = 0;
112362306a36Sopenharmony_ci	char *p = NULL;
112462306a36Sopenharmony_ci	char *token;
112562306a36Sopenharmony_ci	u32 idx = 0;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++)
112862306a36Sopenharmony_ci		hw->config.filter_def[idx] = 0;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	p = kstrdup(value, GFP_KERNEL);
113162306a36Sopenharmony_ci	if (!p || !*p) {
113262306a36Sopenharmony_ci		efc_log_err(hw->os, "p is NULL\n");
113362306a36Sopenharmony_ci		return -ENOMEM;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	idx = 0;
113762306a36Sopenharmony_ci	while ((token = strsep(&p, ",")) && *token) {
113862306a36Sopenharmony_ci		if (kstrtou32(token, 0, &hw->config.filter_def[idx++]))
113962306a36Sopenharmony_ci			efc_log_err(hw->os, "kstrtoint failed\n");
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		if (!p || !*p)
114262306a36Sopenharmony_ci			break;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		if (idx == ARRAY_SIZE(hw->config.filter_def))
114562306a36Sopenharmony_ci			break;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	kfree(p);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	return rc;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ciu64
115362306a36Sopenharmony_ciefct_get_wwnn(struct efct_hw *hw)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct sli4 *sli = &hw->sli;
115662306a36Sopenharmony_ci	u8 p[8];
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	memcpy(p, sli->wwnn, sizeof(p));
115962306a36Sopenharmony_ci	return get_unaligned_be64(p);
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ciu64
116362306a36Sopenharmony_ciefct_get_wwpn(struct efct_hw *hw)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct sli4 *sli = &hw->sli;
116662306a36Sopenharmony_ci	u8 p[8];
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	memcpy(p, sli->wwpn, sizeof(p));
116962306a36Sopenharmony_ci	return get_unaligned_be64(p);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic struct efc_hw_rq_buffer *
117362306a36Sopenharmony_ciefct_hw_rx_buffer_alloc(struct efct_hw *hw, u32 rqindex, u32 count,
117462306a36Sopenharmony_ci			u32 size)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct efct *efct = hw->os;
117762306a36Sopenharmony_ci	struct efc_hw_rq_buffer *rq_buf = NULL;
117862306a36Sopenharmony_ci	struct efc_hw_rq_buffer *prq;
117962306a36Sopenharmony_ci	u32 i;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (!count)
118262306a36Sopenharmony_ci		return NULL;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	rq_buf = kmalloc_array(count, sizeof(*rq_buf), GFP_KERNEL);
118562306a36Sopenharmony_ci	if (!rq_buf)
118662306a36Sopenharmony_ci		return NULL;
118762306a36Sopenharmony_ci	memset(rq_buf, 0, sizeof(*rq_buf) * count);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
119062306a36Sopenharmony_ci		prq->rqindex = rqindex;
119162306a36Sopenharmony_ci		prq->dma.size = size;
119262306a36Sopenharmony_ci		prq->dma.virt = dma_alloc_coherent(&efct->pci->dev,
119362306a36Sopenharmony_ci						   prq->dma.size,
119462306a36Sopenharmony_ci						   &prq->dma.phys,
119562306a36Sopenharmony_ci						   GFP_KERNEL);
119662306a36Sopenharmony_ci		if (!prq->dma.virt) {
119762306a36Sopenharmony_ci			efc_log_err(hw->os, "DMA allocation failed\n");
119862306a36Sopenharmony_ci			kfree(rq_buf);
119962306a36Sopenharmony_ci			return NULL;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci	return rq_buf;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic void
120662306a36Sopenharmony_ciefct_hw_rx_buffer_free(struct efct_hw *hw,
120762306a36Sopenharmony_ci		       struct efc_hw_rq_buffer *rq_buf,
120862306a36Sopenharmony_ci			u32 count)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct efct *efct = hw->os;
121162306a36Sopenharmony_ci	u32 i;
121262306a36Sopenharmony_ci	struct efc_hw_rq_buffer *prq;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (rq_buf) {
121562306a36Sopenharmony_ci		for (i = 0, prq = rq_buf; i < count; i++, prq++) {
121662306a36Sopenharmony_ci			dma_free_coherent(&efct->pci->dev,
121762306a36Sopenharmony_ci					  prq->dma.size, prq->dma.virt,
121862306a36Sopenharmony_ci					  prq->dma.phys);
121962306a36Sopenharmony_ci			memset(&prq->dma, 0, sizeof(struct efc_dma));
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		kfree(rq_buf);
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ciint
122762306a36Sopenharmony_ciefct_hw_rx_allocate(struct efct_hw *hw)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	struct efct *efct = hw->os;
123062306a36Sopenharmony_ci	u32 i;
123162306a36Sopenharmony_ci	int rc = 0;
123262306a36Sopenharmony_ci	u32 rqindex = 0;
123362306a36Sopenharmony_ci	u32 hdr_size = EFCT_HW_RQ_SIZE_HDR;
123462306a36Sopenharmony_ci	u32 payload_size = hw->config.rq_default_buffer_size;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	rqindex = 0;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	for (i = 0; i < hw->hw_rq_count; i++) {
123962306a36Sopenharmony_ci		struct hw_rq *rq = hw->hw_rq[i];
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci		/* Allocate header buffers */
124262306a36Sopenharmony_ci		rq->hdr_buf = efct_hw_rx_buffer_alloc(hw, rqindex,
124362306a36Sopenharmony_ci						      rq->entry_count,
124462306a36Sopenharmony_ci						      hdr_size);
124562306a36Sopenharmony_ci		if (!rq->hdr_buf) {
124662306a36Sopenharmony_ci			efc_log_err(efct, "rx_buffer_alloc hdr_buf failed\n");
124762306a36Sopenharmony_ci			rc = -EIO;
124862306a36Sopenharmony_ci			break;
124962306a36Sopenharmony_ci		}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci		efc_log_debug(hw->os,
125262306a36Sopenharmony_ci			      "rq[%2d] rq_id %02d header  %4d by %4d bytes\n",
125362306a36Sopenharmony_ci			      i, rq->hdr->id, rq->entry_count, hdr_size);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		rqindex++;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		/* Allocate payload buffers */
125862306a36Sopenharmony_ci		rq->payload_buf = efct_hw_rx_buffer_alloc(hw, rqindex,
125962306a36Sopenharmony_ci							  rq->entry_count,
126062306a36Sopenharmony_ci							  payload_size);
126162306a36Sopenharmony_ci		if (!rq->payload_buf) {
126262306a36Sopenharmony_ci			efc_log_err(efct, "rx_buffer_alloc fb_buf failed\n");
126362306a36Sopenharmony_ci			rc = -EIO;
126462306a36Sopenharmony_ci			break;
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci		efc_log_debug(hw->os,
126762306a36Sopenharmony_ci			      "rq[%2d] rq_id %02d default %4d by %4d bytes\n",
126862306a36Sopenharmony_ci			      i, rq->data->id, rq->entry_count, payload_size);
126962306a36Sopenharmony_ci		rqindex++;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return rc ? -EIO : 0;
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ciint
127662306a36Sopenharmony_ciefct_hw_rx_post(struct efct_hw *hw)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	u32 i;
127962306a36Sopenharmony_ci	u32 idx;
128062306a36Sopenharmony_ci	u32 rq_idx;
128162306a36Sopenharmony_ci	int rc = 0;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (!hw->seq_pool) {
128462306a36Sopenharmony_ci		u32 count = 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		for (i = 0; i < hw->hw_rq_count; i++)
128762306a36Sopenharmony_ci			count += hw->hw_rq[i]->entry_count;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		hw->seq_pool = kmalloc_array(count,
129062306a36Sopenharmony_ci				sizeof(struct efc_hw_sequence),	GFP_KERNEL);
129162306a36Sopenharmony_ci		if (!hw->seq_pool)
129262306a36Sopenharmony_ci			return -ENOMEM;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/*
129662306a36Sopenharmony_ci	 * In RQ pair mode, we MUST post the header and payload buffer at the
129762306a36Sopenharmony_ci	 * same time.
129862306a36Sopenharmony_ci	 */
129962306a36Sopenharmony_ci	for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
130062306a36Sopenharmony_ci		struct hw_rq *rq = hw->hw_rq[rq_idx];
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		for (i = 0; i < rq->entry_count - 1; i++) {
130362306a36Sopenharmony_ci			struct efc_hw_sequence *seq;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci			seq = hw->seq_pool + idx;
130662306a36Sopenharmony_ci			idx++;
130762306a36Sopenharmony_ci			seq->header = &rq->hdr_buf[i];
130862306a36Sopenharmony_ci			seq->payload = &rq->payload_buf[i];
130962306a36Sopenharmony_ci			rc = efct_hw_sequence_free(hw, seq);
131062306a36Sopenharmony_ci			if (rc)
131162306a36Sopenharmony_ci				break;
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci		if (rc)
131462306a36Sopenharmony_ci			break;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (rc && hw->seq_pool)
131862306a36Sopenharmony_ci		kfree(hw->seq_pool);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return rc;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_civoid
132462306a36Sopenharmony_ciefct_hw_rx_free(struct efct_hw *hw)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	u32 i;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	/* Free hw_rq buffers */
132962306a36Sopenharmony_ci	for (i = 0; i < hw->hw_rq_count; i++) {
133062306a36Sopenharmony_ci		struct hw_rq *rq = hw->hw_rq[i];
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci		if (rq) {
133362306a36Sopenharmony_ci			efct_hw_rx_buffer_free(hw, rq->hdr_buf,
133462306a36Sopenharmony_ci					       rq->entry_count);
133562306a36Sopenharmony_ci			rq->hdr_buf = NULL;
133662306a36Sopenharmony_ci			efct_hw_rx_buffer_free(hw, rq->payload_buf,
133762306a36Sopenharmony_ci					       rq->entry_count);
133862306a36Sopenharmony_ci			rq->payload_buf = NULL;
133962306a36Sopenharmony_ci		}
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic int
134462306a36Sopenharmony_ciefct_hw_cmd_submit_pending(struct efct_hw *hw)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	int rc = 0;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/* Assumes lock held */
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* Only submit MQE if there's room */
135162306a36Sopenharmony_ci	while (hw->cmd_head_count < (EFCT_HW_MQ_DEPTH - 1) &&
135262306a36Sopenharmony_ci	       !list_empty(&hw->cmd_pending)) {
135362306a36Sopenharmony_ci		struct efct_command_ctx *ctx;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci		ctx = list_first_entry(&hw->cmd_pending,
135662306a36Sopenharmony_ci				       struct efct_command_ctx, list_entry);
135762306a36Sopenharmony_ci		if (!ctx)
135862306a36Sopenharmony_ci			break;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		list_del_init(&ctx->list_entry);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		list_add_tail(&ctx->list_entry, &hw->cmd_head);
136362306a36Sopenharmony_ci		hw->cmd_head_count++;
136462306a36Sopenharmony_ci		if (sli_mq_write(&hw->sli, hw->mq, ctx->buf) < 0) {
136562306a36Sopenharmony_ci			efc_log_debug(hw->os,
136662306a36Sopenharmony_ci				      "sli_queue_write failed: %d\n", rc);
136762306a36Sopenharmony_ci			rc = -EIO;
136862306a36Sopenharmony_ci			break;
136962306a36Sopenharmony_ci		}
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci	return rc;
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ciint
137562306a36Sopenharmony_ciefct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb, void *arg)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	int rc = -EIO;
137862306a36Sopenharmony_ci	unsigned long flags = 0;
137962306a36Sopenharmony_ci	void *bmbx = NULL;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/*
138262306a36Sopenharmony_ci	 * If the chip is in an error state (UE'd) then reject this mailbox
138362306a36Sopenharmony_ci	 * command.
138462306a36Sopenharmony_ci	 */
138562306a36Sopenharmony_ci	if (sli_fw_error_status(&hw->sli) > 0) {
138662306a36Sopenharmony_ci		efc_log_crit(hw->os, "Chip in an error state - reset needed\n");
138762306a36Sopenharmony_ci		efc_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
138862306a36Sopenharmony_ci			     sli_reg_read_status(&hw->sli),
138962306a36Sopenharmony_ci			     sli_reg_read_err1(&hw->sli),
139062306a36Sopenharmony_ci			     sli_reg_read_err2(&hw->sli));
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		return -EIO;
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	/*
139662306a36Sopenharmony_ci	 * Send a mailbox command to the hardware, and either wait for
139762306a36Sopenharmony_ci	 * a completion (EFCT_CMD_POLL) or get an optional asynchronous
139862306a36Sopenharmony_ci	 * completion (EFCT_CMD_NOWAIT).
139962306a36Sopenharmony_ci	 */
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (opts == EFCT_CMD_POLL) {
140262306a36Sopenharmony_ci		mutex_lock(&hw->bmbx_lock);
140362306a36Sopenharmony_ci		bmbx = hw->sli.bmbx.virt;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		if (sli_bmbx_command(&hw->sli) == 0) {
140862306a36Sopenharmony_ci			rc = 0;
140962306a36Sopenharmony_ci			memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
141062306a36Sopenharmony_ci		}
141162306a36Sopenharmony_ci		mutex_unlock(&hw->bmbx_lock);
141262306a36Sopenharmony_ci	} else if (opts == EFCT_CMD_NOWAIT) {
141362306a36Sopenharmony_ci		struct efct_command_ctx	*ctx = NULL;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (hw->state != EFCT_HW_STATE_ACTIVE) {
141662306a36Sopenharmony_ci			efc_log_err(hw->os, "Can't send command, HW state=%d\n",
141762306a36Sopenharmony_ci				    hw->state);
141862306a36Sopenharmony_ci			return -EIO;
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		ctx = mempool_alloc(hw->cmd_ctx_pool, GFP_ATOMIC);
142262306a36Sopenharmony_ci		if (!ctx)
142362306a36Sopenharmony_ci			return -ENOSPC;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci		memset(ctx, 0, sizeof(struct efct_command_ctx));
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		if (cb) {
142862306a36Sopenharmony_ci			ctx->cb = cb;
142962306a36Sopenharmony_ci			ctx->arg = arg;
143062306a36Sopenharmony_ci		}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		memcpy(ctx->buf, cmd, SLI4_BMBX_SIZE);
143362306a36Sopenharmony_ci		ctx->ctx = hw;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci		spin_lock_irqsave(&hw->cmd_lock, flags);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		/* Add to pending list */
143862306a36Sopenharmony_ci		INIT_LIST_HEAD(&ctx->list_entry);
143962306a36Sopenharmony_ci		list_add_tail(&ctx->list_entry, &hw->cmd_pending);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci		/* Submit as much of the pending list as we can */
144262306a36Sopenharmony_ci		rc = efct_hw_cmd_submit_pending(hw);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->cmd_lock, flags);
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	return rc;
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic int
145162306a36Sopenharmony_ciefct_hw_command_process(struct efct_hw *hw, int status, u8 *mqe,
145262306a36Sopenharmony_ci			size_t size)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct efct_command_ctx *ctx = NULL;
145562306a36Sopenharmony_ci	unsigned long flags = 0;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	spin_lock_irqsave(&hw->cmd_lock, flags);
145862306a36Sopenharmony_ci	if (!list_empty(&hw->cmd_head)) {
145962306a36Sopenharmony_ci		ctx = list_first_entry(&hw->cmd_head,
146062306a36Sopenharmony_ci				       struct efct_command_ctx, list_entry);
146162306a36Sopenharmony_ci		list_del_init(&ctx->list_entry);
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci	if (!ctx) {
146462306a36Sopenharmony_ci		efc_log_err(hw->os, "no command context\n");
146562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->cmd_lock, flags);
146662306a36Sopenharmony_ci		return -EIO;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	hw->cmd_head_count--;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/* Post any pending requests */
147262306a36Sopenharmony_ci	efct_hw_cmd_submit_pending(hw);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->cmd_lock, flags);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	if (ctx->cb) {
147762306a36Sopenharmony_ci		memcpy(ctx->buf, mqe, size);
147862306a36Sopenharmony_ci		ctx->cb(hw, status, ctx->buf, ctx->arg);
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	mempool_free(ctx, hw->cmd_ctx_pool);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	return 0;
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic int
148762306a36Sopenharmony_ciefct_hw_mq_process(struct efct_hw *hw,
148862306a36Sopenharmony_ci		   int status, struct sli4_queue *mq)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	u8 mqe[SLI4_BMBX_SIZE];
149162306a36Sopenharmony_ci	int rc;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	rc = sli_mq_read(&hw->sli, mq, mqe);
149462306a36Sopenharmony_ci	if (!rc)
149562306a36Sopenharmony_ci		rc = efct_hw_command_process(hw, status, mqe, mq->size);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return rc;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic int
150162306a36Sopenharmony_ciefct_hw_command_cancel(struct efct_hw *hw)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	unsigned long flags = 0;
150462306a36Sopenharmony_ci	int rc = 0;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	spin_lock_irqsave(&hw->cmd_lock, flags);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/*
150962306a36Sopenharmony_ci	 * Manually clean up remaining commands. Note: since this calls
151062306a36Sopenharmony_ci	 * efct_hw_command_process(), we'll also process the cmd_pending
151162306a36Sopenharmony_ci	 * list, so no need to manually clean that out.
151262306a36Sopenharmony_ci	 */
151362306a36Sopenharmony_ci	while (!list_empty(&hw->cmd_head)) {
151462306a36Sopenharmony_ci		u8		mqe[SLI4_BMBX_SIZE] = { 0 };
151562306a36Sopenharmony_ci		struct efct_command_ctx *ctx;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci		ctx = list_first_entry(&hw->cmd_head,
151862306a36Sopenharmony_ci				       struct efct_command_ctx, list_entry);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci		efc_log_debug(hw->os, "hung command %08x\n",
152162306a36Sopenharmony_ci			      !ctx ? U32_MAX : *((u32 *)ctx->buf));
152262306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->cmd_lock, flags);
152362306a36Sopenharmony_ci		rc = efct_hw_command_process(hw, -1, mqe, SLI4_BMBX_SIZE);
152462306a36Sopenharmony_ci		spin_lock_irqsave(&hw->cmd_lock, flags);
152562306a36Sopenharmony_ci	}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->cmd_lock, flags);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	return rc;
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic void
153362306a36Sopenharmony_ciefct_mbox_rsp_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct efct_mbox_rqst_ctx *ctx = arg;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (ctx) {
153862306a36Sopenharmony_ci		if (ctx->callback)
153962306a36Sopenharmony_ci			(*ctx->callback)(hw->os->efcport, status, mqe,
154062306a36Sopenharmony_ci					 ctx->arg);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci		mempool_free(ctx, hw->mbox_rqst_pool);
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ciint
154762306a36Sopenharmony_ciefct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	struct efct_mbox_rqst_ctx *ctx;
155062306a36Sopenharmony_ci	struct efct *efct = base;
155162306a36Sopenharmony_ci	struct efct_hw *hw = &efct->hw;
155262306a36Sopenharmony_ci	int rc;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	/*
155562306a36Sopenharmony_ci	 * Allocate a callback context (which includes the mbox cmd buffer),
155662306a36Sopenharmony_ci	 * we need this to be persistent as the mbox cmd submission may be
155762306a36Sopenharmony_ci	 * queued and executed later execution.
155862306a36Sopenharmony_ci	 */
155962306a36Sopenharmony_ci	ctx = mempool_alloc(hw->mbox_rqst_pool, GFP_ATOMIC);
156062306a36Sopenharmony_ci	if (!ctx)
156162306a36Sopenharmony_ci		return -EIO;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	ctx->callback = cb;
156462306a36Sopenharmony_ci	ctx->arg = arg;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	rc = efct_hw_command(hw, cmd, EFCT_CMD_NOWAIT, efct_mbox_rsp_cb, ctx);
156762306a36Sopenharmony_ci	if (rc) {
156862306a36Sopenharmony_ci		efc_log_err(efct, "issue mbox rqst failure rc:%d\n", rc);
156962306a36Sopenharmony_ci		mempool_free(ctx, hw->mbox_rqst_pool);
157062306a36Sopenharmony_ci		return -EIO;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	return 0;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_cistatic inline struct efct_hw_io *
157762306a36Sopenharmony_ci_efct_hw_io_alloc(struct efct_hw *hw)
157862306a36Sopenharmony_ci{
157962306a36Sopenharmony_ci	struct efct_hw_io *io = NULL;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (!list_empty(&hw->io_free)) {
158262306a36Sopenharmony_ci		io = list_first_entry(&hw->io_free, struct efct_hw_io,
158362306a36Sopenharmony_ci				      list_entry);
158462306a36Sopenharmony_ci		list_del(&io->list_entry);
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci	if (io) {
158762306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->list_entry);
158862306a36Sopenharmony_ci		list_add_tail(&io->list_entry, &hw->io_inuse);
158962306a36Sopenharmony_ci		io->state = EFCT_HW_IO_STATE_INUSE;
159062306a36Sopenharmony_ci		io->abort_reqtag = U32_MAX;
159162306a36Sopenharmony_ci		io->wq = hw->wq_cpu_array[raw_smp_processor_id()];
159262306a36Sopenharmony_ci		if (!io->wq) {
159362306a36Sopenharmony_ci			efc_log_err(hw->os, "WQ not assigned for cpu:%d\n",
159462306a36Sopenharmony_ci				    raw_smp_processor_id());
159562306a36Sopenharmony_ci			io->wq = hw->hw_wq[0];
159662306a36Sopenharmony_ci		}
159762306a36Sopenharmony_ci		kref_init(&io->ref);
159862306a36Sopenharmony_ci		io->release = efct_hw_io_free_internal;
159962306a36Sopenharmony_ci	} else {
160062306a36Sopenharmony_ci		atomic_add(1, &hw->io_alloc_failed_count);
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return io;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cistruct efct_hw_io *
160762306a36Sopenharmony_ciefct_hw_io_alloc(struct efct_hw *hw)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct efct_hw_io *io = NULL;
161062306a36Sopenharmony_ci	unsigned long flags = 0;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	spin_lock_irqsave(&hw->io_lock, flags);
161362306a36Sopenharmony_ci	io = _efct_hw_io_alloc(hw);
161462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->io_lock, flags);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	return io;
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic void
162062306a36Sopenharmony_ciefct_hw_io_free_move_correct_list(struct efct_hw *hw,
162162306a36Sopenharmony_ci				  struct efct_hw_io *io)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	/*
162462306a36Sopenharmony_ci	 * When an IO is freed, depending on the exchange busy flag,
162562306a36Sopenharmony_ci	 * move it to the correct list.
162662306a36Sopenharmony_ci	 */
162762306a36Sopenharmony_ci	if (io->xbusy) {
162862306a36Sopenharmony_ci		/*
162962306a36Sopenharmony_ci		 * add to wait_free list and wait for XRI_ABORTED CQEs to clean
163062306a36Sopenharmony_ci		 * up
163162306a36Sopenharmony_ci		 */
163262306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->list_entry);
163362306a36Sopenharmony_ci		list_add_tail(&io->list_entry, &hw->io_wait_free);
163462306a36Sopenharmony_ci		io->state = EFCT_HW_IO_STATE_WAIT_FREE;
163562306a36Sopenharmony_ci	} else {
163662306a36Sopenharmony_ci		/* IO not busy, add to free list */
163762306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->list_entry);
163862306a36Sopenharmony_ci		list_add_tail(&io->list_entry, &hw->io_free);
163962306a36Sopenharmony_ci		io->state = EFCT_HW_IO_STATE_FREE;
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic inline void
164462306a36Sopenharmony_ciefct_hw_io_free_common(struct efct_hw *hw, struct efct_hw_io *io)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	/* initialize IO fields */
164762306a36Sopenharmony_ci	efct_hw_init_free_io(io);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	/* Restore default SGL */
165062306a36Sopenharmony_ci	efct_hw_io_restore_sgl(hw, io);
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_civoid
165462306a36Sopenharmony_ciefct_hw_io_free_internal(struct kref *arg)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	unsigned long flags = 0;
165762306a36Sopenharmony_ci	struct efct_hw_io *io =	container_of(arg, struct efct_hw_io, ref);
165862306a36Sopenharmony_ci	struct efct_hw *hw = io->hw;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	/* perform common cleanup */
166162306a36Sopenharmony_ci	efct_hw_io_free_common(hw, io);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	spin_lock_irqsave(&hw->io_lock, flags);
166462306a36Sopenharmony_ci	/* remove from in-use list */
166562306a36Sopenharmony_ci	if (!list_empty(&io->list_entry) && !list_empty(&hw->io_inuse)) {
166662306a36Sopenharmony_ci		list_del_init(&io->list_entry);
166762306a36Sopenharmony_ci		efct_hw_io_free_move_correct_list(hw, io);
166862306a36Sopenharmony_ci	}
166962306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->io_lock, flags);
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ciint
167362306a36Sopenharmony_ciefct_hw_io_free(struct efct_hw *hw, struct efct_hw_io *io)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	return kref_put(&io->ref, io->release);
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistruct efct_hw_io *
167962306a36Sopenharmony_ciefct_hw_io_lookup(struct efct_hw *hw, u32 xri)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	u32 ioindex;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	ioindex = xri - hw->sli.ext[SLI4_RSRC_XRI].base[0];
168462306a36Sopenharmony_ci	return hw->io[ioindex];
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ciint
168862306a36Sopenharmony_ciefct_hw_io_init_sges(struct efct_hw *hw, struct efct_hw_io *io,
168962306a36Sopenharmony_ci		     enum efct_hw_io_type type)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	struct sli4_sge	*data = NULL;
169262306a36Sopenharmony_ci	u32 i = 0;
169362306a36Sopenharmony_ci	u32 skips = 0;
169462306a36Sopenharmony_ci	u32 sge_flags = 0;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	if (!io) {
169762306a36Sopenharmony_ci		efc_log_err(hw->os, "bad parameter hw=%p io=%p\n", hw, io);
169862306a36Sopenharmony_ci		return -EIO;
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	/* Clear / reset the scatter-gather list */
170262306a36Sopenharmony_ci	io->sgl = &io->def_sgl;
170362306a36Sopenharmony_ci	io->sgl_count = io->def_sgl_count;
170462306a36Sopenharmony_ci	io->first_data_sge = 0;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	memset(io->sgl->virt, 0, 2 * sizeof(struct sli4_sge));
170762306a36Sopenharmony_ci	io->n_sge = 0;
170862306a36Sopenharmony_ci	io->sge_offset = 0;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	io->type = type;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	data = io->sgl->virt;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	/*
171562306a36Sopenharmony_ci	 * Some IO types have underlying hardware requirements on the order
171662306a36Sopenharmony_ci	 * of SGEs. Process all special entries here.
171762306a36Sopenharmony_ci	 */
171862306a36Sopenharmony_ci	switch (type) {
171962306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_WRITE:
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci		/* populate host resident XFER_RDY buffer */
172262306a36Sopenharmony_ci		sge_flags = le32_to_cpu(data->dw2_flags);
172362306a36Sopenharmony_ci		sge_flags &= (~SLI4_SGE_TYPE_MASK);
172462306a36Sopenharmony_ci		sge_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
172562306a36Sopenharmony_ci		data->buffer_address_high =
172662306a36Sopenharmony_ci			cpu_to_le32(upper_32_bits(io->xfer_rdy.phys));
172762306a36Sopenharmony_ci		data->buffer_address_low  =
172862306a36Sopenharmony_ci			cpu_to_le32(lower_32_bits(io->xfer_rdy.phys));
172962306a36Sopenharmony_ci		data->buffer_length = cpu_to_le32(io->xfer_rdy.size);
173062306a36Sopenharmony_ci		data->dw2_flags = cpu_to_le32(sge_flags);
173162306a36Sopenharmony_ci		data++;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		skips = EFCT_TARGET_WRITE_SKIPS;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci		io->n_sge = 1;
173662306a36Sopenharmony_ci		break;
173762306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_READ:
173862306a36Sopenharmony_ci		/*
173962306a36Sopenharmony_ci		 * For FCP_TSEND64, the first 2 entries are SKIP SGE's
174062306a36Sopenharmony_ci		 */
174162306a36Sopenharmony_ci		skips = EFCT_TARGET_READ_SKIPS;
174262306a36Sopenharmony_ci		break;
174362306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_RSP:
174462306a36Sopenharmony_ci		/*
174562306a36Sopenharmony_ci		 * No skips, etc. for FCP_TRSP64
174662306a36Sopenharmony_ci		 */
174762306a36Sopenharmony_ci		break;
174862306a36Sopenharmony_ci	default:
174962306a36Sopenharmony_ci		efc_log_err(hw->os, "unsupported IO type %#x\n", type);
175062306a36Sopenharmony_ci		return -EIO;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	/*
175462306a36Sopenharmony_ci	 * Write skip entries
175562306a36Sopenharmony_ci	 */
175662306a36Sopenharmony_ci	for (i = 0; i < skips; i++) {
175762306a36Sopenharmony_ci		sge_flags = le32_to_cpu(data->dw2_flags);
175862306a36Sopenharmony_ci		sge_flags &= (~SLI4_SGE_TYPE_MASK);
175962306a36Sopenharmony_ci		sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT);
176062306a36Sopenharmony_ci		data->dw2_flags = cpu_to_le32(sge_flags);
176162306a36Sopenharmony_ci		data++;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	io->n_sge += skips;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	/*
176762306a36Sopenharmony_ci	 * Set last
176862306a36Sopenharmony_ci	 */
176962306a36Sopenharmony_ci	sge_flags = le32_to_cpu(data->dw2_flags);
177062306a36Sopenharmony_ci	sge_flags |= SLI4_SGE_LAST;
177162306a36Sopenharmony_ci	data->dw2_flags = cpu_to_le32(sge_flags);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	return 0;
177462306a36Sopenharmony_ci}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ciint
177762306a36Sopenharmony_ciefct_hw_io_add_sge(struct efct_hw *hw, struct efct_hw_io *io,
177862306a36Sopenharmony_ci		   uintptr_t addr, u32 length)
177962306a36Sopenharmony_ci{
178062306a36Sopenharmony_ci	struct sli4_sge	*data = NULL;
178162306a36Sopenharmony_ci	u32 sge_flags = 0;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	if (!io || !addr || !length) {
178462306a36Sopenharmony_ci		efc_log_err(hw->os,
178562306a36Sopenharmony_ci			    "bad parameter hw=%p io=%p addr=%lx length=%u\n",
178662306a36Sopenharmony_ci			    hw, io, addr, length);
178762306a36Sopenharmony_ci		return -EIO;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (length > hw->sli.sge_supported_length) {
179162306a36Sopenharmony_ci		efc_log_err(hw->os,
179262306a36Sopenharmony_ci			    "length of SGE %d bigger than allowed %d\n",
179362306a36Sopenharmony_ci			    length, hw->sli.sge_supported_length);
179462306a36Sopenharmony_ci		return -EIO;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	data = io->sgl->virt;
179862306a36Sopenharmony_ci	data += io->n_sge;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	sge_flags = le32_to_cpu(data->dw2_flags);
180162306a36Sopenharmony_ci	sge_flags &= ~SLI4_SGE_TYPE_MASK;
180262306a36Sopenharmony_ci	sge_flags |= SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT;
180362306a36Sopenharmony_ci	sge_flags &= ~SLI4_SGE_DATA_OFFSET_MASK;
180462306a36Sopenharmony_ci	sge_flags |= SLI4_SGE_DATA_OFFSET_MASK & io->sge_offset;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	data->buffer_address_high = cpu_to_le32(upper_32_bits(addr));
180762306a36Sopenharmony_ci	data->buffer_address_low  = cpu_to_le32(lower_32_bits(addr));
180862306a36Sopenharmony_ci	data->buffer_length = cpu_to_le32(length);
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	/*
181162306a36Sopenharmony_ci	 * Always assume this is the last entry and mark as such.
181262306a36Sopenharmony_ci	 * If this is not the first entry unset the "last SGE"
181362306a36Sopenharmony_ci	 * indication for the previous entry
181462306a36Sopenharmony_ci	 */
181562306a36Sopenharmony_ci	sge_flags |= SLI4_SGE_LAST;
181662306a36Sopenharmony_ci	data->dw2_flags = cpu_to_le32(sge_flags);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (io->n_sge) {
181962306a36Sopenharmony_ci		sge_flags = le32_to_cpu(data[-1].dw2_flags);
182062306a36Sopenharmony_ci		sge_flags &= ~SLI4_SGE_LAST;
182162306a36Sopenharmony_ci		data[-1].dw2_flags = cpu_to_le32(sge_flags);
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* Set first_data_bde if not previously set */
182562306a36Sopenharmony_ci	if (io->first_data_sge == 0)
182662306a36Sopenharmony_ci		io->first_data_sge = io->n_sge;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	io->sge_offset += length;
182962306a36Sopenharmony_ci	io->n_sge++;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	return 0;
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_civoid
183562306a36Sopenharmony_ciefct_hw_io_abort_all(struct efct_hw *hw)
183662306a36Sopenharmony_ci{
183762306a36Sopenharmony_ci	struct efct_hw_io *io_to_abort	= NULL;
183862306a36Sopenharmony_ci	struct efct_hw_io *next_io = NULL;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	list_for_each_entry_safe(io_to_abort, next_io,
184162306a36Sopenharmony_ci				 &hw->io_inuse, list_entry) {
184262306a36Sopenharmony_ci		efct_hw_io_abort(hw, io_to_abort, true, NULL, NULL);
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic void
184762306a36Sopenharmony_ciefct_hw_wq_process_abort(void *arg, u8 *cqe, int status)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct efct_hw_io *io = arg;
185062306a36Sopenharmony_ci	struct efct_hw *hw = io->hw;
185162306a36Sopenharmony_ci	u32 ext = 0;
185262306a36Sopenharmony_ci	u32 len = 0;
185362306a36Sopenharmony_ci	struct hw_wq_callback *wqcb;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	/*
185662306a36Sopenharmony_ci	 * For IOs that were aborted internally, we may need to issue the
185762306a36Sopenharmony_ci	 * callback here depending on whether a XRI_ABORTED CQE is expected ot
185862306a36Sopenharmony_ci	 * not. If the status is Local Reject/No XRI, then
185962306a36Sopenharmony_ci	 * issue the callback now.
186062306a36Sopenharmony_ci	 */
186162306a36Sopenharmony_ci	ext = sli_fc_ext_status(&hw->sli, cqe);
186262306a36Sopenharmony_ci	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
186362306a36Sopenharmony_ci	    ext == SLI4_FC_LOCAL_REJECT_NO_XRI && io->done) {
186462306a36Sopenharmony_ci		efct_hw_done_t done = io->done;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci		io->done = NULL;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci		/*
186962306a36Sopenharmony_ci		 * Use latched status as this is always saved for an internal
187062306a36Sopenharmony_ci		 * abort Note: We won't have both a done and abort_done
187162306a36Sopenharmony_ci		 * function, so don't worry about
187262306a36Sopenharmony_ci		 *       clobbering the len, status and ext fields.
187362306a36Sopenharmony_ci		 */
187462306a36Sopenharmony_ci		status = io->saved_status;
187562306a36Sopenharmony_ci		len = io->saved_len;
187662306a36Sopenharmony_ci		ext = io->saved_ext;
187762306a36Sopenharmony_ci		io->status_saved = false;
187862306a36Sopenharmony_ci		done(io, len, status, ext, io->arg);
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (io->abort_done) {
188262306a36Sopenharmony_ci		efct_hw_done_t done = io->abort_done;
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci		io->abort_done = NULL;
188562306a36Sopenharmony_ci		done(io, len, status, ext, io->abort_arg);
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	/* clear abort bit to indicate abort is complete */
188962306a36Sopenharmony_ci	io->abort_in_progress = false;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	/* Free the WQ callback */
189262306a36Sopenharmony_ci	if (io->abort_reqtag == U32_MAX) {
189362306a36Sopenharmony_ci		efc_log_err(hw->os, "HW IO already freed\n");
189462306a36Sopenharmony_ci		return;
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	wqcb = efct_hw_reqtag_get_instance(hw, io->abort_reqtag);
189862306a36Sopenharmony_ci	efct_hw_reqtag_free(hw, wqcb);
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	/*
190162306a36Sopenharmony_ci	 * Call efct_hw_io_free() because this releases the WQ reservation as
190262306a36Sopenharmony_ci	 * well as doing the refcount put. Don't duplicate the code here.
190362306a36Sopenharmony_ci	 */
190462306a36Sopenharmony_ci	(void)efct_hw_io_free(hw, io);
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic void
190862306a36Sopenharmony_ciefct_hw_fill_abort_wqe(struct efct_hw *hw, struct efct_hw_wqe *wqe)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct sli4_abort_wqe *abort = (void *)wqe->wqebuf;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	memset(abort, 0, hw->sli.wqe_size);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG;
191562306a36Sopenharmony_ci	abort->ia_ir_byte |= wqe->send_abts ? 0 : 1;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	/* Suppress ABTS retries */
191862306a36Sopenharmony_ci	abort->ia_ir_byte |= SLI4_ABRT_WQE_IR;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	abort->t_tag  = cpu_to_le32(wqe->id);
192162306a36Sopenharmony_ci	abort->command = SLI4_WQE_ABORT;
192262306a36Sopenharmony_ci	abort->request_tag = cpu_to_le16(wqe->abort_reqtag);
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	abort->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
192762306a36Sopenharmony_ci}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ciint
193062306a36Sopenharmony_ciefct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
193162306a36Sopenharmony_ci		 bool send_abts, void *cb, void *arg)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct hw_wq_callback *wqcb;
193462306a36Sopenharmony_ci	unsigned long flags = 0;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (!io_to_abort) {
193762306a36Sopenharmony_ci		efc_log_err(hw->os, "bad parameter hw=%p io=%p\n",
193862306a36Sopenharmony_ci			    hw, io_to_abort);
193962306a36Sopenharmony_ci		return -EIO;
194062306a36Sopenharmony_ci	}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_ACTIVE) {
194362306a36Sopenharmony_ci		efc_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
194462306a36Sopenharmony_ci			    hw->state);
194562306a36Sopenharmony_ci		return -EIO;
194662306a36Sopenharmony_ci	}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	/* take a reference on IO being aborted */
194962306a36Sopenharmony_ci	if (kref_get_unless_zero(&io_to_abort->ref) == 0) {
195062306a36Sopenharmony_ci		/* command no longer active */
195162306a36Sopenharmony_ci		efc_log_debug(hw->os,
195262306a36Sopenharmony_ci			      "io not active xri=0x%x tag=0x%x\n",
195362306a36Sopenharmony_ci			      io_to_abort->indicator, io_to_abort->reqtag);
195462306a36Sopenharmony_ci		return -ENOENT;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	/* Must have a valid WQ reference */
195862306a36Sopenharmony_ci	if (!io_to_abort->wq) {
195962306a36Sopenharmony_ci		efc_log_debug(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
196062306a36Sopenharmony_ci			      io_to_abort->indicator);
196162306a36Sopenharmony_ci		/* efct_ref_get(): same function */
196262306a36Sopenharmony_ci		kref_put(&io_to_abort->ref, io_to_abort->release);
196362306a36Sopenharmony_ci		return -ENOENT;
196462306a36Sopenharmony_ci	}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	/*
196762306a36Sopenharmony_ci	 * Validation checks complete; now check to see if already being
196862306a36Sopenharmony_ci	 * aborted, if not set the flag.
196962306a36Sopenharmony_ci	 */
197062306a36Sopenharmony_ci	if (cmpxchg(&io_to_abort->abort_in_progress, false, true)) {
197162306a36Sopenharmony_ci		/* efct_ref_get(): same function */
197262306a36Sopenharmony_ci		kref_put(&io_to_abort->ref, io_to_abort->release);
197362306a36Sopenharmony_ci		efc_log_debug(hw->os,
197462306a36Sopenharmony_ci			      "io already being aborted xri=0x%x tag=0x%x\n",
197562306a36Sopenharmony_ci			      io_to_abort->indicator, io_to_abort->reqtag);
197662306a36Sopenharmony_ci		return -EINPROGRESS;
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	/*
198062306a36Sopenharmony_ci	 * If we got here, the possibilities are:
198162306a36Sopenharmony_ci	 * - host owned xri
198262306a36Sopenharmony_ci	 *	- io_to_abort->wq_index != U32_MAX
198362306a36Sopenharmony_ci	 *		- submit ABORT_WQE to same WQ
198462306a36Sopenharmony_ci	 * - port owned xri:
198562306a36Sopenharmony_ci	 *	- rxri: io_to_abort->wq_index == U32_MAX
198662306a36Sopenharmony_ci	 *		- submit ABORT_WQE to any WQ
198762306a36Sopenharmony_ci	 *	- non-rxri
198862306a36Sopenharmony_ci	 *		- io_to_abort->index != U32_MAX
198962306a36Sopenharmony_ci	 *			- submit ABORT_WQE to same WQ
199062306a36Sopenharmony_ci	 *		- io_to_abort->index == U32_MAX
199162306a36Sopenharmony_ci	 *			- submit ABORT_WQE to any WQ
199262306a36Sopenharmony_ci	 */
199362306a36Sopenharmony_ci	io_to_abort->abort_done = cb;
199462306a36Sopenharmony_ci	io_to_abort->abort_arg  = arg;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	/* Allocate a request tag for the abort portion of this IO */
199762306a36Sopenharmony_ci	wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort);
199862306a36Sopenharmony_ci	if (!wqcb) {
199962306a36Sopenharmony_ci		efc_log_err(hw->os, "can't allocate request tag\n");
200062306a36Sopenharmony_ci		return -ENOSPC;
200162306a36Sopenharmony_ci	}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	io_to_abort->abort_reqtag = wqcb->instance_index;
200462306a36Sopenharmony_ci	io_to_abort->wqe.send_abts = send_abts;
200562306a36Sopenharmony_ci	io_to_abort->wqe.id = io_to_abort->indicator;
200662306a36Sopenharmony_ci	io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	/*
200962306a36Sopenharmony_ci	 * If the wqe is on the pending list, then set this wqe to be
201062306a36Sopenharmony_ci	 * aborted when the IO's wqe is removed from the list.
201162306a36Sopenharmony_ci	 */
201262306a36Sopenharmony_ci	if (io_to_abort->wq) {
201362306a36Sopenharmony_ci		spin_lock_irqsave(&io_to_abort->wq->queue->lock, flags);
201462306a36Sopenharmony_ci		if (io_to_abort->wqe.list_entry.next) {
201562306a36Sopenharmony_ci			io_to_abort->wqe.abort_wqe_submit_needed = true;
201662306a36Sopenharmony_ci			spin_unlock_irqrestore(&io_to_abort->wq->queue->lock,
201762306a36Sopenharmony_ci					       flags);
201862306a36Sopenharmony_ci			return 0;
201962306a36Sopenharmony_ci		}
202062306a36Sopenharmony_ci		spin_unlock_irqrestore(&io_to_abort->wq->queue->lock, flags);
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	efct_hw_fill_abort_wqe(hw, &io_to_abort->wqe);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	/* ABORT_WQE does not actually utilize an XRI on the Port,
202662306a36Sopenharmony_ci	 * therefore, keep xbusy as-is to track the exchange's state,
202762306a36Sopenharmony_ci	 * not the ABORT_WQE's state
202862306a36Sopenharmony_ci	 */
202962306a36Sopenharmony_ci	if (efct_hw_wq_write(io_to_abort->wq, &io_to_abort->wqe)) {
203062306a36Sopenharmony_ci		io_to_abort->abort_in_progress = false;
203162306a36Sopenharmony_ci		/* efct_ref_get(): same function */
203262306a36Sopenharmony_ci		kref_put(&io_to_abort->ref, io_to_abort->release);
203362306a36Sopenharmony_ci		return -EIO;
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	return 0;
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_civoid
204062306a36Sopenharmony_ciefct_hw_reqtag_pool_free(struct efct_hw *hw)
204162306a36Sopenharmony_ci{
204262306a36Sopenharmony_ci	u32 i;
204362306a36Sopenharmony_ci	struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
204462306a36Sopenharmony_ci	struct hw_wq_callback *wqcb = NULL;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	if (reqtag_pool) {
204762306a36Sopenharmony_ci		for (i = 0; i < U16_MAX; i++) {
204862306a36Sopenharmony_ci			wqcb = reqtag_pool->tags[i];
204962306a36Sopenharmony_ci			if (!wqcb)
205062306a36Sopenharmony_ci				continue;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci			kfree(wqcb);
205362306a36Sopenharmony_ci		}
205462306a36Sopenharmony_ci		kfree(reqtag_pool);
205562306a36Sopenharmony_ci		hw->wq_reqtag_pool = NULL;
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_cistruct reqtag_pool *
206062306a36Sopenharmony_ciefct_hw_reqtag_pool_alloc(struct efct_hw *hw)
206162306a36Sopenharmony_ci{
206262306a36Sopenharmony_ci	u32 i = 0;
206362306a36Sopenharmony_ci	struct reqtag_pool *reqtag_pool;
206462306a36Sopenharmony_ci	struct hw_wq_callback *wqcb;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	reqtag_pool = kzalloc(sizeof(*reqtag_pool), GFP_KERNEL);
206762306a36Sopenharmony_ci	if (!reqtag_pool)
206862306a36Sopenharmony_ci		return NULL;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	INIT_LIST_HEAD(&reqtag_pool->freelist);
207162306a36Sopenharmony_ci	/* initialize reqtag pool lock */
207262306a36Sopenharmony_ci	spin_lock_init(&reqtag_pool->lock);
207362306a36Sopenharmony_ci	for (i = 0; i < U16_MAX; i++) {
207462306a36Sopenharmony_ci		wqcb = kmalloc(sizeof(*wqcb), GFP_KERNEL);
207562306a36Sopenharmony_ci		if (!wqcb)
207662306a36Sopenharmony_ci			break;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci		reqtag_pool->tags[i] = wqcb;
207962306a36Sopenharmony_ci		wqcb->instance_index = i;
208062306a36Sopenharmony_ci		wqcb->callback = NULL;
208162306a36Sopenharmony_ci		wqcb->arg = NULL;
208262306a36Sopenharmony_ci		INIT_LIST_HEAD(&wqcb->list_entry);
208362306a36Sopenharmony_ci		list_add_tail(&wqcb->list_entry, &reqtag_pool->freelist);
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return reqtag_pool;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistruct hw_wq_callback *
209062306a36Sopenharmony_ciefct_hw_reqtag_alloc(struct efct_hw *hw,
209162306a36Sopenharmony_ci		     void (*callback)(void *arg, u8 *cqe, int status),
209262306a36Sopenharmony_ci		     void *arg)
209362306a36Sopenharmony_ci{
209462306a36Sopenharmony_ci	struct hw_wq_callback *wqcb = NULL;
209562306a36Sopenharmony_ci	struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
209662306a36Sopenharmony_ci	unsigned long flags = 0;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (!callback)
209962306a36Sopenharmony_ci		return wqcb;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	spin_lock_irqsave(&reqtag_pool->lock, flags);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	if (!list_empty(&reqtag_pool->freelist)) {
210462306a36Sopenharmony_ci		wqcb = list_first_entry(&reqtag_pool->freelist,
210562306a36Sopenharmony_ci					struct hw_wq_callback, list_entry);
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	if (wqcb) {
210962306a36Sopenharmony_ci		list_del_init(&wqcb->list_entry);
211062306a36Sopenharmony_ci		spin_unlock_irqrestore(&reqtag_pool->lock, flags);
211162306a36Sopenharmony_ci		wqcb->callback = callback;
211262306a36Sopenharmony_ci		wqcb->arg = arg;
211362306a36Sopenharmony_ci	} else {
211462306a36Sopenharmony_ci		spin_unlock_irqrestore(&reqtag_pool->lock, flags);
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	return wqcb;
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_civoid
212162306a36Sopenharmony_ciefct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	unsigned long flags = 0;
212462306a36Sopenharmony_ci	struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	if (!wqcb->callback)
212762306a36Sopenharmony_ci		efc_log_err(hw->os, "WQCB is already freed\n");
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	spin_lock_irqsave(&reqtag_pool->lock, flags);
213062306a36Sopenharmony_ci	wqcb->callback = NULL;
213162306a36Sopenharmony_ci	wqcb->arg = NULL;
213262306a36Sopenharmony_ci	INIT_LIST_HEAD(&wqcb->list_entry);
213362306a36Sopenharmony_ci	list_add(&wqcb->list_entry, &hw->wq_reqtag_pool->freelist);
213462306a36Sopenharmony_ci	spin_unlock_irqrestore(&reqtag_pool->lock, flags);
213562306a36Sopenharmony_ci}
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_cistruct hw_wq_callback *
213862306a36Sopenharmony_ciefct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci	struct hw_wq_callback *wqcb;
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	wqcb = hw->wq_reqtag_pool->tags[instance_index];
214362306a36Sopenharmony_ci	if (!wqcb)
214462306a36Sopenharmony_ci		efc_log_err(hw->os, "wqcb for instance %d is null\n",
214562306a36Sopenharmony_ci			    instance_index);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	return wqcb;
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ciint
215162306a36Sopenharmony_ciefct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id)
215262306a36Sopenharmony_ci{
215362306a36Sopenharmony_ci	int index = -1;
215462306a36Sopenharmony_ci	int i = id & (EFCT_HW_Q_HASH_SIZE - 1);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	/*
215762306a36Sopenharmony_ci	 * Since the hash is always bigger than the maximum number of Qs, then
215862306a36Sopenharmony_ci	 * we never have to worry about an infinite loop. We will always find
215962306a36Sopenharmony_ci	 * an unused entry.
216062306a36Sopenharmony_ci	 */
216162306a36Sopenharmony_ci	do {
216262306a36Sopenharmony_ci		if (hash[i].in_use && hash[i].id == id)
216362306a36Sopenharmony_ci			index = hash[i].index;
216462306a36Sopenharmony_ci		else
216562306a36Sopenharmony_ci			i = (i + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
216662306a36Sopenharmony_ci	} while (index == -1 && hash[i].in_use);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	return index;
216962306a36Sopenharmony_ci}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ciint
217262306a36Sopenharmony_ciefct_hw_process(struct efct_hw *hw, u32 vector,
217362306a36Sopenharmony_ci		u32 max_isr_time_msec)
217462306a36Sopenharmony_ci{
217562306a36Sopenharmony_ci	struct hw_eq *eq;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	/*
217862306a36Sopenharmony_ci	 * The caller should disable interrupts if they wish to prevent us
217962306a36Sopenharmony_ci	 * from processing during a shutdown. The following states are defined:
218062306a36Sopenharmony_ci	 *   EFCT_HW_STATE_UNINITIALIZED - No queues allocated
218162306a36Sopenharmony_ci	 *   EFCT_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
218262306a36Sopenharmony_ci	 *                                    queues are cleared.
218362306a36Sopenharmony_ci	 *   EFCT_HW_STATE_ACTIVE - Chip and queues are operational
218462306a36Sopenharmony_ci	 *   EFCT_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
218562306a36Sopenharmony_ci	 *   EFCT_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
218662306a36Sopenharmony_ci	 *                                        completions.
218762306a36Sopenharmony_ci	 */
218862306a36Sopenharmony_ci	if (hw->state == EFCT_HW_STATE_UNINITIALIZED)
218962306a36Sopenharmony_ci		return 0;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	/* Get pointer to struct hw_eq */
219262306a36Sopenharmony_ci	eq = hw->hw_eq[vector];
219362306a36Sopenharmony_ci	if (!eq)
219462306a36Sopenharmony_ci		return 0;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	eq->use_count++;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	return efct_hw_eq_process(hw, eq, max_isr_time_msec);
219962306a36Sopenharmony_ci}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ciint
220262306a36Sopenharmony_ciefct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq,
220362306a36Sopenharmony_ci		   u32 max_isr_time_msec)
220462306a36Sopenharmony_ci{
220562306a36Sopenharmony_ci	u8 eqe[sizeof(struct sli4_eqe)] = { 0 };
220662306a36Sopenharmony_ci	u32 tcheck_count;
220762306a36Sopenharmony_ci	u64 tstart;
220862306a36Sopenharmony_ci	u64 telapsed;
220962306a36Sopenharmony_ci	bool done = false;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
221262306a36Sopenharmony_ci	tstart = jiffies_to_msecs(jiffies);
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	while (!done && !sli_eq_read(&hw->sli, eq->queue, eqe)) {
221562306a36Sopenharmony_ci		u16 cq_id = 0;
221662306a36Sopenharmony_ci		int rc;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci		rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
221962306a36Sopenharmony_ci		if (unlikely(rc)) {
222062306a36Sopenharmony_ci			if (rc == SLI4_EQE_STATUS_EQ_FULL) {
222162306a36Sopenharmony_ci				u32 i;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci				/*
222462306a36Sopenharmony_ci				 * Received a sentinel EQE indicating the
222562306a36Sopenharmony_ci				 * EQ is full. Process all CQs
222662306a36Sopenharmony_ci				 */
222762306a36Sopenharmony_ci				for (i = 0; i < hw->cq_count; i++)
222862306a36Sopenharmony_ci					efct_hw_cq_process(hw, hw->hw_cq[i]);
222962306a36Sopenharmony_ci				continue;
223062306a36Sopenharmony_ci			} else {
223162306a36Sopenharmony_ci				return rc;
223262306a36Sopenharmony_ci			}
223362306a36Sopenharmony_ci		} else {
223462306a36Sopenharmony_ci			int index;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci			index  = efct_hw_queue_hash_find(hw->cq_hash, cq_id);
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci			if (likely(index >= 0))
223962306a36Sopenharmony_ci				efct_hw_cq_process(hw, hw->hw_cq[index]);
224062306a36Sopenharmony_ci			else
224162306a36Sopenharmony_ci				efc_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
224262306a36Sopenharmony_ci		}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci		if (eq->queue->n_posted > eq->queue->posted_limit)
224562306a36Sopenharmony_ci			sli_queue_arm(&hw->sli, eq->queue, false);
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci		if (tcheck_count && (--tcheck_count == 0)) {
224862306a36Sopenharmony_ci			tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
224962306a36Sopenharmony_ci			telapsed = jiffies_to_msecs(jiffies) - tstart;
225062306a36Sopenharmony_ci			if (telapsed >= max_isr_time_msec)
225162306a36Sopenharmony_ci				done = true;
225262306a36Sopenharmony_ci		}
225362306a36Sopenharmony_ci	}
225462306a36Sopenharmony_ci	sli_queue_eq_arm(&hw->sli, eq->queue, true);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	return 0;
225762306a36Sopenharmony_ci}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_cistatic int
226062306a36Sopenharmony_ci_efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe)
226162306a36Sopenharmony_ci{
226262306a36Sopenharmony_ci	int queue_rc;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	/* Every so often, set the wqec bit to generate comsummed completions */
226562306a36Sopenharmony_ci	if (wq->wqec_count)
226662306a36Sopenharmony_ci		wq->wqec_count--;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	if (wq->wqec_count == 0) {
226962306a36Sopenharmony_ci		struct sli4_generic_wqe *genwqe = (void *)wqe->wqebuf;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci		genwqe->cmdtype_wqec_byte |= SLI4_GEN_WQE_WQEC;
227262306a36Sopenharmony_ci		wq->wqec_count = wq->wqec_set_count;
227362306a36Sopenharmony_ci	}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	/* Decrement WQ free count */
227662306a36Sopenharmony_ci	wq->free_count--;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	queue_rc = sli_wq_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	return (queue_rc < 0) ? -EIO : 0;
228162306a36Sopenharmony_ci}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_cistatic void
228462306a36Sopenharmony_cihw_wq_submit_pending(struct hw_wq *wq, u32 update_free_count)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	struct efct_hw_wqe *wqe;
228762306a36Sopenharmony_ci	unsigned long flags = 0;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	spin_lock_irqsave(&wq->queue->lock, flags);
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	/* Update free count with value passed in */
229262306a36Sopenharmony_ci	wq->free_count += update_free_count;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	while ((wq->free_count > 0) && (!list_empty(&wq->pending_list))) {
229562306a36Sopenharmony_ci		wqe = list_first_entry(&wq->pending_list,
229662306a36Sopenharmony_ci				       struct efct_hw_wqe, list_entry);
229762306a36Sopenharmony_ci		list_del_init(&wqe->list_entry);
229862306a36Sopenharmony_ci		_efct_hw_wq_write(wq, wqe);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci		if (wqe->abort_wqe_submit_needed) {
230162306a36Sopenharmony_ci			wqe->abort_wqe_submit_needed = false;
230262306a36Sopenharmony_ci			efct_hw_fill_abort_wqe(wq->hw, wqe);
230362306a36Sopenharmony_ci			INIT_LIST_HEAD(&wqe->list_entry);
230462306a36Sopenharmony_ci			list_add_tail(&wqe->list_entry, &wq->pending_list);
230562306a36Sopenharmony_ci			wq->wq_pending_count++;
230662306a36Sopenharmony_ci		}
230762306a36Sopenharmony_ci	}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	spin_unlock_irqrestore(&wq->queue->lock, flags);
231062306a36Sopenharmony_ci}
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_civoid
231362306a36Sopenharmony_ciefct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq)
231462306a36Sopenharmony_ci{
231562306a36Sopenharmony_ci	u8 cqe[sizeof(struct sli4_mcqe)];
231662306a36Sopenharmony_ci	u16 rid = U16_MAX;
231762306a36Sopenharmony_ci	/* completion type */
231862306a36Sopenharmony_ci	enum sli4_qentry ctype;
231962306a36Sopenharmony_ci	u32 n_processed = 0;
232062306a36Sopenharmony_ci	u32 tstart, telapsed;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	tstart = jiffies_to_msecs(jiffies);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	while (!sli_cq_read(&hw->sli, cq->queue, cqe)) {
232562306a36Sopenharmony_ci		int status;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci		status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
232862306a36Sopenharmony_ci		/*
232962306a36Sopenharmony_ci		 * The sign of status is significant. If status is:
233062306a36Sopenharmony_ci		 * == 0 : call completed correctly and
233162306a36Sopenharmony_ci		 * the CQE indicated success
233262306a36Sopenharmony_ci		 * > 0 : call completed correctly and
233362306a36Sopenharmony_ci		 * the CQE indicated an error
233462306a36Sopenharmony_ci		 * < 0 : call failed and no information is available about the
233562306a36Sopenharmony_ci		 * CQE
233662306a36Sopenharmony_ci		 */
233762306a36Sopenharmony_ci		if (status < 0) {
233862306a36Sopenharmony_ci			if (status == SLI4_MCQE_STATUS_NOT_COMPLETED)
233962306a36Sopenharmony_ci				/*
234062306a36Sopenharmony_ci				 * Notification that an entry was consumed,
234162306a36Sopenharmony_ci				 * but not completed
234262306a36Sopenharmony_ci				 */
234362306a36Sopenharmony_ci				continue;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci			break;
234662306a36Sopenharmony_ci		}
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci		switch (ctype) {
234962306a36Sopenharmony_ci		case SLI4_QENTRY_ASYNC:
235062306a36Sopenharmony_ci			sli_cqe_async(&hw->sli, cqe);
235162306a36Sopenharmony_ci			break;
235262306a36Sopenharmony_ci		case SLI4_QENTRY_MQ:
235362306a36Sopenharmony_ci			/*
235462306a36Sopenharmony_ci			 * Process MQ entry. Note there is no way to determine
235562306a36Sopenharmony_ci			 * the MQ_ID from the completion entry.
235662306a36Sopenharmony_ci			 */
235762306a36Sopenharmony_ci			efct_hw_mq_process(hw, status, hw->mq);
235862306a36Sopenharmony_ci			break;
235962306a36Sopenharmony_ci		case SLI4_QENTRY_WQ:
236062306a36Sopenharmony_ci			efct_hw_wq_process(hw, cq, cqe, status, rid);
236162306a36Sopenharmony_ci			break;
236262306a36Sopenharmony_ci		case SLI4_QENTRY_WQ_RELEASE: {
236362306a36Sopenharmony_ci			u32 wq_id = rid;
236462306a36Sopenharmony_ci			int index;
236562306a36Sopenharmony_ci			struct hw_wq *wq = NULL;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci			index = efct_hw_queue_hash_find(hw->wq_hash, wq_id);
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci			if (likely(index >= 0)) {
237062306a36Sopenharmony_ci				wq = hw->hw_wq[index];
237162306a36Sopenharmony_ci			} else {
237262306a36Sopenharmony_ci				efc_log_err(hw->os, "bad WQ_ID %#06x\n", wq_id);
237362306a36Sopenharmony_ci				break;
237462306a36Sopenharmony_ci			}
237562306a36Sopenharmony_ci			/* Submit any HW IOs that are on the WQ pending list */
237662306a36Sopenharmony_ci			hw_wq_submit_pending(wq, wq->wqec_set_count);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci			break;
237962306a36Sopenharmony_ci		}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci		case SLI4_QENTRY_RQ:
238262306a36Sopenharmony_ci			efct_hw_rqpair_process_rq(hw, cq, cqe);
238362306a36Sopenharmony_ci			break;
238462306a36Sopenharmony_ci		case SLI4_QENTRY_XABT: {
238562306a36Sopenharmony_ci			efct_hw_xabt_process(hw, cq, cqe, rid);
238662306a36Sopenharmony_ci			break;
238762306a36Sopenharmony_ci		}
238862306a36Sopenharmony_ci		default:
238962306a36Sopenharmony_ci			efc_log_debug(hw->os, "unhandled ctype=%#x rid=%#x\n",
239062306a36Sopenharmony_ci				      ctype, rid);
239162306a36Sopenharmony_ci			break;
239262306a36Sopenharmony_ci		}
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci		n_processed++;
239562306a36Sopenharmony_ci		if (n_processed == cq->queue->proc_limit)
239662306a36Sopenharmony_ci			break;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci		if (cq->queue->n_posted >= cq->queue->posted_limit)
239962306a36Sopenharmony_ci			sli_queue_arm(&hw->sli, cq->queue, false);
240062306a36Sopenharmony_ci	}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	sli_queue_arm(&hw->sli, cq->queue, true);
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	if (n_processed > cq->queue->max_num_processed)
240562306a36Sopenharmony_ci		cq->queue->max_num_processed = n_processed;
240662306a36Sopenharmony_ci	telapsed = jiffies_to_msecs(jiffies) - tstart;
240762306a36Sopenharmony_ci	if (telapsed > cq->queue->max_process_time)
240862306a36Sopenharmony_ci		cq->queue->max_process_time = telapsed;
240962306a36Sopenharmony_ci}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_civoid
241262306a36Sopenharmony_ciefct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq,
241362306a36Sopenharmony_ci		   u8 *cqe, int status, u16 rid)
241462306a36Sopenharmony_ci{
241562306a36Sopenharmony_ci	struct hw_wq_callback *wqcb;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	if (rid == EFCT_HW_REQUE_XRI_REGTAG) {
241862306a36Sopenharmony_ci		if (status)
241962306a36Sopenharmony_ci			efc_log_err(hw->os, "reque xri failed, status = %d\n",
242062306a36Sopenharmony_ci				    status);
242162306a36Sopenharmony_ci		return;
242262306a36Sopenharmony_ci	}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	wqcb = efct_hw_reqtag_get_instance(hw, rid);
242562306a36Sopenharmony_ci	if (!wqcb) {
242662306a36Sopenharmony_ci		efc_log_err(hw->os, "invalid request tag: x%x\n", rid);
242762306a36Sopenharmony_ci		return;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	if (!wqcb->callback) {
243162306a36Sopenharmony_ci		efc_log_err(hw->os, "wqcb callback is NULL\n");
243262306a36Sopenharmony_ci		return;
243362306a36Sopenharmony_ci	}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	(*wqcb->callback)(wqcb->arg, cqe, status);
243662306a36Sopenharmony_ci}
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_civoid
243962306a36Sopenharmony_ciefct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq,
244062306a36Sopenharmony_ci		     u8 *cqe, u16 rid)
244162306a36Sopenharmony_ci{
244262306a36Sopenharmony_ci	/* search IOs wait free list */
244362306a36Sopenharmony_ci	struct efct_hw_io *io = NULL;
244462306a36Sopenharmony_ci	unsigned long flags = 0;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	io = efct_hw_io_lookup(hw, rid);
244762306a36Sopenharmony_ci	if (!io) {
244862306a36Sopenharmony_ci		/* IO lookup failure should never happen */
244962306a36Sopenharmony_ci		efc_log_err(hw->os, "xabt io lookup failed rid=%#x\n", rid);
245062306a36Sopenharmony_ci		return;
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	if (!io->xbusy)
245462306a36Sopenharmony_ci		efc_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
245562306a36Sopenharmony_ci	else
245662306a36Sopenharmony_ci		/* mark IO as no longer busy */
245762306a36Sopenharmony_ci		io->xbusy = false;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	/*
246062306a36Sopenharmony_ci	 * For IOs that were aborted internally, we need to issue any pending
246162306a36Sopenharmony_ci	 * callback here.
246262306a36Sopenharmony_ci	 */
246362306a36Sopenharmony_ci	if (io->done) {
246462306a36Sopenharmony_ci		efct_hw_done_t done = io->done;
246562306a36Sopenharmony_ci		void		*arg = io->arg;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		/*
246862306a36Sopenharmony_ci		 * Use latched status as this is always saved for an internal
246962306a36Sopenharmony_ci		 * abort
247062306a36Sopenharmony_ci		 */
247162306a36Sopenharmony_ci		int status = io->saved_status;
247262306a36Sopenharmony_ci		u32 len = io->saved_len;
247362306a36Sopenharmony_ci		u32 ext = io->saved_ext;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci		io->done = NULL;
247662306a36Sopenharmony_ci		io->status_saved = false;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci		done(io, len, status, ext, arg);
247962306a36Sopenharmony_ci	}
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	spin_lock_irqsave(&hw->io_lock, flags);
248262306a36Sopenharmony_ci	if (io->state == EFCT_HW_IO_STATE_INUSE ||
248362306a36Sopenharmony_ci	    io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
248462306a36Sopenharmony_ci		/* if on wait_free list, caller has already freed IO;
248562306a36Sopenharmony_ci		 * remove from wait_free list and add to free list.
248662306a36Sopenharmony_ci		 * if on in-use list, already marked as no longer busy;
248762306a36Sopenharmony_ci		 * just leave there and wait for caller to free.
248862306a36Sopenharmony_ci		 */
248962306a36Sopenharmony_ci		if (io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
249062306a36Sopenharmony_ci			io->state = EFCT_HW_IO_STATE_FREE;
249162306a36Sopenharmony_ci			list_del_init(&io->list_entry);
249262306a36Sopenharmony_ci			efct_hw_io_free_move_correct_list(hw, io);
249362306a36Sopenharmony_ci		}
249462306a36Sopenharmony_ci	}
249562306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->io_lock, flags);
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_cistatic int
249962306a36Sopenharmony_ciefct_hw_flush(struct efct_hw *hw)
250062306a36Sopenharmony_ci{
250162306a36Sopenharmony_ci	u32 i = 0;
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	/* Process any remaining completions */
250462306a36Sopenharmony_ci	for (i = 0; i < hw->eq_count; i++)
250562306a36Sopenharmony_ci		efct_hw_process(hw, i, ~0);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	return 0;
250862306a36Sopenharmony_ci}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ciint
251162306a36Sopenharmony_ciefct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe)
251262306a36Sopenharmony_ci{
251362306a36Sopenharmony_ci	int rc = 0;
251462306a36Sopenharmony_ci	unsigned long flags = 0;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	spin_lock_irqsave(&wq->queue->lock, flags);
251762306a36Sopenharmony_ci	if (list_empty(&wq->pending_list)) {
251862306a36Sopenharmony_ci		if (wq->free_count > 0) {
251962306a36Sopenharmony_ci			rc = _efct_hw_wq_write(wq, wqe);
252062306a36Sopenharmony_ci		} else {
252162306a36Sopenharmony_ci			INIT_LIST_HEAD(&wqe->list_entry);
252262306a36Sopenharmony_ci			list_add_tail(&wqe->list_entry, &wq->pending_list);
252362306a36Sopenharmony_ci			wq->wq_pending_count++;
252462306a36Sopenharmony_ci		}
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci		spin_unlock_irqrestore(&wq->queue->lock, flags);
252762306a36Sopenharmony_ci		return rc;
252862306a36Sopenharmony_ci	}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	INIT_LIST_HEAD(&wqe->list_entry);
253162306a36Sopenharmony_ci	list_add_tail(&wqe->list_entry, &wq->pending_list);
253262306a36Sopenharmony_ci	wq->wq_pending_count++;
253362306a36Sopenharmony_ci	while (wq->free_count > 0) {
253462306a36Sopenharmony_ci		wqe = list_first_entry(&wq->pending_list, struct efct_hw_wqe,
253562306a36Sopenharmony_ci				       list_entry);
253662306a36Sopenharmony_ci		if (!wqe)
253762306a36Sopenharmony_ci			break;
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci		list_del_init(&wqe->list_entry);
254062306a36Sopenharmony_ci		rc = _efct_hw_wq_write(wq, wqe);
254162306a36Sopenharmony_ci		if (rc)
254262306a36Sopenharmony_ci			break;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci		if (wqe->abort_wqe_submit_needed) {
254562306a36Sopenharmony_ci			wqe->abort_wqe_submit_needed = false;
254662306a36Sopenharmony_ci			efct_hw_fill_abort_wqe(wq->hw, wqe);
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci			INIT_LIST_HEAD(&wqe->list_entry);
254962306a36Sopenharmony_ci			list_add_tail(&wqe->list_entry, &wq->pending_list);
255062306a36Sopenharmony_ci			wq->wq_pending_count++;
255162306a36Sopenharmony_ci		}
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	spin_unlock_irqrestore(&wq->queue->lock, flags);
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	return rc;
255762306a36Sopenharmony_ci}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ciint
256062306a36Sopenharmony_ciefct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls)
256162306a36Sopenharmony_ci{
256262306a36Sopenharmony_ci	struct efct *efct = efc->base;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	return efct_hw_bls_send(efct, type, bls, NULL, NULL);
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ciint
256862306a36Sopenharmony_ciefct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params,
256962306a36Sopenharmony_ci		 void *cb, void *arg)
257062306a36Sopenharmony_ci{
257162306a36Sopenharmony_ci	struct efct_hw *hw = &efct->hw;
257262306a36Sopenharmony_ci	struct efct_hw_io *hio;
257362306a36Sopenharmony_ci	struct sli_bls_payload bls;
257462306a36Sopenharmony_ci	int rc;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_ACTIVE) {
257762306a36Sopenharmony_ci		efc_log_err(hw->os,
257862306a36Sopenharmony_ci			    "cannot send BLS, HW state=%d\n", hw->state);
257962306a36Sopenharmony_ci		return -EIO;
258062306a36Sopenharmony_ci	}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	hio = efct_hw_io_alloc(hw);
258362306a36Sopenharmony_ci	if (!hio) {
258462306a36Sopenharmony_ci		efc_log_err(hw->os, "HIO allocation failed\n");
258562306a36Sopenharmony_ci		return -EIO;
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	hio->done = cb;
258962306a36Sopenharmony_ci	hio->arg  = arg;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	bls_params->xri = hio->indicator;
259262306a36Sopenharmony_ci	bls_params->tag = hio->reqtag;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	if (type == FC_RCTL_BA_ACC) {
259562306a36Sopenharmony_ci		hio->type = EFCT_HW_BLS_ACC;
259662306a36Sopenharmony_ci		bls.type = SLI4_SLI_BLS_ACC;
259762306a36Sopenharmony_ci		memcpy(&bls.u.acc, bls_params->payload, sizeof(bls.u.acc));
259862306a36Sopenharmony_ci	} else {
259962306a36Sopenharmony_ci		hio->type = EFCT_HW_BLS_RJT;
260062306a36Sopenharmony_ci		bls.type = SLI4_SLI_BLS_RJT;
260162306a36Sopenharmony_ci		memcpy(&bls.u.rjt, bls_params->payload, sizeof(bls.u.rjt));
260262306a36Sopenharmony_ci	}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	bls.ox_id = cpu_to_le16(bls_params->ox_id);
260562306a36Sopenharmony_ci	bls.rx_id = cpu_to_le16(bls_params->rx_id);
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	if (sli_xmit_bls_rsp64_wqe(&hw->sli, hio->wqe.wqebuf,
260862306a36Sopenharmony_ci				   &bls, bls_params)) {
260962306a36Sopenharmony_ci		efc_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
261062306a36Sopenharmony_ci		return -EIO;
261162306a36Sopenharmony_ci	}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	hio->xbusy = true;
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	/*
261662306a36Sopenharmony_ci	 * Add IO to active io wqe list before submitting, in case the
261762306a36Sopenharmony_ci	 * wcqe processing preempts this thread.
261862306a36Sopenharmony_ci	 */
261962306a36Sopenharmony_ci	hio->wq->use_count++;
262062306a36Sopenharmony_ci	rc = efct_hw_wq_write(hio->wq, &hio->wqe);
262162306a36Sopenharmony_ci	if (rc >= 0) {
262262306a36Sopenharmony_ci		/* non-negative return is success */
262362306a36Sopenharmony_ci		rc = 0;
262462306a36Sopenharmony_ci	} else {
262562306a36Sopenharmony_ci		/* failed to write wqe, remove from active wqe list */
262662306a36Sopenharmony_ci		efc_log_err(hw->os,
262762306a36Sopenharmony_ci			    "sli_queue_write failed: %d\n", rc);
262862306a36Sopenharmony_ci		hio->xbusy = false;
262962306a36Sopenharmony_ci	}
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	return rc;
263262306a36Sopenharmony_ci}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_cistatic int
263562306a36Sopenharmony_ciefct_els_ssrs_send_cb(struct efct_hw_io *hio, u32 length, int status,
263662306a36Sopenharmony_ci		      u32 ext_status, void *arg)
263762306a36Sopenharmony_ci{
263862306a36Sopenharmony_ci	struct efc_disc_io *io = arg;
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	efc_disc_io_complete(io, length, status, ext_status);
264162306a36Sopenharmony_ci	return 0;
264262306a36Sopenharmony_ci}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_cistatic inline void
264562306a36Sopenharmony_ciefct_fill_els_params(struct efc_disc_io *io, struct sli_els_params *params)
264662306a36Sopenharmony_ci{
264762306a36Sopenharmony_ci	u8 *cmd = io->req.virt;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	params->cmd = *cmd;
265062306a36Sopenharmony_ci	params->s_id = io->s_id;
265162306a36Sopenharmony_ci	params->d_id = io->d_id;
265262306a36Sopenharmony_ci	params->ox_id = io->iparam.els.ox_id;
265362306a36Sopenharmony_ci	params->rpi = io->rpi;
265462306a36Sopenharmony_ci	params->vpi = io->vpi;
265562306a36Sopenharmony_ci	params->rpi_registered = io->rpi_registered;
265662306a36Sopenharmony_ci	params->xmit_len = io->xmit_len;
265762306a36Sopenharmony_ci	params->rsp_len = io->rsp_len;
265862306a36Sopenharmony_ci	params->timeout = io->iparam.els.timeout;
265962306a36Sopenharmony_ci}
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_cistatic inline void
266262306a36Sopenharmony_ciefct_fill_ct_params(struct efc_disc_io *io, struct sli_ct_params *params)
266362306a36Sopenharmony_ci{
266462306a36Sopenharmony_ci	params->r_ctl = io->iparam.ct.r_ctl;
266562306a36Sopenharmony_ci	params->type = io->iparam.ct.type;
266662306a36Sopenharmony_ci	params->df_ctl =  io->iparam.ct.df_ctl;
266762306a36Sopenharmony_ci	params->d_id = io->d_id;
266862306a36Sopenharmony_ci	params->ox_id = io->iparam.ct.ox_id;
266962306a36Sopenharmony_ci	params->rpi = io->rpi;
267062306a36Sopenharmony_ci	params->vpi = io->vpi;
267162306a36Sopenharmony_ci	params->rpi_registered = io->rpi_registered;
267262306a36Sopenharmony_ci	params->xmit_len = io->xmit_len;
267362306a36Sopenharmony_ci	params->rsp_len = io->rsp_len;
267462306a36Sopenharmony_ci	params->timeout = io->iparam.ct.timeout;
267562306a36Sopenharmony_ci}
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci/**
267862306a36Sopenharmony_ci * efct_els_hw_srrs_send() - Send a single request and response cmd.
267962306a36Sopenharmony_ci * @efc: efc library structure
268062306a36Sopenharmony_ci * @io: Discovery IO used to hold els and ct cmd context.
268162306a36Sopenharmony_ci *
268262306a36Sopenharmony_ci * This routine supports communication sequences consisting of a single
268362306a36Sopenharmony_ci * request and single response between two endpoints. Examples include:
268462306a36Sopenharmony_ci *  - Sending an ELS request.
268562306a36Sopenharmony_ci *  - Sending an ELS response - To send an ELS response, the caller must provide
268662306a36Sopenharmony_ci * the OX_ID from the received request.
268762306a36Sopenharmony_ci *  - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
268862306a36Sopenharmony_ci * the caller must provide the R_CTL, TYPE, and DF_CTL
268962306a36Sopenharmony_ci * values to place in the FC frame header.
269062306a36Sopenharmony_ci *
269162306a36Sopenharmony_ci * Return: Status of the request.
269262306a36Sopenharmony_ci */
269362306a36Sopenharmony_ciint
269462306a36Sopenharmony_ciefct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io)
269562306a36Sopenharmony_ci{
269662306a36Sopenharmony_ci	struct efct *efct = efc->base;
269762306a36Sopenharmony_ci	struct efct_hw_io *hio;
269862306a36Sopenharmony_ci	struct efct_hw *hw = &efct->hw;
269962306a36Sopenharmony_ci	struct efc_dma *send = &io->req;
270062306a36Sopenharmony_ci	struct efc_dma *receive = &io->rsp;
270162306a36Sopenharmony_ci	struct sli4_sge	*sge = NULL;
270262306a36Sopenharmony_ci	int rc = 0;
270362306a36Sopenharmony_ci	u32 len = io->xmit_len;
270462306a36Sopenharmony_ci	u32 sge0_flags;
270562306a36Sopenharmony_ci	u32 sge1_flags;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	hio = efct_hw_io_alloc(hw);
270862306a36Sopenharmony_ci	if (!hio) {
270962306a36Sopenharmony_ci		pr_err("HIO alloc failed\n");
271062306a36Sopenharmony_ci		return -EIO;
271162306a36Sopenharmony_ci	}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_ACTIVE) {
271462306a36Sopenharmony_ci		efc_log_debug(hw->os,
271562306a36Sopenharmony_ci			      "cannot send SRRS, HW state=%d\n", hw->state);
271662306a36Sopenharmony_ci		return -EIO;
271762306a36Sopenharmony_ci	}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	hio->done = efct_els_ssrs_send_cb;
272062306a36Sopenharmony_ci	hio->arg  = io;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	sge = hio->sgl->virt;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	/* clear both SGE */
272562306a36Sopenharmony_ci	memset(hio->sgl->virt, 0, 2 * sizeof(struct sli4_sge));
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	sge0_flags = le32_to_cpu(sge[0].dw2_flags);
272862306a36Sopenharmony_ci	sge1_flags = le32_to_cpu(sge[1].dw2_flags);
272962306a36Sopenharmony_ci	if (send->size) {
273062306a36Sopenharmony_ci		sge[0].buffer_address_high =
273162306a36Sopenharmony_ci			cpu_to_le32(upper_32_bits(send->phys));
273262306a36Sopenharmony_ci		sge[0].buffer_address_low  =
273362306a36Sopenharmony_ci			cpu_to_le32(lower_32_bits(send->phys));
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci		sge0_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci		sge[0].buffer_length = cpu_to_le32(len);
273862306a36Sopenharmony_ci	}
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	if (io->io_type == EFC_DISC_IO_ELS_REQ ||
274162306a36Sopenharmony_ci	    io->io_type == EFC_DISC_IO_CT_REQ) {
274262306a36Sopenharmony_ci		sge[1].buffer_address_high =
274362306a36Sopenharmony_ci			cpu_to_le32(upper_32_bits(receive->phys));
274462306a36Sopenharmony_ci		sge[1].buffer_address_low  =
274562306a36Sopenharmony_ci			cpu_to_le32(lower_32_bits(receive->phys));
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci		sge1_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
274862306a36Sopenharmony_ci		sge1_flags |= SLI4_SGE_LAST;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci		sge[1].buffer_length = cpu_to_le32(receive->size);
275162306a36Sopenharmony_ci	} else {
275262306a36Sopenharmony_ci		sge0_flags |= SLI4_SGE_LAST;
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	sge[0].dw2_flags = cpu_to_le32(sge0_flags);
275662306a36Sopenharmony_ci	sge[1].dw2_flags = cpu_to_le32(sge1_flags);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	switch (io->io_type) {
275962306a36Sopenharmony_ci	case EFC_DISC_IO_ELS_REQ: {
276062306a36Sopenharmony_ci		struct sli_els_params els_params;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci		hio->type = EFCT_HW_ELS_REQ;
276362306a36Sopenharmony_ci		efct_fill_els_params(io, &els_params);
276462306a36Sopenharmony_ci		els_params.xri = hio->indicator;
276562306a36Sopenharmony_ci		els_params.tag = hio->reqtag;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci		if (sli_els_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
276862306a36Sopenharmony_ci					  &els_params)) {
276962306a36Sopenharmony_ci			efc_log_err(hw->os, "REQ WQE error\n");
277062306a36Sopenharmony_ci			rc = -EIO;
277162306a36Sopenharmony_ci		}
277262306a36Sopenharmony_ci		break;
277362306a36Sopenharmony_ci	}
277462306a36Sopenharmony_ci	case EFC_DISC_IO_ELS_RESP: {
277562306a36Sopenharmony_ci		struct sli_els_params els_params;
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci		hio->type = EFCT_HW_ELS_RSP;
277862306a36Sopenharmony_ci		efct_fill_els_params(io, &els_params);
277962306a36Sopenharmony_ci		els_params.xri = hio->indicator;
278062306a36Sopenharmony_ci		els_params.tag = hio->reqtag;
278162306a36Sopenharmony_ci		if (sli_xmit_els_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, send,
278262306a36Sopenharmony_ci					   &els_params)){
278362306a36Sopenharmony_ci			efc_log_err(hw->os, "RSP WQE error\n");
278462306a36Sopenharmony_ci			rc = -EIO;
278562306a36Sopenharmony_ci		}
278662306a36Sopenharmony_ci		break;
278762306a36Sopenharmony_ci	}
278862306a36Sopenharmony_ci	case EFC_DISC_IO_CT_REQ: {
278962306a36Sopenharmony_ci		struct sli_ct_params ct_params;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci		hio->type = EFCT_HW_FC_CT;
279262306a36Sopenharmony_ci		efct_fill_ct_params(io, &ct_params);
279362306a36Sopenharmony_ci		ct_params.xri = hio->indicator;
279462306a36Sopenharmony_ci		ct_params.tag = hio->reqtag;
279562306a36Sopenharmony_ci		if (sli_gen_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
279662306a36Sopenharmony_ci					  &ct_params)){
279762306a36Sopenharmony_ci			efc_log_err(hw->os, "GEN WQE error\n");
279862306a36Sopenharmony_ci			rc = -EIO;
279962306a36Sopenharmony_ci		}
280062306a36Sopenharmony_ci		break;
280162306a36Sopenharmony_ci	}
280262306a36Sopenharmony_ci	case EFC_DISC_IO_CT_RESP: {
280362306a36Sopenharmony_ci		struct sli_ct_params ct_params;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci		hio->type = EFCT_HW_FC_CT_RSP;
280662306a36Sopenharmony_ci		efct_fill_ct_params(io, &ct_params);
280762306a36Sopenharmony_ci		ct_params.xri = hio->indicator;
280862306a36Sopenharmony_ci		ct_params.tag = hio->reqtag;
280962306a36Sopenharmony_ci		if (sli_xmit_sequence64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
281062306a36Sopenharmony_ci					    &ct_params)){
281162306a36Sopenharmony_ci			efc_log_err(hw->os, "XMIT SEQ WQE error\n");
281262306a36Sopenharmony_ci			rc = -EIO;
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci		break;
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci	default:
281762306a36Sopenharmony_ci		efc_log_err(hw->os, "bad SRRS type %#x\n", io->io_type);
281862306a36Sopenharmony_ci		rc = -EIO;
281962306a36Sopenharmony_ci	}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	if (rc == 0) {
282262306a36Sopenharmony_ci		hio->xbusy = true;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci		/*
282562306a36Sopenharmony_ci		 * Add IO to active io wqe list before submitting, in case the
282662306a36Sopenharmony_ci		 * wcqe processing preempts this thread.
282762306a36Sopenharmony_ci		 */
282862306a36Sopenharmony_ci		hio->wq->use_count++;
282962306a36Sopenharmony_ci		rc = efct_hw_wq_write(hio->wq, &hio->wqe);
283062306a36Sopenharmony_ci		if (rc >= 0) {
283162306a36Sopenharmony_ci			/* non-negative return is success */
283262306a36Sopenharmony_ci			rc = 0;
283362306a36Sopenharmony_ci		} else {
283462306a36Sopenharmony_ci			/* failed to write wqe, remove from active wqe list */
283562306a36Sopenharmony_ci			efc_log_err(hw->os,
283662306a36Sopenharmony_ci				    "sli_queue_write failed: %d\n", rc);
283762306a36Sopenharmony_ci			hio->xbusy = false;
283862306a36Sopenharmony_ci		}
283962306a36Sopenharmony_ci	}
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	return rc;
284262306a36Sopenharmony_ci}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ciint
284562306a36Sopenharmony_ciefct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type,
284662306a36Sopenharmony_ci		struct efct_hw_io *io, union efct_hw_io_param_u *iparam,
284762306a36Sopenharmony_ci		void *cb, void *arg)
284862306a36Sopenharmony_ci{
284962306a36Sopenharmony_ci	int rc = 0;
285062306a36Sopenharmony_ci	bool send_wqe = true;
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	if (!io) {
285362306a36Sopenharmony_ci		pr_err("bad parm hw=%p io=%p\n", hw, io);
285462306a36Sopenharmony_ci		return -EIO;
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_ACTIVE) {
285862306a36Sopenharmony_ci		efc_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
285962306a36Sopenharmony_ci		return -EIO;
286062306a36Sopenharmony_ci	}
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	/*
286362306a36Sopenharmony_ci	 * Save state needed during later stages
286462306a36Sopenharmony_ci	 */
286562306a36Sopenharmony_ci	io->type  = type;
286662306a36Sopenharmony_ci	io->done  = cb;
286762306a36Sopenharmony_ci	io->arg   = arg;
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	/*
287062306a36Sopenharmony_ci	 * Format the work queue entry used to send the IO
287162306a36Sopenharmony_ci	 */
287262306a36Sopenharmony_ci	switch (type) {
287362306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_WRITE: {
287462306a36Sopenharmony_ci		u16 *flags = &iparam->fcp_tgt.flags;
287562306a36Sopenharmony_ci		struct fcp_txrdy *xfer = io->xfer_rdy.virt;
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci		/*
287862306a36Sopenharmony_ci		 * Fill in the XFER_RDY for IF_TYPE 0 devices
287962306a36Sopenharmony_ci		 */
288062306a36Sopenharmony_ci		xfer->ft_data_ro = cpu_to_be32(iparam->fcp_tgt.offset);
288162306a36Sopenharmony_ci		xfer->ft_burst_len = cpu_to_be32(iparam->fcp_tgt.xmit_len);
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci		if (io->xbusy)
288462306a36Sopenharmony_ci			*flags |= SLI4_IO_CONTINUATION;
288562306a36Sopenharmony_ci		else
288662306a36Sopenharmony_ci			*flags &= ~SLI4_IO_CONTINUATION;
288762306a36Sopenharmony_ci		iparam->fcp_tgt.xri = io->indicator;
288862306a36Sopenharmony_ci		iparam->fcp_tgt.tag = io->reqtag;
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci		if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf,
289162306a36Sopenharmony_ci					   &io->def_sgl, io->first_data_sge,
289262306a36Sopenharmony_ci					   SLI4_CQ_DEFAULT,
289362306a36Sopenharmony_ci					   0, 0, &iparam->fcp_tgt)) {
289462306a36Sopenharmony_ci			efc_log_err(hw->os, "TRECEIVE WQE error\n");
289562306a36Sopenharmony_ci			rc = -EIO;
289662306a36Sopenharmony_ci		}
289762306a36Sopenharmony_ci		break;
289862306a36Sopenharmony_ci	}
289962306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_READ: {
290062306a36Sopenharmony_ci		u16 *flags = &iparam->fcp_tgt.flags;
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci		if (io->xbusy)
290362306a36Sopenharmony_ci			*flags |= SLI4_IO_CONTINUATION;
290462306a36Sopenharmony_ci		else
290562306a36Sopenharmony_ci			*flags &= ~SLI4_IO_CONTINUATION;
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci		iparam->fcp_tgt.xri = io->indicator;
290862306a36Sopenharmony_ci		iparam->fcp_tgt.tag = io->reqtag;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci		if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf,
291162306a36Sopenharmony_ci					&io->def_sgl, io->first_data_sge,
291262306a36Sopenharmony_ci					SLI4_CQ_DEFAULT,
291362306a36Sopenharmony_ci					0, 0, &iparam->fcp_tgt)) {
291462306a36Sopenharmony_ci			efc_log_err(hw->os, "TSEND WQE error\n");
291562306a36Sopenharmony_ci			rc = -EIO;
291662306a36Sopenharmony_ci		}
291762306a36Sopenharmony_ci		break;
291862306a36Sopenharmony_ci	}
291962306a36Sopenharmony_ci	case EFCT_HW_IO_TARGET_RSP: {
292062306a36Sopenharmony_ci		u16 *flags = &iparam->fcp_tgt.flags;
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci		if (io->xbusy)
292362306a36Sopenharmony_ci			*flags |= SLI4_IO_CONTINUATION;
292462306a36Sopenharmony_ci		else
292562306a36Sopenharmony_ci			*flags &= ~SLI4_IO_CONTINUATION;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci		iparam->fcp_tgt.xri = io->indicator;
292862306a36Sopenharmony_ci		iparam->fcp_tgt.tag = io->reqtag;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci		if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf,
293162306a36Sopenharmony_ci				       &io->def_sgl, SLI4_CQ_DEFAULT,
293262306a36Sopenharmony_ci				       0, &iparam->fcp_tgt)) {
293362306a36Sopenharmony_ci			efc_log_err(hw->os, "TRSP WQE error\n");
293462306a36Sopenharmony_ci			rc = -EIO;
293562306a36Sopenharmony_ci		}
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci		break;
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci	default:
294062306a36Sopenharmony_ci		efc_log_err(hw->os, "unsupported IO type %#x\n", type);
294162306a36Sopenharmony_ci		rc = -EIO;
294262306a36Sopenharmony_ci	}
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	if (send_wqe && rc == 0) {
294562306a36Sopenharmony_ci		io->xbusy = true;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci		/*
294862306a36Sopenharmony_ci		 * Add IO to active io wqe list before submitting, in case the
294962306a36Sopenharmony_ci		 * wcqe processing preempts this thread.
295062306a36Sopenharmony_ci		 */
295162306a36Sopenharmony_ci		hw->tcmd_wq_submit[io->wq->instance]++;
295262306a36Sopenharmony_ci		io->wq->use_count++;
295362306a36Sopenharmony_ci		rc = efct_hw_wq_write(io->wq, &io->wqe);
295462306a36Sopenharmony_ci		if (rc >= 0) {
295562306a36Sopenharmony_ci			/* non-negative return is success */
295662306a36Sopenharmony_ci			rc = 0;
295762306a36Sopenharmony_ci		} else {
295862306a36Sopenharmony_ci			/* failed to write wqe, remove from active wqe list */
295962306a36Sopenharmony_ci			efc_log_err(hw->os,
296062306a36Sopenharmony_ci				    "sli_queue_write failed: %d\n", rc);
296162306a36Sopenharmony_ci			io->xbusy = false;
296262306a36Sopenharmony_ci		}
296362306a36Sopenharmony_ci	}
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	return rc;
296662306a36Sopenharmony_ci}
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ciint
296962306a36Sopenharmony_ciefct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr,
297062306a36Sopenharmony_ci		   u8 sof, u8 eof, struct efc_dma *payload,
297162306a36Sopenharmony_ci		   struct efct_hw_send_frame_context *ctx,
297262306a36Sopenharmony_ci		   void (*callback)(void *arg, u8 *cqe, int status),
297362306a36Sopenharmony_ci		   void *arg)
297462306a36Sopenharmony_ci{
297562306a36Sopenharmony_ci	int rc;
297662306a36Sopenharmony_ci	struct efct_hw_wqe *wqe;
297762306a36Sopenharmony_ci	u32 xri;
297862306a36Sopenharmony_ci	struct hw_wq *wq;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	wqe = &ctx->wqe;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	/* populate the callback object */
298362306a36Sopenharmony_ci	ctx->hw = hw;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	/* Fetch and populate request tag */
298662306a36Sopenharmony_ci	ctx->wqcb = efct_hw_reqtag_alloc(hw, callback, arg);
298762306a36Sopenharmony_ci	if (!ctx->wqcb) {
298862306a36Sopenharmony_ci		efc_log_err(hw->os, "can't allocate request tag\n");
298962306a36Sopenharmony_ci		return -ENOSPC;
299062306a36Sopenharmony_ci	}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	wq = hw->hw_wq[0];
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	/* Set XRI and RX_ID in the header based on which WQ, and which
299562306a36Sopenharmony_ci	 * send_frame_io we are using
299662306a36Sopenharmony_ci	 */
299762306a36Sopenharmony_ci	xri = wq->send_frame_io->indicator;
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	/* Build the send frame WQE */
300062306a36Sopenharmony_ci	rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf,
300162306a36Sopenharmony_ci				sof, eof, (u32 *)hdr, payload, payload->len,
300262306a36Sopenharmony_ci				EFCT_HW_SEND_FRAME_TIMEOUT, xri,
300362306a36Sopenharmony_ci				ctx->wqcb->instance_index);
300462306a36Sopenharmony_ci	if (rc) {
300562306a36Sopenharmony_ci		efc_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
300662306a36Sopenharmony_ci		return -EIO;
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	/* Write to WQ */
301062306a36Sopenharmony_ci	rc = efct_hw_wq_write(wq, wqe);
301162306a36Sopenharmony_ci	if (rc) {
301262306a36Sopenharmony_ci		efc_log_err(hw->os, "efct_hw_wq_write failed: %d\n", rc);
301362306a36Sopenharmony_ci		return -EIO;
301462306a36Sopenharmony_ci	}
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci	wq->use_count++;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	return 0;
301962306a36Sopenharmony_ci}
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_cistatic int
302262306a36Sopenharmony_ciefct_hw_cb_link_stat(struct efct_hw *hw, int status,
302362306a36Sopenharmony_ci		     u8 *mqe, void  *arg)
302462306a36Sopenharmony_ci{
302562306a36Sopenharmony_ci	struct sli4_cmd_read_link_stats *mbox_rsp;
302662306a36Sopenharmony_ci	struct efct_hw_link_stat_cb_arg *cb_arg = arg;
302762306a36Sopenharmony_ci	struct efct_hw_link_stat_counts counts[EFCT_HW_LINK_STAT_MAX];
302862306a36Sopenharmony_ci	u32 num_counters, i;
302962306a36Sopenharmony_ci	u32 mbox_rsp_flags = 0;
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	mbox_rsp = (struct sli4_cmd_read_link_stats *)mqe;
303262306a36Sopenharmony_ci	mbox_rsp_flags = le32_to_cpu(mbox_rsp->dw1_flags);
303362306a36Sopenharmony_ci	num_counters = (mbox_rsp_flags & SLI4_READ_LNKSTAT_GEC) ? 20 : 13;
303462306a36Sopenharmony_ci	memset(counts, 0, sizeof(struct efct_hw_link_stat_counts) *
303562306a36Sopenharmony_ci				 EFCT_HW_LINK_STAT_MAX);
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	/* Fill overflow counts, mask starts from SLI4_READ_LNKSTAT_W02OF*/
303862306a36Sopenharmony_ci	for (i = 0; i < EFCT_HW_LINK_STAT_MAX; i++)
303962306a36Sopenharmony_ci		counts[i].overflow = (mbox_rsp_flags & (1 << (i + 2)));
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter =
304262306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->linkfail_errcnt);
304362306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter =
304462306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->losssync_errcnt);
304562306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter =
304662306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->losssignal_errcnt);
304762306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter =
304862306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->primseq_errcnt);
304962306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter =
305062306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->inval_txword_errcnt);
305162306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_CRC_COUNT].counter =
305262306a36Sopenharmony_ci		le32_to_cpu(mbox_rsp->crc_errcnt);
305362306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter =
305462306a36Sopenharmony_ci		le32_to_cpu(mbox_rsp->primseq_eventtimeout_cnt);
305562306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter =
305662306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->elastic_bufoverrun_errcnt);
305762306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter =
305862306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->arbit_fc_al_timeout_cnt);
305962306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter =
306062306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->adv_rx_buftor_to_buf_credit);
306162306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter =
306262306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->curr_rx_buf_to_buf_credit);
306362306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter =
306462306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->adv_tx_buf_to_buf_credit);
306562306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter =
306662306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->curr_tx_buf_to_buf_credit);
306762306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_EOFA_COUNT].counter =
306862306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_eofa_cnt);
306962306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter =
307062306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_eofdti_cnt);
307162306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_EOFNI_COUNT].counter =
307262306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_eofni_cnt);
307362306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_SOFF_COUNT].counter =
307462306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_soff_cnt);
307562306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter =
307662306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_dropped_no_aer_cnt);
307762306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter =
307862306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_dropped_no_avail_rpi_rescnt);
307962306a36Sopenharmony_ci	counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter =
308062306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->rx_dropped_no_avail_xri_rescnt);
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	if (cb_arg) {
308362306a36Sopenharmony_ci		if (cb_arg->cb) {
308462306a36Sopenharmony_ci			if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status))
308562306a36Sopenharmony_ci				status = le16_to_cpu(mbox_rsp->hdr.status);
308662306a36Sopenharmony_ci			cb_arg->cb(status, num_counters, counts, cb_arg->arg);
308762306a36Sopenharmony_ci		}
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci		kfree(cb_arg);
309062306a36Sopenharmony_ci	}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	return 0;
309362306a36Sopenharmony_ci}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ciint
309662306a36Sopenharmony_ciefct_hw_get_link_stats(struct efct_hw *hw, u8 req_ext_counters,
309762306a36Sopenharmony_ci		       u8 clear_overflow_flags, u8 clear_all_counters,
309862306a36Sopenharmony_ci		       void (*cb)(int status, u32 num_counters,
309962306a36Sopenharmony_ci				  struct efct_hw_link_stat_counts *counters,
310062306a36Sopenharmony_ci				  void *arg),
310162306a36Sopenharmony_ci		       void *arg)
310262306a36Sopenharmony_ci{
310362306a36Sopenharmony_ci	int rc = -EIO;
310462306a36Sopenharmony_ci	struct efct_hw_link_stat_cb_arg *cb_arg;
310562306a36Sopenharmony_ci	u8 mbxdata[SLI4_BMBX_SIZE];
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci	cb_arg = kzalloc(sizeof(*cb_arg), GFP_ATOMIC);
310862306a36Sopenharmony_ci	if (!cb_arg)
310962306a36Sopenharmony_ci		return -ENOMEM;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	cb_arg->cb = cb;
311262306a36Sopenharmony_ci	cb_arg->arg = arg;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	/* Send the HW command */
311562306a36Sopenharmony_ci	if (!sli_cmd_read_link_stats(&hw->sli, mbxdata, req_ext_counters,
311662306a36Sopenharmony_ci				    clear_overflow_flags, clear_all_counters))
311762306a36Sopenharmony_ci		rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
311862306a36Sopenharmony_ci				     efct_hw_cb_link_stat, cb_arg);
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	if (rc)
312162306a36Sopenharmony_ci		kfree(cb_arg);
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	return rc;
312462306a36Sopenharmony_ci}
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_cistatic int
312762306a36Sopenharmony_ciefct_hw_cb_host_stat(struct efct_hw *hw, int status, u8 *mqe, void  *arg)
312862306a36Sopenharmony_ci{
312962306a36Sopenharmony_ci	struct sli4_cmd_read_status *mbox_rsp =
313062306a36Sopenharmony_ci					(struct sli4_cmd_read_status *)mqe;
313162306a36Sopenharmony_ci	struct efct_hw_host_stat_cb_arg *cb_arg = arg;
313262306a36Sopenharmony_ci	struct efct_hw_host_stat_counts counts[EFCT_HW_HOST_STAT_MAX];
313362306a36Sopenharmony_ci	u32 num_counters = EFCT_HW_HOST_STAT_MAX;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	memset(counts, 0, sizeof(struct efct_hw_host_stat_counts) *
313662306a36Sopenharmony_ci	       EFCT_HW_HOST_STAT_MAX);
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter =
313962306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->trans_kbyte_cnt);
314062306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter =
314162306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->recv_kbyte_cnt);
314262306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter =
314362306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->trans_frame_cnt);
314462306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter =
314562306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->recv_frame_cnt);
314662306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_TX_SEQ_COUNT].counter =
314762306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->trans_seq_cnt);
314862306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_RX_SEQ_COUNT].counter =
314962306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->recv_seq_cnt);
315062306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter =
315162306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->tot_exchanges_orig);
315262306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_RESP].counter =
315362306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->tot_exchanges_resp);
315462306a36Sopenharmony_ci	counts[EFCT_HW_HOSY_STAT_RX_P_BSY_COUNT].counter =
315562306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->recv_p_bsy_cnt);
315662306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_RX_F_BSY_COUNT].counter =
315762306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->recv_f_bsy_cnt);
315862306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter =
315962306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->no_rq_buf_dropped_frames_cnt);
316062306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter =
316162306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->empty_rq_timeout_cnt);
316262306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter =
316362306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->no_xri_dropped_frames_cnt);
316462306a36Sopenharmony_ci	counts[EFCT_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter =
316562306a36Sopenharmony_ci		 le32_to_cpu(mbox_rsp->empty_xri_pool_cnt);
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	if (cb_arg) {
316862306a36Sopenharmony_ci		if (cb_arg->cb) {
316962306a36Sopenharmony_ci			if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status))
317062306a36Sopenharmony_ci				status = le16_to_cpu(mbox_rsp->hdr.status);
317162306a36Sopenharmony_ci			cb_arg->cb(status, num_counters, counts, cb_arg->arg);
317262306a36Sopenharmony_ci		}
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci		kfree(cb_arg);
317562306a36Sopenharmony_ci	}
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	return 0;
317862306a36Sopenharmony_ci}
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ciint
318162306a36Sopenharmony_ciefct_hw_get_host_stats(struct efct_hw *hw, u8 cc,
318262306a36Sopenharmony_ci		       void (*cb)(int status, u32 num_counters,
318362306a36Sopenharmony_ci				  struct efct_hw_host_stat_counts *counters,
318462306a36Sopenharmony_ci				  void *arg),
318562306a36Sopenharmony_ci		       void *arg)
318662306a36Sopenharmony_ci{
318762306a36Sopenharmony_ci	int rc = -EIO;
318862306a36Sopenharmony_ci	struct efct_hw_host_stat_cb_arg *cb_arg;
318962306a36Sopenharmony_ci	u8 mbxdata[SLI4_BMBX_SIZE];
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	cb_arg = kmalloc(sizeof(*cb_arg), GFP_ATOMIC);
319262306a36Sopenharmony_ci	if (!cb_arg)
319362306a36Sopenharmony_ci		return -ENOMEM;
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	cb_arg->cb = cb;
319662306a36Sopenharmony_ci	cb_arg->arg = arg;
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci	 /* Send the HW command to get the host stats */
319962306a36Sopenharmony_ci	if (!sli_cmd_read_status(&hw->sli, mbxdata, cc))
320062306a36Sopenharmony_ci		rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
320162306a36Sopenharmony_ci				     efct_hw_cb_host_stat, cb_arg);
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	if (rc) {
320462306a36Sopenharmony_ci		efc_log_debug(hw->os, "READ_HOST_STATS failed\n");
320562306a36Sopenharmony_ci		kfree(cb_arg);
320662306a36Sopenharmony_ci	}
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	return rc;
320962306a36Sopenharmony_ci}
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_cistruct efct_hw_async_call_ctx {
321262306a36Sopenharmony_ci	efct_hw_async_cb_t callback;
321362306a36Sopenharmony_ci	void *arg;
321462306a36Sopenharmony_ci	u8 cmd[SLI4_BMBX_SIZE];
321562306a36Sopenharmony_ci};
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_cistatic void
321862306a36Sopenharmony_ciefct_hw_async_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
321962306a36Sopenharmony_ci{
322062306a36Sopenharmony_ci	struct efct_hw_async_call_ctx *ctx = arg;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	if (ctx) {
322362306a36Sopenharmony_ci		if (ctx->callback)
322462306a36Sopenharmony_ci			(*ctx->callback)(hw, status, mqe, ctx->arg);
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci		kfree(ctx);
322762306a36Sopenharmony_ci	}
322862306a36Sopenharmony_ci}
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ciint
323162306a36Sopenharmony_ciefct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg)
323262306a36Sopenharmony_ci{
323362306a36Sopenharmony_ci	struct efct_hw_async_call_ctx *ctx;
323462306a36Sopenharmony_ci	int rc;
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci	/*
323762306a36Sopenharmony_ci	 * Allocate a callback context (which includes the mbox cmd buffer),
323862306a36Sopenharmony_ci	 * we need this to be persistent as the mbox cmd submission may be
323962306a36Sopenharmony_ci	 * queued and executed later execution.
324062306a36Sopenharmony_ci	 */
324162306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
324262306a36Sopenharmony_ci	if (!ctx)
324362306a36Sopenharmony_ci		return -ENOMEM;
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	ctx->callback = callback;
324662306a36Sopenharmony_ci	ctx->arg = arg;
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	/* Build and send a NOP mailbox command */
324962306a36Sopenharmony_ci	if (sli_cmd_common_nop(&hw->sli, ctx->cmd, 0)) {
325062306a36Sopenharmony_ci		efc_log_err(hw->os, "COMMON_NOP format failure\n");
325162306a36Sopenharmony_ci		kfree(ctx);
325262306a36Sopenharmony_ci		return -EIO;
325362306a36Sopenharmony_ci	}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	rc = efct_hw_command(hw, ctx->cmd, EFCT_CMD_NOWAIT, efct_hw_async_cb,
325662306a36Sopenharmony_ci			     ctx);
325762306a36Sopenharmony_ci	if (rc) {
325862306a36Sopenharmony_ci		efc_log_err(hw->os, "COMMON_NOP command failure, rc=%d\n", rc);
325962306a36Sopenharmony_ci		kfree(ctx);
326062306a36Sopenharmony_ci		return -EIO;
326162306a36Sopenharmony_ci	}
326262306a36Sopenharmony_ci	return 0;
326362306a36Sopenharmony_ci}
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_cistatic int
326662306a36Sopenharmony_ciefct_hw_cb_fw_write(struct efct_hw *hw, int status, u8 *mqe, void  *arg)
326762306a36Sopenharmony_ci{
326862306a36Sopenharmony_ci	struct sli4_cmd_sli_config *mbox_rsp =
326962306a36Sopenharmony_ci					(struct sli4_cmd_sli_config *)mqe;
327062306a36Sopenharmony_ci	struct sli4_rsp_cmn_write_object *wr_obj_rsp;
327162306a36Sopenharmony_ci	struct efct_hw_fw_wr_cb_arg *cb_arg = arg;
327262306a36Sopenharmony_ci	u32 bytes_written;
327362306a36Sopenharmony_ci	u16 mbox_status;
327462306a36Sopenharmony_ci	u32 change_status;
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	wr_obj_rsp = (struct sli4_rsp_cmn_write_object *)
327762306a36Sopenharmony_ci		      &mbox_rsp->payload.embed;
327862306a36Sopenharmony_ci	bytes_written = le32_to_cpu(wr_obj_rsp->actual_write_length);
327962306a36Sopenharmony_ci	mbox_status = le16_to_cpu(mbox_rsp->hdr.status);
328062306a36Sopenharmony_ci	change_status = (le32_to_cpu(wr_obj_rsp->change_status_dword) &
328162306a36Sopenharmony_ci			 RSP_CHANGE_STATUS);
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	if (cb_arg) {
328462306a36Sopenharmony_ci		if (cb_arg->cb) {
328562306a36Sopenharmony_ci			if (!status && mbox_status)
328662306a36Sopenharmony_ci				status = mbox_status;
328762306a36Sopenharmony_ci			cb_arg->cb(status, bytes_written, change_status,
328862306a36Sopenharmony_ci				   cb_arg->arg);
328962306a36Sopenharmony_ci		}
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci		kfree(cb_arg);
329262306a36Sopenharmony_ci	}
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	return 0;
329562306a36Sopenharmony_ci}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ciint
329862306a36Sopenharmony_ciefct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma, u32 size,
329962306a36Sopenharmony_ci		       u32 offset, int last,
330062306a36Sopenharmony_ci		       void (*cb)(int status, u32 bytes_written,
330162306a36Sopenharmony_ci				   u32 change_status, void *arg),
330262306a36Sopenharmony_ci		       void *arg)
330362306a36Sopenharmony_ci{
330462306a36Sopenharmony_ci	int rc = -EIO;
330562306a36Sopenharmony_ci	u8 mbxdata[SLI4_BMBX_SIZE];
330662306a36Sopenharmony_ci	struct efct_hw_fw_wr_cb_arg *cb_arg;
330762306a36Sopenharmony_ci	int noc = 0;
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	cb_arg = kzalloc(sizeof(*cb_arg), GFP_KERNEL);
331062306a36Sopenharmony_ci	if (!cb_arg)
331162306a36Sopenharmony_ci		return -ENOMEM;
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_ci	cb_arg->cb = cb;
331462306a36Sopenharmony_ci	cb_arg->arg = arg;
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci	/* Write a portion of a firmware image to the device */
331762306a36Sopenharmony_ci	if (!sli_cmd_common_write_object(&hw->sli, mbxdata,
331862306a36Sopenharmony_ci					 noc, last, size, offset, "/prg/",
331962306a36Sopenharmony_ci					 dma))
332062306a36Sopenharmony_ci		rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
332162306a36Sopenharmony_ci				     efct_hw_cb_fw_write, cb_arg);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	if (rc != 0) {
332462306a36Sopenharmony_ci		efc_log_debug(hw->os, "COMMON_WRITE_OBJECT failed\n");
332562306a36Sopenharmony_ci		kfree(cb_arg);
332662306a36Sopenharmony_ci	}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	return rc;
332962306a36Sopenharmony_ci}
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_cistatic int
333262306a36Sopenharmony_ciefct_hw_cb_port_control(struct efct_hw *hw, int status, u8 *mqe,
333362306a36Sopenharmony_ci			void  *arg)
333462306a36Sopenharmony_ci{
333562306a36Sopenharmony_ci	return 0;
333662306a36Sopenharmony_ci}
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ciint
333962306a36Sopenharmony_ciefct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
334062306a36Sopenharmony_ci		     uintptr_t value,
334162306a36Sopenharmony_ci		     void (*cb)(int status, uintptr_t value, void *arg),
334262306a36Sopenharmony_ci		     void *arg)
334362306a36Sopenharmony_ci{
334462306a36Sopenharmony_ci	int rc = -EIO;
334562306a36Sopenharmony_ci	u8 link[SLI4_BMBX_SIZE];
334662306a36Sopenharmony_ci	u32 speed = 0;
334762306a36Sopenharmony_ci	u8 reset_alpa = 0;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	switch (ctrl) {
335062306a36Sopenharmony_ci	case EFCT_HW_PORT_INIT:
335162306a36Sopenharmony_ci		if (!sli_cmd_config_link(&hw->sli, link))
335262306a36Sopenharmony_ci			rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
335362306a36Sopenharmony_ci					     efct_hw_cb_port_control, NULL);
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci		if (rc != 0) {
335662306a36Sopenharmony_ci			efc_log_err(hw->os, "CONFIG_LINK failed\n");
335762306a36Sopenharmony_ci			break;
335862306a36Sopenharmony_ci		}
335962306a36Sopenharmony_ci		speed = hw->config.speed;
336062306a36Sopenharmony_ci		reset_alpa = (u8)(value & 0xff);
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci		rc = -EIO;
336362306a36Sopenharmony_ci		if (!sli_cmd_init_link(&hw->sli, link, speed, reset_alpa))
336462306a36Sopenharmony_ci			rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
336562306a36Sopenharmony_ci					     efct_hw_cb_port_control, NULL);
336662306a36Sopenharmony_ci		/* Free buffer on error, since no callback is coming */
336762306a36Sopenharmony_ci		if (rc)
336862306a36Sopenharmony_ci			efc_log_err(hw->os, "INIT_LINK failed\n");
336962306a36Sopenharmony_ci		break;
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci	case EFCT_HW_PORT_SHUTDOWN:
337262306a36Sopenharmony_ci		if (!sli_cmd_down_link(&hw->sli, link))
337362306a36Sopenharmony_ci			rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
337462306a36Sopenharmony_ci					     efct_hw_cb_port_control, NULL);
337562306a36Sopenharmony_ci		/* Free buffer on error, since no callback is coming */
337662306a36Sopenharmony_ci		if (rc)
337762306a36Sopenharmony_ci			efc_log_err(hw->os, "DOWN_LINK failed\n");
337862306a36Sopenharmony_ci		break;
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	default:
338162306a36Sopenharmony_ci		efc_log_debug(hw->os, "unhandled control %#x\n", ctrl);
338262306a36Sopenharmony_ci		break;
338362306a36Sopenharmony_ci	}
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci	return rc;
338662306a36Sopenharmony_ci}
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_civoid
338962306a36Sopenharmony_ciefct_hw_teardown(struct efct_hw *hw)
339062306a36Sopenharmony_ci{
339162306a36Sopenharmony_ci	u32 i = 0;
339262306a36Sopenharmony_ci	u32 destroy_queues;
339362306a36Sopenharmony_ci	u32 free_memory;
339462306a36Sopenharmony_ci	struct efc_dma *dma;
339562306a36Sopenharmony_ci	struct efct *efct = hw->os;
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE);
339862306a36Sopenharmony_ci	free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED);
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	/* Cancel Sliport Healthcheck */
340162306a36Sopenharmony_ci	if (hw->sliport_healthcheck) {
340262306a36Sopenharmony_ci		hw->sliport_healthcheck = 0;
340362306a36Sopenharmony_ci		efct_hw_config_sli_port_health_check(hw, 0, 0);
340462306a36Sopenharmony_ci	}
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_QUEUES_ALLOCATED) {
340762306a36Sopenharmony_ci		hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci		efct_hw_flush(hw);
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci		if (list_empty(&hw->cmd_head))
341262306a36Sopenharmony_ci			efc_log_debug(hw->os,
341362306a36Sopenharmony_ci				      "All commands completed on MQ queue\n");
341462306a36Sopenharmony_ci		else
341562306a36Sopenharmony_ci			efc_log_debug(hw->os,
341662306a36Sopenharmony_ci				      "Some cmds still pending on MQ queue\n");
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci		/* Cancel any remaining commands */
341962306a36Sopenharmony_ci		efct_hw_command_cancel(hw);
342062306a36Sopenharmony_ci	} else {
342162306a36Sopenharmony_ci		hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
342262306a36Sopenharmony_ci	}
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci	dma_free_coherent(&efct->pci->dev,
342562306a36Sopenharmony_ci			  hw->rnode_mem.size, hw->rnode_mem.virt,
342662306a36Sopenharmony_ci			  hw->rnode_mem.phys);
342762306a36Sopenharmony_ci	memset(&hw->rnode_mem, 0, sizeof(struct efc_dma));
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	if (hw->io) {
343062306a36Sopenharmony_ci		for (i = 0; i < hw->config.n_io; i++) {
343162306a36Sopenharmony_ci			if (hw->io[i] && hw->io[i]->sgl &&
343262306a36Sopenharmony_ci			    hw->io[i]->sgl->virt) {
343362306a36Sopenharmony_ci				dma_free_coherent(&efct->pci->dev,
343462306a36Sopenharmony_ci						  hw->io[i]->sgl->size,
343562306a36Sopenharmony_ci						  hw->io[i]->sgl->virt,
343662306a36Sopenharmony_ci						  hw->io[i]->sgl->phys);
343762306a36Sopenharmony_ci			}
343862306a36Sopenharmony_ci			kfree(hw->io[i]);
343962306a36Sopenharmony_ci			hw->io[i] = NULL;
344062306a36Sopenharmony_ci		}
344162306a36Sopenharmony_ci		kfree(hw->io);
344262306a36Sopenharmony_ci		hw->io = NULL;
344362306a36Sopenharmony_ci		kfree(hw->wqe_buffs);
344462306a36Sopenharmony_ci		hw->wqe_buffs = NULL;
344562306a36Sopenharmony_ci	}
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	dma = &hw->xfer_rdy;
344862306a36Sopenharmony_ci	dma_free_coherent(&efct->pci->dev,
344962306a36Sopenharmony_ci			  dma->size, dma->virt, dma->phys);
345062306a36Sopenharmony_ci	memset(dma, 0, sizeof(struct efc_dma));
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	dma = &hw->loop_map;
345362306a36Sopenharmony_ci	dma_free_coherent(&efct->pci->dev,
345462306a36Sopenharmony_ci			  dma->size, dma->virt, dma->phys);
345562306a36Sopenharmony_ci	memset(dma, 0, sizeof(struct efc_dma));
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	for (i = 0; i < hw->wq_count; i++)
345862306a36Sopenharmony_ci		sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues,
345962306a36Sopenharmony_ci			       free_memory);
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci	for (i = 0; i < hw->rq_count; i++)
346262306a36Sopenharmony_ci		sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues,
346362306a36Sopenharmony_ci			       free_memory);
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	for (i = 0; i < hw->mq_count; i++)
346662306a36Sopenharmony_ci		sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues,
346762306a36Sopenharmony_ci			       free_memory);
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	for (i = 0; i < hw->cq_count; i++)
347062306a36Sopenharmony_ci		sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues,
347162306a36Sopenharmony_ci			       free_memory);
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	for (i = 0; i < hw->eq_count; i++)
347462306a36Sopenharmony_ci		sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues,
347562306a36Sopenharmony_ci			       free_memory);
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	/* Free rq buffers */
347862306a36Sopenharmony_ci	efct_hw_rx_free(hw);
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci	efct_hw_queue_teardown(hw);
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	kfree(hw->wq_cpu_array);
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	sli_teardown(&hw->sli);
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	/* record the fact that the queues are non-functional */
348762306a36Sopenharmony_ci	hw->state = EFCT_HW_STATE_UNINITIALIZED;
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	/* free sequence free pool */
349062306a36Sopenharmony_ci	kfree(hw->seq_pool);
349162306a36Sopenharmony_ci	hw->seq_pool = NULL;
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	/* free hw_wq_callback pool */
349462306a36Sopenharmony_ci	efct_hw_reqtag_pool_free(hw);
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ci	mempool_destroy(hw->cmd_ctx_pool);
349762306a36Sopenharmony_ci	mempool_destroy(hw->mbox_rqst_pool);
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci	/* Mark HW setup as not having been called */
350062306a36Sopenharmony_ci	hw->hw_setup_called = false;
350162306a36Sopenharmony_ci}
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_cistatic int
350462306a36Sopenharmony_ciefct_hw_sli_reset(struct efct_hw *hw, enum efct_hw_reset reset,
350562306a36Sopenharmony_ci		  enum efct_hw_state prev_state)
350662306a36Sopenharmony_ci{
350762306a36Sopenharmony_ci	int rc = 0;
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ci	switch (reset) {
351062306a36Sopenharmony_ci	case EFCT_HW_RESET_FUNCTION:
351162306a36Sopenharmony_ci		efc_log_debug(hw->os, "issuing function level reset\n");
351262306a36Sopenharmony_ci		if (sli_reset(&hw->sli)) {
351362306a36Sopenharmony_ci			efc_log_err(hw->os, "sli_reset failed\n");
351462306a36Sopenharmony_ci			rc = -EIO;
351562306a36Sopenharmony_ci		}
351662306a36Sopenharmony_ci		break;
351762306a36Sopenharmony_ci	case EFCT_HW_RESET_FIRMWARE:
351862306a36Sopenharmony_ci		efc_log_debug(hw->os, "issuing firmware reset\n");
351962306a36Sopenharmony_ci		if (sli_fw_reset(&hw->sli)) {
352062306a36Sopenharmony_ci			efc_log_err(hw->os, "sli_soft_reset failed\n");
352162306a36Sopenharmony_ci			rc = -EIO;
352262306a36Sopenharmony_ci		}
352362306a36Sopenharmony_ci		/*
352462306a36Sopenharmony_ci		 * Because the FW reset leaves the FW in a non-running state,
352562306a36Sopenharmony_ci		 * follow that with a regular reset.
352662306a36Sopenharmony_ci		 */
352762306a36Sopenharmony_ci		efc_log_debug(hw->os, "issuing function level reset\n");
352862306a36Sopenharmony_ci		if (sli_reset(&hw->sli)) {
352962306a36Sopenharmony_ci			efc_log_err(hw->os, "sli_reset failed\n");
353062306a36Sopenharmony_ci			rc = -EIO;
353162306a36Sopenharmony_ci		}
353262306a36Sopenharmony_ci		break;
353362306a36Sopenharmony_ci	default:
353462306a36Sopenharmony_ci		efc_log_err(hw->os, "unknown type - no reset performed\n");
353562306a36Sopenharmony_ci		hw->state = prev_state;
353662306a36Sopenharmony_ci		rc = -EINVAL;
353762306a36Sopenharmony_ci		break;
353862306a36Sopenharmony_ci	}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	return rc;
354162306a36Sopenharmony_ci}
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ciint
354462306a36Sopenharmony_ciefct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset)
354562306a36Sopenharmony_ci{
354662306a36Sopenharmony_ci	int rc = 0;
354762306a36Sopenharmony_ci	enum efct_hw_state prev_state = hw->state;
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci	if (hw->state != EFCT_HW_STATE_ACTIVE)
355062306a36Sopenharmony_ci		efc_log_debug(hw->os,
355162306a36Sopenharmony_ci			      "HW state %d is not active\n", hw->state);
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	hw->state = EFCT_HW_STATE_RESET_IN_PROGRESS;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	/*
355662306a36Sopenharmony_ci	 * If the prev_state is already reset/teardown in progress,
355762306a36Sopenharmony_ci	 * don't continue further
355862306a36Sopenharmony_ci	 */
355962306a36Sopenharmony_ci	if (prev_state == EFCT_HW_STATE_RESET_IN_PROGRESS ||
356062306a36Sopenharmony_ci	    prev_state == EFCT_HW_STATE_TEARDOWN_IN_PROGRESS)
356162306a36Sopenharmony_ci		return efct_hw_sli_reset(hw, reset, prev_state);
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	if (prev_state != EFCT_HW_STATE_UNINITIALIZED) {
356462306a36Sopenharmony_ci		efct_hw_flush(hw);
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci		if (list_empty(&hw->cmd_head))
356762306a36Sopenharmony_ci			efc_log_debug(hw->os,
356862306a36Sopenharmony_ci				      "All commands completed on MQ queue\n");
356962306a36Sopenharmony_ci		else
357062306a36Sopenharmony_ci			efc_log_err(hw->os,
357162306a36Sopenharmony_ci				    "Some commands still pending on MQ queue\n");
357262306a36Sopenharmony_ci	}
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	/* Reset the chip */
357562306a36Sopenharmony_ci	rc = efct_hw_sli_reset(hw, reset, prev_state);
357662306a36Sopenharmony_ci	if (rc == -EINVAL)
357762306a36Sopenharmony_ci		return -EIO;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	return rc;
358062306a36Sopenharmony_ci}
3581