1331766Sken/*-
2331766Sken * Copyright (c) 2017 Broadcom. All rights reserved.
3331766Sken * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4331766Sken *
5331766Sken * Redistribution and use in source and binary forms, with or without
6331766Sken * modification, are permitted provided that the following conditions are met:
7331766Sken *
8331766Sken * 1. Redistributions of source code must retain the above copyright notice,
9331766Sken *    this list of conditions and the following disclaimer.
10331766Sken *
11331766Sken * 2. Redistributions in binary form must reproduce the above copyright notice,
12331766Sken *    this list of conditions and the following disclaimer in the documentation
13331766Sken *    and/or other materials provided with the distribution.
14331766Sken *
15331766Sken * 3. Neither the name of the copyright holder nor the names of its contributors
16331766Sken *    may be used to endorse or promote products derived from this software
17331766Sken *    without specific prior written permission.
18331766Sken *
19331766Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20331766Sken * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21331766Sken * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22331766Sken * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23331766Sken * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24331766Sken * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25331766Sken * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26331766Sken * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27331766Sken * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28331766Sken * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29331766Sken * POSSIBILITY OF SUCH DAMAGE.
30331766Sken *
31331766Sken * $FreeBSD: stable/11/sys/dev/ocs_fc/ocs_hw.c 331766 2018-03-30 15:28:25Z ken $
32331766Sken */
33331766Sken
34331766Sken/**
35331766Sken * @file
36331766Sken * Defines and implements the Hardware Abstraction Layer (HW).
37331766Sken * All interaction with the hardware is performed through the HW, which abstracts
38331766Sken * the details of the underlying SLI-4 implementation.
39331766Sken */
40331766Sken
41331766Sken/**
42331766Sken * @defgroup devInitShutdown Device Initialization and Shutdown
43331766Sken * @defgroup domain Domain Functions
44331766Sken * @defgroup port Port Functions
45331766Sken * @defgroup node Remote Node Functions
46331766Sken * @defgroup io IO Functions
47331766Sken * @defgroup interrupt Interrupt handling
48331766Sken * @defgroup os OS Required Functions
49331766Sken */
50331766Sken
51331766Sken#include "ocs.h"
52331766Sken#include "ocs_os.h"
53331766Sken#include "ocs_hw.h"
54331766Sken#include "ocs_hw_queues.h"
55331766Sken
56331766Sken#define OCS_HW_MQ_DEPTH	128
57331766Sken#define OCS_HW_READ_FCF_SIZE	4096
58331766Sken#define OCS_HW_DEFAULT_AUTO_XFER_RDY_IOS	256
59331766Sken#define OCS_HW_WQ_TIMER_PERIOD_MS	500
60331766Sken
61331766Sken/* values used for setting the auto xfer rdy parameters */
62331766Sken#define OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT		0 /* 512 bytes */
63331766Sken#define OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT	TRUE
64331766Sken#define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT	FALSE
65331766Sken#define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT	0
66331766Sken#define OCS_HW_REQUE_XRI_REGTAG			65534
67331766Sken/* max command and response buffer lengths -- arbitrary at the moment */
68331766Sken#define OCS_HW_DMTF_CLP_CMD_MAX	256
69331766Sken#define OCS_HW_DMTF_CLP_RSP_MAX	256
70331766Sken
71331766Sken/* HW global data */
72331766Skenocs_hw_global_t hw_global;
73331766Sken
74331766Skenstatic void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
75331766Skenstatic void ocs_hw_adjust_wqs(ocs_hw_t *hw);
76331766Skenstatic uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
77331766Skenstatic int32_t ocs_hw_cb_link(void *, void *);
78331766Skenstatic int32_t ocs_hw_cb_fip(void *, void *);
79331766Skenstatic int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
80331766Skenstatic int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
81331766Skenstatic int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
82331766Skenstatic int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
83331766Skenstatic int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
84331766Skenstatic int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
85331766Skenstatic ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
86331766Skenstatic ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
87331766Skenstatic int32_t ocs_hw_flush(ocs_hw_t *);
88331766Skenstatic int32_t ocs_hw_command_cancel(ocs_hw_t *);
89331766Skenstatic int32_t ocs_hw_io_cancel(ocs_hw_t *);
90331766Skenstatic void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
91331766Skenstatic void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
92331766Skenstatic int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
93331766Skenstatic ocs_hw_rtn_e ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg);
94331766Skenstatic int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void  *);
95331766Skenstatic int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void  *);
96331766Skenstatic int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void  *);
97331766Skenstatic int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void  *);
98331766Skenstatic int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
99331766Skenstatic void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
100331766Skenstatic int32_t ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len);
101331766Skentypedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
102331766Skenstatic ocs_hw_rtn_e ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg);
103331766Skenstatic void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
104331766Sken
105331766Skenstatic int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
106331766Skenstatic ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
107331766Skenstatic ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
108331766Skenstatic ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
109331766Skenstatic ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
110331766Skenstatic ocs_hw_rtn_e ocs_hw_set_linkcfg_lancer(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
111331766Skenstatic ocs_hw_rtn_e ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
112331766Skenstatic void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
113331766Skenstatic ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
114331766Skenstatic ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
115331766Skenstatic ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
116331766Skenstatic void ocs_hw_io_free_internal(void *arg);
117331766Skenstatic void ocs_hw_io_free_port_owned(void *arg);
118331766Skenstatic ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
119331766Skenstatic ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
120331766Skenstatic void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
121331766Skenstatic int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
122331766Skenstatic ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
123331766Skenstatic ocs_hw_rtn_e ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable);
124331766Sken
125331766Sken/* HW domain database operations */
126331766Skenstatic int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
127331766Skenstatic int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
128331766Sken
129331766Sken
130331766Sken/* Port state machine */
131331766Skenstatic void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
132331766Skenstatic void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
133331766Skenstatic void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
134331766Skenstatic void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
135331766Skenstatic void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
136331766Sken
137331766Sken/* Domain state machine */
138331766Skenstatic void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
139331766Skenstatic void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
140331766Skenstatic void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
141331766Skenstatic void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
142331766Skenstatic void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
143331766Skenstatic int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
144331766Skenstatic int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
145331766Skenstatic int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
146331766Sken
147331766Sken/* BZ 161832 */
148331766Skenstatic void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
149331766Sken
150331766Sken/* WQE timeouts */
151331766Skenstatic void target_wqe_timer_cb(void *arg);
152331766Skenstatic void shutdown_target_wqe_timer(ocs_hw_t *hw);
153331766Sken
154331766Skenstatic inline void
155331766Skenocs_hw_add_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
156331766Sken{
157331766Sken	if (hw->config.emulate_tgt_wqe_timeout && io->tgt_wqe_timeout) {
158331766Sken		/*
159331766Sken		 * Active WQE list currently only used for
160331766Sken		 * target WQE timeouts.
161331766Sken		 */
162331766Sken		ocs_lock(&hw->io_lock);
163331766Sken			ocs_list_add_tail(&hw->io_timed_wqe, io);
164331766Sken			io->submit_ticks = ocs_get_os_ticks();
165331766Sken		ocs_unlock(&hw->io_lock);
166331766Sken	}
167331766Sken}
168331766Sken
169331766Skenstatic inline void
170331766Skenocs_hw_remove_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
171331766Sken{
172331766Sken	if (hw->config.emulate_tgt_wqe_timeout) {
173331766Sken		/*
174331766Sken		 * If target wqe timeouts are enabled,
175331766Sken		 * remove from active wqe list.
176331766Sken		 */
177331766Sken		ocs_lock(&hw->io_lock);
178331766Sken			if (ocs_list_on_list(&io->wqe_link)) {
179331766Sken				ocs_list_remove(&hw->io_timed_wqe, io);
180331766Sken			}
181331766Sken		ocs_unlock(&hw->io_lock);
182331766Sken	}
183331766Sken}
184331766Sken
185331766Skenstatic uint8_t ocs_hw_iotype_is_originator(uint16_t io_type)
186331766Sken{
187331766Sken	switch (io_type) {
188331766Sken	case OCS_HW_IO_INITIATOR_READ:
189331766Sken	case OCS_HW_IO_INITIATOR_WRITE:
190331766Sken	case OCS_HW_IO_INITIATOR_NODATA:
191331766Sken	case OCS_HW_FC_CT:
192331766Sken	case OCS_HW_ELS_REQ:
193331766Sken		return 1;
194331766Sken	default:
195331766Sken		return 0;
196331766Sken	}
197331766Sken}
198331766Sken
199331766Skenstatic uint8_t ocs_hw_wcqe_abort_needed(uint16_t status, uint8_t ext, uint8_t xb)
200331766Sken{
201331766Sken	/* if exchange not active, nothing to abort */
202331766Sken	if (!xb) {
203331766Sken		return FALSE;
204331766Sken	}
205331766Sken	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT) {
206331766Sken		switch (ext) {
207331766Sken		/* exceptions where abort is not needed */
208331766Sken		case SLI4_FC_LOCAL_REJECT_INVALID_RPI: /* lancer returns this after unreg_rpi */
209331766Sken		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED: /* abort already in progress */
210331766Sken			return FALSE;
211331766Sken		default:
212331766Sken			break;
213331766Sken		}
214331766Sken	}
215331766Sken	return TRUE;
216331766Sken}
217331766Sken
218331766Sken/**
219331766Sken * @brief Determine the number of chutes on the device.
220331766Sken *
221331766Sken * @par Description
222331766Sken * Some devices require queue resources allocated per protocol processor
223331766Sken * (chute). This function returns the number of chutes on this device.
224331766Sken *
225331766Sken * @param hw Hardware context allocated by the caller.
226331766Sken *
227331766Sken * @return Returns the number of chutes on the device for protocol.
228331766Sken */
229331766Skenstatic uint32_t
230331766Skenocs_hw_get_num_chutes(ocs_hw_t *hw)
231331766Sken{
232331766Sken	uint32_t num_chutes = 1;
233331766Sken
234331766Sken	if (sli_get_is_dual_ulp_capable(&hw->sli) &&
235331766Sken	    sli_get_is_ulp_enabled(&hw->sli, 0) &&
236331766Sken	    sli_get_is_ulp_enabled(&hw->sli, 1)) {
237331766Sken		num_chutes = 2;
238331766Sken	}
239331766Sken	return num_chutes;
240331766Sken}
241331766Sken
242331766Skenstatic ocs_hw_rtn_e
243331766Skenocs_hw_link_event_init(ocs_hw_t *hw)
244331766Sken{
245331766Sken	if (hw == NULL) {
246331766Sken		ocs_log_err(hw->os, "bad parameter hw=%p\n", hw);
247331766Sken		return OCS_HW_RTN_ERROR;
248331766Sken	}
249331766Sken
250331766Sken	hw->link.status = SLI_LINK_STATUS_MAX;
251331766Sken	hw->link.topology = SLI_LINK_TOPO_NONE;
252331766Sken	hw->link.medium = SLI_LINK_MEDIUM_MAX;
253331766Sken	hw->link.speed = 0;
254331766Sken	hw->link.loop_map = NULL;
255331766Sken	hw->link.fc_id = UINT32_MAX;
256331766Sken
257331766Sken	return OCS_HW_RTN_SUCCESS;
258331766Sken}
259331766Sken
260331766Sken/**
261331766Sken * @ingroup devInitShutdown
262331766Sken * @brief If this is physical port 0, then read the max dump size.
263331766Sken *
264331766Sken * @par Description
265331766Sken * Queries the FW for the maximum dump size
266331766Sken *
267331766Sken * @param hw Hardware context allocated by the caller.
268331766Sken *
269331766Sken * @return Returns 0 on success, or a non-zero value on failure.
270331766Sken */
271331766Skenstatic ocs_hw_rtn_e
272331766Skenocs_hw_read_max_dump_size(ocs_hw_t *hw)
273331766Sken{
274331766Sken	uint8_t	buf[SLI4_BMBX_SIZE];
275331766Sken	uint8_t bus, dev, func;
276331766Sken	int 	rc;
277331766Sken
278331766Sken	/* lancer only */
279331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
280331766Sken		ocs_log_debug(hw->os, "Function only supported for I/F type 2\n");
281331766Sken		return OCS_HW_RTN_ERROR;
282331766Sken	}
283331766Sken
284331766Sken	/*
285331766Sken	 * Make sure the FW is new enough to support this command. If the FW
286331766Sken	 * is too old, the FW will UE.
287331766Sken	 */
288331766Sken	if (hw->workaround.disable_dump_loc) {
289331766Sken		ocs_log_test(hw->os, "FW version is too old for this feature\n");
290331766Sken		return OCS_HW_RTN_ERROR;
291331766Sken	}
292331766Sken
293331766Sken	/* attempt to detemine the dump size for function 0 only. */
294331766Sken	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
295331766Sken	if (func == 0) {
296331766Sken		if (sli_cmd_common_set_dump_location(&hw->sli, buf,
297331766Sken							SLI4_BMBX_SIZE, 1, 0, NULL, 0)) {
298331766Sken			sli4_res_common_set_dump_location_t *rsp =
299331766Sken				(sli4_res_common_set_dump_location_t *)
300331766Sken				(buf + offsetof(sli4_cmd_sli_config_t,
301331766Sken						payload.embed));
302331766Sken
303331766Sken			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
304331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
305331766Sken				ocs_log_test(hw->os, "set dump location command failed\n");
306331766Sken				return rc;
307331766Sken			} else {
308331766Sken				hw->dump_size = rsp->buffer_length;
309331766Sken				ocs_log_debug(hw->os, "Dump size %x\n", rsp->buffer_length);
310331766Sken			}
311331766Sken		}
312331766Sken	}
313331766Sken	return OCS_HW_RTN_SUCCESS;
314331766Sken}
315331766Sken
316331766Sken/**
317331766Sken * @ingroup devInitShutdown
318331766Sken * @brief Set up the Hardware Abstraction Layer module.
319331766Sken *
320331766Sken * @par Description
321331766Sken * Calls set up to configure the hardware.
322331766Sken *
323331766Sken * @param hw Hardware context allocated by the caller.
324331766Sken * @param os Device abstraction.
325331766Sken * @param port_type Protocol type of port, such as FC and NIC.
326331766Sken *
327331766Sken * @todo Why is port_type a parameter?
328331766Sken *
329331766Sken * @return Returns 0 on success, or a non-zero value on failure.
330331766Sken */
331331766Skenocs_hw_rtn_e
332331766Skenocs_hw_setup(ocs_hw_t *hw, ocs_os_handle_t os, sli4_port_type_e port_type)
333331766Sken{
334331766Sken	uint32_t i;
335331766Sken	char prop_buf[32];
336331766Sken
337331766Sken	if (hw == NULL) {
338331766Sken		ocs_log_err(os, "bad parameter(s) hw=%p\n", hw);
339331766Sken		return OCS_HW_RTN_ERROR;
340331766Sken	}
341331766Sken
342331766Sken	if (hw->hw_setup_called) {
343331766Sken		/* Setup run-time workarounds.
344331766Sken		 * Call for each setup, to allow for hw_war_version
345331766Sken		 */
346331766Sken		ocs_hw_workaround_setup(hw);
347331766Sken		return OCS_HW_RTN_SUCCESS;
348331766Sken	}
349331766Sken
350331766Sken	/*
351331766Sken	 * ocs_hw_init() relies on NULL pointers indicating that a structure
352331766Sken	 * needs allocation. If a structure is non-NULL, ocs_hw_init() won't
353331766Sken	 * free/realloc that memory
354331766Sken	 */
355331766Sken	ocs_memset(hw, 0, sizeof(ocs_hw_t));
356331766Sken
357331766Sken	hw->hw_setup_called = TRUE;
358331766Sken
359331766Sken	hw->os = os;
360331766Sken
361331766Sken	ocs_lock_init(hw->os, &hw->cmd_lock, "HW_cmd_lock[%d]", ocs_instance(hw->os));
362331766Sken	ocs_list_init(&hw->cmd_head, ocs_command_ctx_t, link);
363331766Sken	ocs_list_init(&hw->cmd_pending, ocs_command_ctx_t, link);
364331766Sken	hw->cmd_head_count = 0;
365331766Sken
366331766Sken	ocs_lock_init(hw->os, &hw->io_lock, "HW_io_lock[%d]", ocs_instance(hw->os));
367331766Sken	ocs_lock_init(hw->os, &hw->io_abort_lock, "HW_io_abort_lock[%d]", ocs_instance(hw->os));
368331766Sken
369331766Sken	ocs_atomic_init(&hw->io_alloc_failed_count, 0);
370331766Sken
371331766Sken	hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
372331766Sken	hw->config.dif_seed = 0;
373331766Sken	hw->config.auto_xfer_rdy_blk_size_chip = OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT;
374331766Sken	hw->config.auto_xfer_rdy_ref_tag_is_lba = OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT;
375331766Sken	hw->config.auto_xfer_rdy_app_tag_valid =  OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT;
376331766Sken	hw->config.auto_xfer_rdy_app_tag_value = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT;
377331766Sken
378331766Sken
379331766Sken	if (sli_setup(&hw->sli, hw->os, port_type)) {
380331766Sken		ocs_log_err(hw->os, "SLI setup failed\n");
381331766Sken		return OCS_HW_RTN_ERROR;
382331766Sken	}
383331766Sken
384331766Sken	ocs_memset(hw->domains, 0, sizeof(hw->domains));
385331766Sken
386331766Sken	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
387331766Sken
388331766Sken	ocs_hw_link_event_init(hw);
389331766Sken
390331766Sken	sli_callback(&hw->sli, SLI4_CB_LINK, ocs_hw_cb_link, hw);
391331766Sken	sli_callback(&hw->sli, SLI4_CB_FIP, ocs_hw_cb_fip, hw);
392331766Sken
393331766Sken	/*
394331766Sken	 * Set all the queue sizes to the maximum allowed. These values may
395331766Sken	 * be changes later by the adjust and workaround functions.
396331766Sken	 */
397331766Sken	for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) {
398331766Sken		hw->num_qentries[i] = sli_get_max_qentries(&hw->sli, i);
399331766Sken	}
400331766Sken
401331766Sken	/*
402331766Sken	 * The RQ assignment for RQ pair mode.
403331766Sken	 */
404331766Sken	hw->config.rq_default_buffer_size = OCS_HW_RQ_SIZE_PAYLOAD;
405331766Sken	hw->config.n_io = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
406331766Sken	if (ocs_get_property("auto_xfer_rdy_xri_cnt", prop_buf, sizeof(prop_buf)) == 0) {
407331766Sken		hw->config.auto_xfer_rdy_xri_cnt = ocs_strtoul(prop_buf, 0, 0);
408331766Sken	}
409331766Sken
410331766Sken	/* by default, enable initiator-only auto-ABTS emulation */
411331766Sken	hw->config.i_only_aab = TRUE;
412331766Sken
413331766Sken	/* Setup run-time workarounds */
414331766Sken	ocs_hw_workaround_setup(hw);
415331766Sken
416331766Sken	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
417331766Sken	if (hw->workaround.override_fcfi) {
418331766Sken		hw->first_domain_idx = -1;
419331766Sken	}
420331766Sken
421331766Sken	/* Must be done after the workaround setup */
422331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
423331766Sken		(void)ocs_hw_read_max_dump_size(hw);
424331766Sken	}
425331766Sken
426331766Sken	/* calculate the number of WQs required. */
427331766Sken	ocs_hw_adjust_wqs(hw);
428331766Sken
429331766Sken	/* Set the default dif mode */
430331766Sken	if (! sli_is_dif_inline_capable(&hw->sli)) {
431331766Sken		ocs_log_test(hw->os, "not inline capable, setting mode to separate\n");
432331766Sken		hw->config.dif_mode = OCS_HW_DIF_MODE_SEPARATE;
433331766Sken	}
434331766Sken	/* Workaround: BZ 161832 */
435331766Sken	if (hw->workaround.use_dif_sec_xri) {
436331766Sken		ocs_list_init(&hw->sec_hio_wait_list, ocs_hw_io_t, link);
437331766Sken	}
438331766Sken
439331766Sken	/*
440331766Sken	 * Figure out the starting and max ULP to spread the WQs across the
441331766Sken	 * ULPs.
442331766Sken	 */
443331766Sken	if (sli_get_is_dual_ulp_capable(&hw->sli)) {
444331766Sken		if (sli_get_is_ulp_enabled(&hw->sli, 0) &&
445331766Sken		    sli_get_is_ulp_enabled(&hw->sli, 1)) {
446331766Sken			hw->ulp_start = 0;
447331766Sken			hw->ulp_max   = 1;
448331766Sken		} else if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
449331766Sken			hw->ulp_start = 0;
450331766Sken			hw->ulp_max   = 0;
451331766Sken		} else {
452331766Sken			hw->ulp_start = 1;
453331766Sken			hw->ulp_max   = 1;
454331766Sken		}
455331766Sken	} else {
456331766Sken		if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
457331766Sken			hw->ulp_start = 0;
458331766Sken			hw->ulp_max   = 0;
459331766Sken		} else {
460331766Sken			hw->ulp_start = 1;
461331766Sken			hw->ulp_max   = 1;
462331766Sken		}
463331766Sken	}
464331766Sken	ocs_log_debug(hw->os, "ulp_start %d, ulp_max %d\n",
465331766Sken		hw->ulp_start, hw->ulp_max);
466331766Sken	hw->config.queue_topology = hw_global.queue_topology_string;
467331766Sken
468331766Sken	hw->qtop = ocs_hw_qtop_parse(hw, hw->config.queue_topology);
469331766Sken
470331766Sken	hw->config.n_eq = hw->qtop->entry_counts[QTOP_EQ];
471331766Sken	hw->config.n_cq = hw->qtop->entry_counts[QTOP_CQ];
472331766Sken	hw->config.n_rq = hw->qtop->entry_counts[QTOP_RQ];
473331766Sken	hw->config.n_wq = hw->qtop->entry_counts[QTOP_WQ];
474331766Sken	hw->config.n_mq = hw->qtop->entry_counts[QTOP_MQ];
475331766Sken
476331766Sken	/* Verify qtop configuration against driver supported configuration */
477331766Sken	if (hw->config.n_rq > OCE_HW_MAX_NUM_MRQ_PAIRS) {
478331766Sken		ocs_log_crit(hw->os, "Max supported MRQ pairs = %d\n",
479331766Sken				OCE_HW_MAX_NUM_MRQ_PAIRS);
480331766Sken		return OCS_HW_RTN_ERROR;
481331766Sken	}
482331766Sken
483331766Sken	if (hw->config.n_eq > OCS_HW_MAX_NUM_EQ) {
484331766Sken		ocs_log_crit(hw->os, "Max supported EQs = %d\n",
485331766Sken				OCS_HW_MAX_NUM_EQ);
486331766Sken		return OCS_HW_RTN_ERROR;
487331766Sken	}
488331766Sken
489331766Sken	if (hw->config.n_cq > OCS_HW_MAX_NUM_CQ) {
490331766Sken		ocs_log_crit(hw->os, "Max supported CQs = %d\n",
491331766Sken				OCS_HW_MAX_NUM_CQ);
492331766Sken		return OCS_HW_RTN_ERROR;
493331766Sken	}
494331766Sken
495331766Sken	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
496331766Sken		ocs_log_crit(hw->os, "Max supported WQs = %d\n",
497331766Sken				OCS_HW_MAX_NUM_WQ);
498331766Sken		return OCS_HW_RTN_ERROR;
499331766Sken	}
500331766Sken
501331766Sken	if (hw->config.n_mq > OCS_HW_MAX_NUM_MQ) {
502331766Sken		ocs_log_crit(hw->os, "Max supported MQs = %d\n",
503331766Sken				OCS_HW_MAX_NUM_MQ);
504331766Sken		return OCS_HW_RTN_ERROR;
505331766Sken	}
506331766Sken
507331766Sken	return OCS_HW_RTN_SUCCESS;
508331766Sken}
509331766Sken
510331766Sken/**
511331766Sken * @ingroup devInitShutdown
512331766Sken * @brief Allocate memory structures to prepare for the device operation.
513331766Sken *
514331766Sken * @par Description
515331766Sken * Allocates memory structures needed by the device and prepares the device
516331766Sken * for operation.
517331766Sken * @n @n @b Note: This function may be called more than once (for example, at
518331766Sken * initialization and then after a reset), but the size of the internal resources
519331766Sken * may not be changed without tearing down the HW (ocs_hw_teardown()).
520331766Sken *
521331766Sken * @param hw Hardware context allocated by the caller.
522331766Sken *
523331766Sken * @return Returns 0 on success, or a non-zero value on failure.
524331766Sken */
525331766Skenocs_hw_rtn_e
526331766Skenocs_hw_init(ocs_hw_t *hw)
527331766Sken{
528331766Sken	ocs_hw_rtn_e	rc;
529331766Sken	uint32_t	i = 0;
530331766Sken	uint8_t		buf[SLI4_BMBX_SIZE];
531331766Sken	uint32_t	max_rpi;
532331766Sken	int		rem_count;
533331766Sken	int	        written_size = 0;
534331766Sken	uint32_t	count;
535331766Sken	char		prop_buf[32];
536331766Sken	uint32_t ramdisc_blocksize = 512;
537331766Sken	uint32_t q_count = 0;
538331766Sken	/*
539331766Sken	 * Make sure the command lists are empty. If this is start-of-day,
540331766Sken	 * they'll be empty since they were just initialized in ocs_hw_setup.
541331766Sken	 * If we've just gone through a reset, the command and command pending
542331766Sken	 * lists should have been cleaned up as part of the reset (ocs_hw_reset()).
543331766Sken	 */
544331766Sken	ocs_lock(&hw->cmd_lock);
545331766Sken		if (!ocs_list_empty(&hw->cmd_head)) {
546331766Sken			ocs_log_test(hw->os, "command found on cmd list\n");
547331766Sken			ocs_unlock(&hw->cmd_lock);
548331766Sken			return OCS_HW_RTN_ERROR;
549331766Sken		}
550331766Sken		if (!ocs_list_empty(&hw->cmd_pending)) {
551331766Sken			ocs_log_test(hw->os, "command found on pending list\n");
552331766Sken			ocs_unlock(&hw->cmd_lock);
553331766Sken			return OCS_HW_RTN_ERROR;
554331766Sken		}
555331766Sken	ocs_unlock(&hw->cmd_lock);
556331766Sken
557331766Sken	/* Free RQ buffers if prevously allocated */
558331766Sken	ocs_hw_rx_free(hw);
559331766Sken
560331766Sken	/*
561331766Sken	 * The IO queues must be initialized here for the reset case. The
562331766Sken	 * ocs_hw_init_io() function will re-add the IOs to the free list.
563331766Sken	 * The cmd_head list should be OK since we free all entries in
564331766Sken	 * ocs_hw_command_cancel() that is called in the ocs_hw_reset().
565331766Sken	 */
566331766Sken
567331766Sken	/* If we are in this function due to a reset, there may be stale items
568331766Sken	 * on lists that need to be removed.  Clean them up.
569331766Sken	 */
570331766Sken	rem_count=0;
571331766Sken	if (ocs_list_valid(&hw->io_wait_free)) {
572331766Sken		while ((!ocs_list_empty(&hw->io_wait_free))) {
573331766Sken			rem_count++;
574331766Sken			ocs_list_remove_head(&hw->io_wait_free);
575331766Sken		}
576331766Sken		if (rem_count > 0) {
577331766Sken			ocs_log_debug(hw->os, "removed %d items from io_wait_free list\n", rem_count);
578331766Sken		}
579331766Sken	}
580331766Sken	rem_count=0;
581331766Sken	if (ocs_list_valid(&hw->io_inuse)) {
582331766Sken		while ((!ocs_list_empty(&hw->io_inuse))) {
583331766Sken			rem_count++;
584331766Sken			ocs_list_remove_head(&hw->io_inuse);
585331766Sken		}
586331766Sken		if (rem_count > 0) {
587331766Sken			ocs_log_debug(hw->os, "removed %d items from io_inuse list\n", rem_count);
588331766Sken		}
589331766Sken	}
590331766Sken	rem_count=0;
591331766Sken	if (ocs_list_valid(&hw->io_free)) {
592331766Sken		while ((!ocs_list_empty(&hw->io_free))) {
593331766Sken			rem_count++;
594331766Sken			ocs_list_remove_head(&hw->io_free);
595331766Sken		}
596331766Sken		if (rem_count > 0) {
597331766Sken			ocs_log_debug(hw->os, "removed %d items from io_free list\n", rem_count);
598331766Sken		}
599331766Sken	}
600331766Sken	if (ocs_list_valid(&hw->io_port_owned)) {
601331766Sken		while ((!ocs_list_empty(&hw->io_port_owned))) {
602331766Sken			ocs_list_remove_head(&hw->io_port_owned);
603331766Sken		}
604331766Sken	}
605331766Sken	ocs_list_init(&hw->io_inuse, ocs_hw_io_t, link);
606331766Sken	ocs_list_init(&hw->io_free, ocs_hw_io_t, link);
607331766Sken	ocs_list_init(&hw->io_port_owned, ocs_hw_io_t, link);
608331766Sken	ocs_list_init(&hw->io_wait_free, ocs_hw_io_t, link);
609331766Sken	ocs_list_init(&hw->io_timed_wqe, ocs_hw_io_t, wqe_link);
610331766Sken	ocs_list_init(&hw->io_port_dnrx, ocs_hw_io_t, dnrx_link);
611331766Sken
612331766Sken	/* If MRQ not required, Make sure we dont request feature. */
613331766Sken	if (hw->config.n_rq == 1) {
614331766Sken		hw->sli.config.features.flag.mrqp = FALSE;
615331766Sken	}
616331766Sken
617331766Sken	if (sli_init(&hw->sli)) {
618331766Sken		ocs_log_err(hw->os, "SLI failed to initialize\n");
619331766Sken		return OCS_HW_RTN_ERROR;
620331766Sken	}
621331766Sken
622331766Sken	/*
623331766Sken	 * Enable the auto xfer rdy feature if requested.
624331766Sken	 */
625331766Sken	hw->auto_xfer_rdy_enabled = FALSE;
626331766Sken	if (sli_get_auto_xfer_rdy_capable(&hw->sli) &&
627331766Sken	    hw->config.auto_xfer_rdy_size > 0) {
628331766Sken		if (hw->config.esoc){
629331766Sken			if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
630331766Sken				ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
631331766Sken			}
632331766Sken			written_size = sli_cmd_config_auto_xfer_rdy_hp(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size, 1, ramdisc_blocksize);
633331766Sken		} else {
634331766Sken			written_size = sli_cmd_config_auto_xfer_rdy(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size);
635331766Sken		}
636331766Sken		if (written_size) {
637331766Sken			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
638331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
639331766Sken				ocs_log_err(hw->os, "config auto xfer rdy failed\n");
640331766Sken				return rc;
641331766Sken			}
642331766Sken		}
643331766Sken		hw->auto_xfer_rdy_enabled = TRUE;
644331766Sken
645331766Sken		if (hw->config.auto_xfer_rdy_t10_enable) {
646331766Sken			rc = ocs_hw_config_auto_xfer_rdy_t10pi(hw, buf);
647331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
648331766Sken				ocs_log_err(hw->os, "set parameters auto xfer rdy T10 PI failed\n");
649331766Sken				return rc;
650331766Sken			}
651331766Sken		}
652331766Sken	}
653331766Sken
654331766Sken	if(hw->sliport_healthcheck) {
655331766Sken		rc = ocs_hw_config_sli_port_health_check(hw, 0, 1);
656331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
657331766Sken			ocs_log_err(hw->os, "Enabling Sliport Health check failed \n");
658331766Sken			return rc;
659331766Sken		}
660331766Sken	}
661331766Sken
662331766Sken	/*
663331766Sken	 * Set FDT transfer hint, only works on Lancer
664331766Sken	 */
665331766Sken	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) && (OCS_HW_FDT_XFER_HINT != 0)) {
666331766Sken		/*
667331766Sken		 * Non-fatal error. In particular, we can disregard failure to set OCS_HW_FDT_XFER_HINT on
668331766Sken		 * devices with legacy firmware that do not support OCS_HW_FDT_XFER_HINT feature.
669331766Sken		 */
670331766Sken		ocs_hw_config_set_fdt_xfer_hint(hw, OCS_HW_FDT_XFER_HINT);
671331766Sken	}
672331766Sken
673331766Sken	/*
674331766Sken	 * Verify that we have not exceeded any queue sizes
675331766Sken	 */
676331766Sken	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_EQ),
677331766Sken					OCS_HW_MAX_NUM_EQ);
678331766Sken	if (hw->config.n_eq > q_count) {
679331766Sken		ocs_log_err(hw->os, "requested %d EQ but %d allowed\n",
680331766Sken			    hw->config.n_eq, q_count);
681331766Sken		return OCS_HW_RTN_ERROR;
682331766Sken	}
683331766Sken
684331766Sken	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_CQ),
685331766Sken					OCS_HW_MAX_NUM_CQ);
686331766Sken	if (hw->config.n_cq > q_count) {
687331766Sken		ocs_log_err(hw->os, "requested %d CQ but %d allowed\n",
688331766Sken			    hw->config.n_cq, q_count);
689331766Sken		return OCS_HW_RTN_ERROR;
690331766Sken	}
691331766Sken
692331766Sken	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_MQ),
693331766Sken					OCS_HW_MAX_NUM_MQ);
694331766Sken	if (hw->config.n_mq > q_count) {
695331766Sken		ocs_log_err(hw->os, "requested %d MQ but %d allowed\n",
696331766Sken			    hw->config.n_mq, q_count);
697331766Sken		return OCS_HW_RTN_ERROR;
698331766Sken	}
699331766Sken
700331766Sken	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_RQ),
701331766Sken					OCS_HW_MAX_NUM_RQ);
702331766Sken	if (hw->config.n_rq > q_count) {
703331766Sken		ocs_log_err(hw->os, "requested %d RQ but %d allowed\n",
704331766Sken			    hw->config.n_rq, q_count);
705331766Sken		return OCS_HW_RTN_ERROR;
706331766Sken	}
707331766Sken
708331766Sken	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ),
709331766Sken					OCS_HW_MAX_NUM_WQ);
710331766Sken	if (hw->config.n_wq > q_count) {
711331766Sken		ocs_log_err(hw->os, "requested %d WQ but %d allowed\n",
712331766Sken			    hw->config.n_wq, q_count);
713331766Sken		return OCS_HW_RTN_ERROR;
714331766Sken	}
715331766Sken
716331766Sken	/* zero the hashes */
717331766Sken	ocs_memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
718331766Sken	ocs_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
719331766Sken			OCS_HW_MAX_NUM_CQ, OCS_HW_Q_HASH_SIZE);
720331766Sken
721331766Sken	ocs_memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
722331766Sken	ocs_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
723331766Sken			OCS_HW_MAX_NUM_RQ, OCS_HW_Q_HASH_SIZE);
724331766Sken
725331766Sken	ocs_memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
726331766Sken	ocs_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
727331766Sken			OCS_HW_MAX_NUM_WQ, OCS_HW_Q_HASH_SIZE);
728331766Sken
729331766Sken
730331766Sken	rc = ocs_hw_init_queues(hw, hw->qtop);
731331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
732331766Sken		return rc;
733331766Sken	}
734331766Sken
735331766Sken	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
736331766Sken	i = sli_fc_get_rpi_requirements(&hw->sli, max_rpi);
737331766Sken	if (i) {
738331766Sken		ocs_dma_t payload_memory;
739331766Sken
740331766Sken		rc = OCS_HW_RTN_ERROR;
741331766Sken
742331766Sken		if (hw->rnode_mem.size) {
743331766Sken			ocs_dma_free(hw->os, &hw->rnode_mem);
744331766Sken		}
745331766Sken
746331766Sken		if (ocs_dma_alloc(hw->os, &hw->rnode_mem, i, 4096)) {
747331766Sken			ocs_log_err(hw->os, "remote node memory allocation fail\n");
748331766Sken			return OCS_HW_RTN_NO_MEMORY;
749331766Sken		}
750331766Sken
751331766Sken		payload_memory.size = 0;
752331766Sken		if (sli_cmd_fcoe_post_hdr_templates(&hw->sli, buf, SLI4_BMBX_SIZE,
753331766Sken					&hw->rnode_mem, UINT16_MAX, &payload_memory)) {
754331766Sken			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
755331766Sken
756331766Sken			if (payload_memory.size != 0) {
757331766Sken				/* The command was non-embedded - need to free the dma buffer */
758331766Sken				ocs_dma_free(hw->os, &payload_memory);
759331766Sken			}
760331766Sken		}
761331766Sken
762331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
763331766Sken			ocs_log_err(hw->os, "header template registration failed\n");
764331766Sken			return rc;
765331766Sken		}
766331766Sken	}
767331766Sken
768331766Sken	/* Allocate and post RQ buffers */
769331766Sken	rc = ocs_hw_rx_allocate(hw);
770331766Sken	if (rc) {
771331766Sken		ocs_log_err(hw->os, "rx_allocate failed\n");
772331766Sken		return rc;
773331766Sken	}
774331766Sken
775331766Sken	/* Populate hw->seq_free_list */
776331766Sken	if (hw->seq_pool == NULL) {
777331766Sken		uint32_t count = 0;
778331766Sken		uint32_t i;
779331766Sken
780331766Sken		/* Sum up the total number of RQ entries, to use to allocate the sequence object pool */
781331766Sken		for (i = 0; i < hw->hw_rq_count; i++) {
782331766Sken			count += hw->hw_rq[i]->entry_count;
783331766Sken		}
784331766Sken
785331766Sken		hw->seq_pool = ocs_array_alloc(hw->os, sizeof(ocs_hw_sequence_t), count);
786331766Sken		if (hw->seq_pool == NULL) {
787331766Sken			ocs_log_err(hw->os, "malloc seq_pool failed\n");
788331766Sken			return OCS_HW_RTN_NO_MEMORY;
789331766Sken		}
790331766Sken	}
791331766Sken
792331766Sken	if(ocs_hw_rx_post(hw)) {
793331766Sken		ocs_log_err(hw->os, "WARNING - error posting RQ buffers\n");
794331766Sken	}
795331766Sken
796331766Sken	/* Allocate rpi_ref if not previously allocated */
797331766Sken	if (hw->rpi_ref == NULL) {
798331766Sken		hw->rpi_ref = ocs_malloc(hw->os, max_rpi * sizeof(*hw->rpi_ref),
799331766Sken					  OCS_M_ZERO | OCS_M_NOWAIT);
800331766Sken		if (hw->rpi_ref == NULL) {
801331766Sken			ocs_log_err(hw->os, "rpi_ref allocation failure (%d)\n", i);
802331766Sken			return OCS_HW_RTN_NO_MEMORY;
803331766Sken		}
804331766Sken	}
805331766Sken
806331766Sken	for (i = 0; i < max_rpi; i ++) {
807331766Sken		ocs_atomic_init(&hw->rpi_ref[i].rpi_count, 0);
808331766Sken		ocs_atomic_init(&hw->rpi_ref[i].rpi_attached, 0);
809331766Sken	}
810331766Sken
811331766Sken	ocs_memset(hw->domains, 0, sizeof(hw->domains));
812331766Sken
813331766Sken	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
814331766Sken	if (hw->workaround.override_fcfi) {
815331766Sken		hw->first_domain_idx = -1;
816331766Sken	}
817331766Sken
818331766Sken	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
819331766Sken
820331766Sken	/* Register a FCFI to allow unsolicited frames to be routed to the driver */
821331766Sken	if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
822331766Sken
823331766Sken		if (hw->hw_mrq_count) {
824331766Sken			ocs_log_debug(hw->os, "using REG_FCFI MRQ\n");
825331766Sken
826331766Sken			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0, 0);
827331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
828331766Sken				ocs_log_err(hw->os, "REG_FCFI_MRQ FCFI registration failed\n");
829331766Sken				return rc;
830331766Sken			}
831331766Sken
832331766Sken			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0, 0);
833331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
834331766Sken				ocs_log_err(hw->os, "REG_FCFI_MRQ MRQ registration failed\n");
835331766Sken				return rc;
836331766Sken			}
837331766Sken		} else {
838331766Sken			sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
839331766Sken
840331766Sken			ocs_log_debug(hw->os, "using REG_FCFI standard\n");
841331766Sken
842331766Sken			/* Set the filter match/mask values from hw's filter_def values */
843331766Sken			for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
844331766Sken				rq_cfg[i].rq_id = 0xffff;
845331766Sken				rq_cfg[i].r_ctl_mask =	(uint8_t)  hw->config.filter_def[i];
846331766Sken				rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
847331766Sken				rq_cfg[i].type_mask =	(uint8_t) (hw->config.filter_def[i] >> 16);
848331766Sken				rq_cfg[i].type_match =	(uint8_t) (hw->config.filter_def[i] >> 24);
849331766Sken			}
850331766Sken
851331766Sken			/*
852331766Sken			 * Update the rq_id's of the FCF configuration (don't update more than the number
853331766Sken			 * of rq_cfg elements)
854331766Sken			 */
855331766Sken			for (i = 0; i < OCS_MIN(hw->hw_rq_count, SLI4_CMD_REG_FCFI_NUM_RQ_CFG); i++) {
856331766Sken				hw_rq_t *rq = hw->hw_rq[i];
857331766Sken				uint32_t j;
858331766Sken				for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
859331766Sken					uint32_t mask = (rq->filter_mask != 0) ? rq->filter_mask : 1;
860331766Sken					if (mask & (1U << j)) {
861331766Sken						rq_cfg[j].rq_id = rq->hdr->id;
862331766Sken						ocs_log_debug(hw->os, "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
863331766Sken							j, hw->config.filter_def[j], i, rq->hdr->id);
864331766Sken					}
865331766Sken				}
866331766Sken			}
867331766Sken
868331766Sken			rc = OCS_HW_RTN_ERROR;
869331766Sken
870331766Sken			if (sli_cmd_reg_fcfi(&hw->sli, buf, SLI4_BMBX_SIZE, 0, rq_cfg, 0)) {
871331766Sken				rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
872331766Sken			}
873331766Sken
874331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
875331766Sken				ocs_log_err(hw->os, "FCFI registration failed\n");
876331766Sken				return rc;
877331766Sken			}
878331766Sken			hw->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)buf)->fcfi;
879331766Sken		}
880331766Sken
881331766Sken	}
882331766Sken
883331766Sken	/*
884331766Sken	 * Allocate the WQ request tag pool, if not previously allocated (the request tag value is 16 bits,
885331766Sken	 * thus the pool allocation size of 64k)
886331766Sken	 */
887331766Sken	rc = ocs_hw_reqtag_init(hw);
888331766Sken	if (rc) {
889331766Sken		ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed: %d\n", rc);
890331766Sken		return rc;
891331766Sken	}
892331766Sken
893331766Sken	rc = ocs_hw_setup_io(hw);
894331766Sken	if (rc) {
895331766Sken		ocs_log_err(hw->os, "IO allocation failure\n");
896331766Sken		return rc;
897331766Sken	}
898331766Sken
899331766Sken	rc = ocs_hw_init_io(hw);
900331766Sken	if (rc) {
901331766Sken		ocs_log_err(hw->os, "IO initialization failure\n");
902331766Sken		return rc;
903331766Sken	}
904331766Sken
905331766Sken	ocs_queue_history_init(hw->os, &hw->q_hist);
906331766Sken
907331766Sken	/* get hw link config; polling, so callback will be called immediately */
908331766Sken	hw->linkcfg = OCS_HW_LINKCFG_NA;
909331766Sken	ocs_hw_get_linkcfg(hw, OCS_CMD_POLL, ocs_hw_init_linkcfg_cb, hw);
910331766Sken
911331766Sken	/* if lancer ethernet, ethernet ports need to be enabled */
912331766Sken	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) &&
913331766Sken	    (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_ETHERNET)) {
914331766Sken		if (ocs_hw_set_eth_license(hw, hw->eth_license)) {
915331766Sken			/* log warning but continue */
916331766Sken			ocs_log_err(hw->os, "Failed to set ethernet license\n");
917331766Sken		}
918331766Sken	}
919331766Sken
920331766Sken	/* Set the DIF seed - only for lancer right now */
921331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli) &&
922331766Sken	    ocs_hw_set_dif_seed(hw) != OCS_HW_RTN_SUCCESS) {
923331766Sken		ocs_log_err(hw->os, "Failed to set DIF seed value\n");
924331766Sken		return rc;
925331766Sken	}
926331766Sken
927331766Sken	/* Set the DIF mode - skyhawk only */
928331766Sken	if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli) &&
929331766Sken	    sli_get_dif_capable(&hw->sli)) {
930331766Sken		rc = ocs_hw_set_dif_mode(hw);
931331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
932331766Sken			ocs_log_err(hw->os, "Failed to set DIF mode value\n");
933331766Sken			return rc;
934331766Sken		}
935331766Sken	}
936331766Sken
937331766Sken	/*
938331766Sken	 * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ entries
939331766Sken	 */
940331766Sken	for (i = 0; i < hw->eq_count; i++) {
941331766Sken		sli_queue_arm(&hw->sli, &hw->eq[i], TRUE);
942331766Sken	}
943331766Sken
944331766Sken	/*
945331766Sken	 * Initialize RQ hash
946331766Sken	 */
947331766Sken	for (i = 0; i < hw->rq_count; i++) {
948331766Sken		ocs_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
949331766Sken	}
950331766Sken
951331766Sken	/*
952331766Sken	 * Initialize WQ hash
953331766Sken	 */
954331766Sken	for (i = 0; i < hw->wq_count; i++) {
955331766Sken		ocs_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
956331766Sken	}
957331766Sken
958331766Sken	/*
959331766Sken	 * Arming the CQ allows (e.g.) MQ completions to write CQ entries
960331766Sken	 */
961331766Sken	for (i = 0; i < hw->cq_count; i++) {
962331766Sken		ocs_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
963331766Sken		sli_queue_arm(&hw->sli, &hw->cq[i], TRUE);
964331766Sken	}
965331766Sken
966331766Sken	/* record the fact that the queues are functional */
967331766Sken	hw->state = OCS_HW_STATE_ACTIVE;
968331766Sken
969331766Sken	/* Note: Must be after the IOs are setup and the state is active*/
970331766Sken	if (ocs_hw_rqpair_init(hw)) {
971331766Sken		ocs_log_err(hw->os, "WARNING - error initializing RQ pair\n");
972331766Sken	}
973331766Sken
974331766Sken	/* finally kick off periodic timer to check for timed out target WQEs */
975331766Sken	if (hw->config.emulate_tgt_wqe_timeout) {
976331766Sken		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw,
977331766Sken				OCS_HW_WQ_TIMER_PERIOD_MS);
978331766Sken	}
979331766Sken
980331766Sken	/*
981331766Sken	 * Allocate a HW IOs for send frame.  Allocate one for each Class 1 WQ, or if there
982331766Sken	 * are none of those, allocate one for WQ[0]
983331766Sken	 */
984331766Sken	if ((count = ocs_varray_get_count(hw->wq_class_array[1])) > 0) {
985331766Sken		for (i = 0; i < count; i++) {
986331766Sken			hw_wq_t *wq = ocs_varray_iter_next(hw->wq_class_array[1]);
987331766Sken			wq->send_frame_io = ocs_hw_io_alloc(hw);
988331766Sken			if (wq->send_frame_io == NULL) {
989331766Sken				ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
990331766Sken			}
991331766Sken		}
992331766Sken	} else {
993331766Sken		hw->hw_wq[0]->send_frame_io = ocs_hw_io_alloc(hw);
994331766Sken		if (hw->hw_wq[0]->send_frame_io == NULL) {
995331766Sken			ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
996331766Sken		}
997331766Sken	}
998331766Sken
999331766Sken	/* Initialize send frame frame sequence id */
1000331766Sken	ocs_atomic_init(&hw->send_frame_seq_id, 0);
1001331766Sken
1002331766Sken	/* Initialize watchdog timer if enabled by user */
1003331766Sken	hw->expiration_logged = 0;
1004331766Sken	if(hw->watchdog_timeout) {
1005331766Sken		if((hw->watchdog_timeout < 1) || (hw->watchdog_timeout > 65534)) {
1006331766Sken			ocs_log_err(hw->os, "watchdog_timeout out of range: Valid range is 1 - 65534\n");
1007331766Sken		}else if(!ocs_hw_config_watchdog_timer(hw)) {
1008331766Sken			ocs_log_info(hw->os, "watchdog timer configured with timeout = %d seconds \n", hw->watchdog_timeout);
1009331766Sken		}
1010331766Sken	}
1011331766Sken
1012331766Sken	if (ocs_dma_alloc(hw->os, &hw->domain_dmem, 112, 4)) {
1013331766Sken	   ocs_log_err(hw->os, "domain node memory allocation fail\n");
1014331766Sken	   return OCS_HW_RTN_NO_MEMORY;
1015331766Sken	}
1016331766Sken
1017331766Sken	if (ocs_dma_alloc(hw->os, &hw->fcf_dmem, OCS_HW_READ_FCF_SIZE, OCS_HW_READ_FCF_SIZE)) {
1018331766Sken	   ocs_log_err(hw->os, "domain fcf memory allocation fail\n");
1019331766Sken	   return OCS_HW_RTN_NO_MEMORY;
1020331766Sken	}
1021331766Sken
1022331766Sken	if ((0 == hw->loop_map.size) &&	ocs_dma_alloc(hw->os, &hw->loop_map,
1023331766Sken				SLI4_MIN_LOOP_MAP_BYTES, 4)) {
1024331766Sken		ocs_log_err(hw->os, "Loop dma alloc failed size:%d \n", hw->loop_map.size);
1025331766Sken	}
1026331766Sken
1027331766Sken	return OCS_HW_RTN_SUCCESS;
1028331766Sken}
1029331766Sken
1030331766Sken/**
1031331766Sken * @brief Configure Multi-RQ
1032331766Sken *
1033331766Sken * @param hw	Hardware context allocated by the caller.
1034331766Sken * @param mode	1 to set MRQ filters and 0 to set FCFI index
1035331766Sken * @param vlanid    valid in mode 0
1036331766Sken * @param fcf_index valid in mode 0
1037331766Sken *
1038331766Sken * @return Returns 0 on success, or a non-zero value on failure.
1039331766Sken */
1040331766Skenstatic int32_t
1041331766Skenocs_hw_config_mrq(ocs_hw_t *hw, uint8_t mode, uint16_t vlanid, uint16_t fcf_index)
1042331766Sken{
1043331766Sken	uint8_t buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
1044331766Sken	hw_rq_t *rq;
1045331766Sken	sli4_cmd_reg_fcfi_mrq_t *rsp = NULL;
1046331766Sken	uint32_t i, j;
1047331766Sken	sli4_cmd_rq_cfg_t rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
1048331766Sken	int32_t rc;
1049331766Sken
1050331766Sken	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1051331766Sken		goto issue_cmd;
1052331766Sken	}
1053331766Sken
1054331766Sken	/* Set the filter match/mask values from hw's filter_def values */
1055331766Sken	for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
1056331766Sken		rq_filter[i].rq_id = 0xffff;
1057331766Sken		rq_filter[i].r_ctl_mask  = (uint8_t)  hw->config.filter_def[i];
1058331766Sken		rq_filter[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
1059331766Sken		rq_filter[i].type_mask   = (uint8_t) (hw->config.filter_def[i] >> 16);
1060331766Sken		rq_filter[i].type_match  = (uint8_t) (hw->config.filter_def[i] >> 24);
1061331766Sken	}
1062331766Sken
1063331766Sken	/* Accumulate counts for each filter type used, build rq_ids[] list */
1064331766Sken	for (i = 0; i < hw->hw_rq_count; i++) {
1065331766Sken		rq = hw->hw_rq[i];
1066331766Sken		for (j = 0; j < SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG; j++) {
1067331766Sken			if (rq->filter_mask & (1U << j)) {
1068331766Sken				if (rq_filter[j].rq_id != 0xffff) {
1069331766Sken					/* Already used. Bailout ifts not RQset case */
1070331766Sken					if (!rq->is_mrq || (rq_filter[j].rq_id != rq->base_mrq_id)) {
1071331766Sken						ocs_log_err(hw->os, "Wrong queue topology.\n");
1072331766Sken						return OCS_HW_RTN_ERROR;
1073331766Sken					}
1074331766Sken					continue;
1075331766Sken				}
1076331766Sken
1077331766Sken				if (rq->is_mrq) {
1078331766Sken					rq_filter[j].rq_id = rq->base_mrq_id;
1079331766Sken					mrq_bitmask |= (1U << j);
1080331766Sken				} else {
1081331766Sken					rq_filter[j].rq_id = rq->hdr->id;
1082331766Sken				}
1083331766Sken			}
1084331766Sken		}
1085331766Sken	}
1086331766Sken
1087331766Skenissue_cmd:
1088331766Sken	/* Invoke REG_FCFI_MRQ */
1089331766Sken	rc = sli_cmd_reg_fcfi_mrq(&hw->sli,
1090331766Sken				 buf,					/* buf */
1091331766Sken				 SLI4_BMBX_SIZE,			/* size */
1092331766Sken				 mode,					/* mode 1 */
1093331766Sken				 fcf_index,				/* fcf_index */
1094331766Sken				 vlanid,				/* vlan_id */
1095331766Sken				 hw->config.rq_selection_policy,	/* RQ selection policy*/
1096331766Sken				 mrq_bitmask,				/* MRQ bitmask */
1097331766Sken				 hw->hw_mrq_count,			/* num_mrqs */
1098331766Sken				 rq_filter);				/* RQ filter */
1099331766Sken	if (rc == 0) {
1100331766Sken		ocs_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed: %d\n", rc);
1101331766Sken		return OCS_HW_RTN_ERROR;
1102331766Sken	}
1103331766Sken
1104331766Sken	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
1105331766Sken
1106331766Sken	rsp = (sli4_cmd_reg_fcfi_mrq_t *)buf;
1107331766Sken
1108331766Sken	if ((rc != OCS_HW_RTN_SUCCESS) || (rsp->hdr.status)) {
1109331766Sken		ocs_log_err(hw->os, "FCFI MRQ registration failed. cmd = %x status = %x\n",
1110331766Sken			    rsp->hdr.command, rsp->hdr.status);
1111331766Sken		return OCS_HW_RTN_ERROR;
1112331766Sken	}
1113331766Sken
1114331766Sken	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1115331766Sken		hw->fcf_indicator = rsp->fcfi;
1116331766Sken	}
1117331766Sken	return 0;
1118331766Sken}
1119331766Sken
1120331766Sken/**
1121331766Sken * @brief Callback function for getting linkcfg during HW initialization.
1122331766Sken *
1123331766Sken * @param status Status of the linkcfg get operation.
1124331766Sken * @param value Link configuration enum to which the link configuration is set.
1125331766Sken * @param arg Callback argument (ocs_hw_t *).
1126331766Sken *
1127331766Sken * @return None.
1128331766Sken */
1129331766Skenstatic void
1130331766Skenocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
1131331766Sken{
1132331766Sken	ocs_hw_t *hw = (ocs_hw_t *)arg;
1133331766Sken	if (status == 0) {
1134331766Sken		hw->linkcfg = (ocs_hw_linkcfg_e)value;
1135331766Sken	} else {
1136331766Sken		hw->linkcfg = OCS_HW_LINKCFG_NA;
1137331766Sken	}
1138331766Sken	ocs_log_debug(hw->os, "linkcfg=%d\n", hw->linkcfg);
1139331766Sken}
1140331766Sken
1141331766Sken/**
1142331766Sken * @ingroup devInitShutdown
1143331766Sken * @brief Tear down the Hardware Abstraction Layer module.
1144331766Sken *
1145331766Sken * @par Description
1146331766Sken * Frees memory structures needed by the device, and shuts down the device. Does
1147331766Sken * not free the HW context memory (which is done by the caller).
1148331766Sken *
1149331766Sken * @param hw Hardware context allocated by the caller.
1150331766Sken *
1151331766Sken * @return Returns 0 on success, or a non-zero value on failure.
1152331766Sken */
1153331766Skenocs_hw_rtn_e
1154331766Skenocs_hw_teardown(ocs_hw_t *hw)
1155331766Sken{
1156331766Sken	uint32_t	i = 0;
1157331766Sken	uint32_t	iters = 10;/*XXX*/
1158331766Sken	uint32_t	max_rpi;
1159331766Sken	uint32_t destroy_queues;
1160331766Sken	uint32_t free_memory;
1161331766Sken
1162331766Sken	if (!hw) {
1163331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p\n", hw);
1164331766Sken		return OCS_HW_RTN_ERROR;
1165331766Sken	}
1166331766Sken
1167331766Sken	destroy_queues = (hw->state == OCS_HW_STATE_ACTIVE);
1168331766Sken	free_memory = (hw->state != OCS_HW_STATE_UNINITIALIZED);
1169331766Sken
1170331766Sken	/* shutdown target wqe timer */
1171331766Sken	shutdown_target_wqe_timer(hw);
1172331766Sken
1173331766Sken	/* Cancel watchdog timer if enabled */
1174331766Sken	if(hw->watchdog_timeout) {
1175331766Sken		hw->watchdog_timeout = 0;
1176331766Sken		ocs_hw_config_watchdog_timer(hw);
1177331766Sken	}
1178331766Sken
1179331766Sken	/* Cancel Sliport Healthcheck */
1180331766Sken	if(hw->sliport_healthcheck) {
1181331766Sken		hw->sliport_healthcheck = 0;
1182331766Sken		ocs_hw_config_sli_port_health_check(hw, 0, 0);
1183331766Sken	}
1184331766Sken
1185331766Sken	if (hw->state != OCS_HW_STATE_QUEUES_ALLOCATED) {
1186331766Sken
1187331766Sken		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1188331766Sken
1189331766Sken		ocs_hw_flush(hw);
1190331766Sken
1191331766Sken		/* If there are outstanding commands, wait for them to complete */
1192331766Sken		while (!ocs_list_empty(&hw->cmd_head) && iters) {
1193331766Sken			ocs_udelay(10000);
1194331766Sken			ocs_hw_flush(hw);
1195331766Sken			iters--;
1196331766Sken		}
1197331766Sken
1198331766Sken		if (ocs_list_empty(&hw->cmd_head)) {
1199331766Sken			ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1200331766Sken		} else {
1201331766Sken			ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1202331766Sken		}
1203331766Sken
1204331766Sken		/* Cancel any remaining commands */
1205331766Sken		ocs_hw_command_cancel(hw);
1206331766Sken	} else {
1207331766Sken		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1208331766Sken	}
1209331766Sken
1210331766Sken	ocs_lock_free(&hw->cmd_lock);
1211331766Sken
1212331766Sken	/* Free unregistered RPI if workaround is in force */
1213331766Sken	if (hw->workaround.use_unregistered_rpi) {
1214331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, hw->workaround.unregistered_rid);
1215331766Sken	}
1216331766Sken
1217331766Sken	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1218331766Sken	if (hw->rpi_ref) {
1219331766Sken		for (i = 0; i < max_rpi; i++) {
1220331766Sken			if (ocs_atomic_read(&hw->rpi_ref[i].rpi_count)) {
1221331766Sken				ocs_log_debug(hw->os, "non-zero ref [%d]=%d\n",
1222331766Sken						i, ocs_atomic_read(&hw->rpi_ref[i].rpi_count));
1223331766Sken			}
1224331766Sken		}
1225331766Sken		ocs_free(hw->os, hw->rpi_ref, max_rpi * sizeof(*hw->rpi_ref));
1226331766Sken		hw->rpi_ref = NULL;
1227331766Sken	}
1228331766Sken
1229331766Sken	ocs_dma_free(hw->os, &hw->rnode_mem);
1230331766Sken
1231331766Sken	if (hw->io) {
1232331766Sken		for (i = 0; i < hw->config.n_io; i++) {
1233331766Sken			if (hw->io[i] && (hw->io[i]->sgl != NULL) &&
1234331766Sken			    (hw->io[i]->sgl->virt != NULL)) {
1235331766Sken				if(hw->io[i]->is_port_owned) {
1236331766Sken					ocs_lock_free(&hw->io[i]->axr_lock);
1237331766Sken				}
1238331766Sken				ocs_dma_free(hw->os, hw->io[i]->sgl);
1239331766Sken			}
1240331766Sken			ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
1241331766Sken			hw->io[i] = NULL;
1242331766Sken		}
1243331766Sken		ocs_free(hw->os, hw->wqe_buffs, hw->config.n_io * hw->sli.config.wqe_size);
1244331766Sken		hw->wqe_buffs = NULL;
1245331766Sken		ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t *));
1246331766Sken		hw->io = NULL;
1247331766Sken	}
1248331766Sken
1249331766Sken	ocs_dma_free(hw->os, &hw->xfer_rdy);
1250331766Sken	ocs_dma_free(hw->os, &hw->dump_sges);
1251331766Sken	ocs_dma_free(hw->os, &hw->loop_map);
1252331766Sken
1253331766Sken	ocs_lock_free(&hw->io_lock);
1254331766Sken	ocs_lock_free(&hw->io_abort_lock);
1255331766Sken
1256331766Sken
1257331766Sken	for (i = 0; i < hw->wq_count; i++) {
1258331766Sken		sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues, free_memory);
1259331766Sken	}
1260331766Sken
1261331766Sken
1262331766Sken	for (i = 0; i < hw->rq_count; i++) {
1263331766Sken		sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, free_memory);
1264331766Sken	}
1265331766Sken
1266331766Sken	for (i = 0; i < hw->mq_count; i++) {
1267331766Sken		sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, free_memory);
1268331766Sken	}
1269331766Sken
1270331766Sken	for (i = 0; i < hw->cq_count; i++) {
1271331766Sken		sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, free_memory);
1272331766Sken	}
1273331766Sken
1274331766Sken	for (i = 0; i < hw->eq_count; i++) {
1275331766Sken		sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, free_memory);
1276331766Sken	}
1277331766Sken
1278331766Sken	ocs_hw_qtop_free(hw->qtop);
1279331766Sken
1280331766Sken	/* Free rq buffers */
1281331766Sken	ocs_hw_rx_free(hw);
1282331766Sken
1283331766Sken	hw_queue_teardown(hw);
1284331766Sken
1285331766Sken	ocs_hw_rqpair_teardown(hw);
1286331766Sken
1287331766Sken	if (sli_teardown(&hw->sli)) {
1288331766Sken		ocs_log_err(hw->os, "SLI teardown failed\n");
1289331766Sken	}
1290331766Sken
1291331766Sken	ocs_queue_history_free(&hw->q_hist);
1292331766Sken
1293331766Sken	/* record the fact that the queues are non-functional */
1294331766Sken	hw->state = OCS_HW_STATE_UNINITIALIZED;
1295331766Sken
1296331766Sken	/* free sequence free pool */
1297331766Sken	ocs_array_free(hw->seq_pool);
1298331766Sken	hw->seq_pool = NULL;
1299331766Sken
1300331766Sken	/* free hw_wq_callback pool */
1301331766Sken	ocs_pool_free(hw->wq_reqtag_pool);
1302331766Sken
1303331766Sken	ocs_dma_free(hw->os, &hw->domain_dmem);
1304331766Sken	ocs_dma_free(hw->os, &hw->fcf_dmem);
1305331766Sken	/* Mark HW setup as not having been called */
1306331766Sken	hw->hw_setup_called = FALSE;
1307331766Sken
1308331766Sken	return OCS_HW_RTN_SUCCESS;
1309331766Sken}
1310331766Sken
1311331766Skenocs_hw_rtn_e
1312331766Skenocs_hw_reset(ocs_hw_t *hw, ocs_hw_reset_e reset)
1313331766Sken{
1314331766Sken	uint32_t	i;
1315331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1316331766Sken	uint32_t	iters;
1317331766Sken	ocs_hw_state_e prev_state = hw->state;
1318331766Sken
1319331766Sken	if (hw->state != OCS_HW_STATE_ACTIVE) {
1320331766Sken		ocs_log_test(hw->os, "HW state %d is not active\n", hw->state);
1321331766Sken	}
1322331766Sken
1323331766Sken	hw->state = OCS_HW_STATE_RESET_IN_PROGRESS;
1324331766Sken
1325331766Sken	/* shutdown target wqe timer */
1326331766Sken	shutdown_target_wqe_timer(hw);
1327331766Sken
1328331766Sken	ocs_hw_flush(hw);
1329331766Sken
1330331766Sken	/*
1331331766Sken	 * If an mailbox command requiring a DMA is outstanding (i.e. SFP/DDM),
1332331766Sken	 * then the FW will UE when the reset is issued. So attempt to complete
1333331766Sken	 * all mailbox commands.
1334331766Sken	 */
1335331766Sken	iters = 10;
1336331766Sken	while (!ocs_list_empty(&hw->cmd_head) && iters) {
1337331766Sken		ocs_udelay(10000);
1338331766Sken		ocs_hw_flush(hw);
1339331766Sken		iters--;
1340331766Sken	}
1341331766Sken
1342331766Sken	if (ocs_list_empty(&hw->cmd_head)) {
1343331766Sken		ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1344331766Sken	} else {
1345331766Sken		ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1346331766Sken	}
1347331766Sken
1348331766Sken	/* Reset the chip */
1349331766Sken	switch(reset) {
1350331766Sken	case OCS_HW_RESET_FUNCTION:
1351331766Sken		ocs_log_debug(hw->os, "issuing function level reset\n");
1352331766Sken		if (sli_reset(&hw->sli)) {
1353331766Sken			ocs_log_err(hw->os, "sli_reset failed\n");
1354331766Sken			rc = OCS_HW_RTN_ERROR;
1355331766Sken		}
1356331766Sken		break;
1357331766Sken	case OCS_HW_RESET_FIRMWARE:
1358331766Sken		ocs_log_debug(hw->os, "issuing firmware reset\n");
1359331766Sken		if (sli_fw_reset(&hw->sli)) {
1360331766Sken			ocs_log_err(hw->os, "sli_soft_reset failed\n");
1361331766Sken			rc = OCS_HW_RTN_ERROR;
1362331766Sken		}
1363331766Sken		/*
1364331766Sken		 * Because the FW reset leaves the FW in a non-running state,
1365331766Sken		 * follow that with a regular reset.
1366331766Sken		 */
1367331766Sken		ocs_log_debug(hw->os, "issuing function level reset\n");
1368331766Sken		if (sli_reset(&hw->sli)) {
1369331766Sken			ocs_log_err(hw->os, "sli_reset failed\n");
1370331766Sken			rc = OCS_HW_RTN_ERROR;
1371331766Sken		}
1372331766Sken		break;
1373331766Sken	default:
1374331766Sken		ocs_log_test(hw->os, "unknown reset type - no reset performed\n");
1375331766Sken		hw->state = prev_state;
1376331766Sken		return OCS_HW_RTN_ERROR;
1377331766Sken	}
1378331766Sken
1379331766Sken	/* Not safe to walk command/io lists unless they've been initialized */
1380331766Sken	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1381331766Sken		ocs_hw_command_cancel(hw);
1382331766Sken
1383331766Sken		/* Clean up the inuse list, the free list and the wait free list */
1384331766Sken		ocs_hw_io_cancel(hw);
1385331766Sken
1386331766Sken		ocs_memset(hw->domains, 0, sizeof(hw->domains));
1387331766Sken		ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
1388331766Sken
1389331766Sken		ocs_hw_link_event_init(hw);
1390331766Sken
1391331766Sken		ocs_lock(&hw->io_lock);
1392331766Sken			/* The io lists should be empty, but remove any that didn't get cleaned up. */
1393331766Sken			while (!ocs_list_empty(&hw->io_timed_wqe)) {
1394331766Sken				ocs_list_remove_head(&hw->io_timed_wqe);
1395331766Sken			}
1396331766Sken			/* Don't clean up the io_inuse list, the backend will do that when it finishes the IO */
1397331766Sken
1398331766Sken			while (!ocs_list_empty(&hw->io_free)) {
1399331766Sken				ocs_list_remove_head(&hw->io_free);
1400331766Sken			}
1401331766Sken			while (!ocs_list_empty(&hw->io_wait_free)) {
1402331766Sken				ocs_list_remove_head(&hw->io_wait_free);
1403331766Sken			}
1404331766Sken
1405331766Sken			/* Reset the request tag pool, the HW IO request tags are reassigned in ocs_hw_setup_io() */
1406331766Sken			ocs_hw_reqtag_reset(hw);
1407331766Sken
1408331766Sken		ocs_unlock(&hw->io_lock);
1409331766Sken	}
1410331766Sken
1411331766Sken	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1412331766Sken		for (i = 0; i < hw->wq_count; i++) {
1413331766Sken			sli_queue_reset(&hw->sli, &hw->wq[i]);
1414331766Sken		}
1415331766Sken
1416331766Sken		for (i = 0; i < hw->rq_count; i++) {
1417331766Sken			sli_queue_reset(&hw->sli, &hw->rq[i]);
1418331766Sken		}
1419331766Sken
1420331766Sken		for (i = 0; i < hw->hw_rq_count; i++) {
1421331766Sken			hw_rq_t *rq = hw->hw_rq[i];
1422331766Sken			if (rq->rq_tracker != NULL) {
1423331766Sken				uint32_t j;
1424331766Sken
1425331766Sken				for (j = 0; j < rq->entry_count; j++) {
1426331766Sken					rq->rq_tracker[j] = NULL;
1427331766Sken				}
1428331766Sken			}
1429331766Sken		}
1430331766Sken
1431331766Sken		for (i = 0; i < hw->mq_count; i++) {
1432331766Sken			sli_queue_reset(&hw->sli, &hw->mq[i]);
1433331766Sken		}
1434331766Sken
1435331766Sken		for (i = 0; i < hw->cq_count; i++) {
1436331766Sken			sli_queue_reset(&hw->sli, &hw->cq[i]);
1437331766Sken		}
1438331766Sken
1439331766Sken		for (i = 0; i < hw->eq_count; i++) {
1440331766Sken			sli_queue_reset(&hw->sli, &hw->eq[i]);
1441331766Sken		}
1442331766Sken
1443331766Sken		/* Free rq buffers */
1444331766Sken		ocs_hw_rx_free(hw);
1445331766Sken
1446331766Sken		/* Teardown the HW queue topology */
1447331766Sken		hw_queue_teardown(hw);
1448331766Sken	} else {
1449331766Sken
1450331766Sken		/* Free rq buffers */
1451331766Sken		ocs_hw_rx_free(hw);
1452331766Sken	}
1453331766Sken
1454331766Sken	/*
1455331766Sken	 * Re-apply the run-time workarounds after clearing the SLI config
1456331766Sken	 * fields in sli_reset.
1457331766Sken	 */
1458331766Sken	ocs_hw_workaround_setup(hw);
1459331766Sken	hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
1460331766Sken
1461331766Sken	return rc;
1462331766Sken}
1463331766Sken
1464331766Skenint32_t
1465331766Skenocs_hw_get_num_eq(ocs_hw_t *hw)
1466331766Sken{
1467331766Sken	return hw->eq_count;
1468331766Sken}
1469331766Sken
1470331766Skenstatic int32_t
1471331766Skenocs_hw_get_fw_timed_out(ocs_hw_t *hw)
1472331766Sken{
1473331766Sken	/* The error values below are taken from LOWLEVEL_SET_WATCHDOG_TIMER_rev1.pdf
1474331766Sken	* No further explanation is given in the document.
1475331766Sken	* */
1476331766Sken	return (sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1) == 0x2 &&
1477331766Sken		sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2) == 0x10);
1478331766Sken}
1479331766Sken
1480331766Sken
1481331766Skenocs_hw_rtn_e
1482331766Skenocs_hw_get(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t *value)
1483331766Sken{
1484331766Sken	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1485331766Sken	int32_t			tmp;
1486331766Sken
1487331766Sken	if (!value) {
1488331766Sken		return OCS_HW_RTN_ERROR;
1489331766Sken	}
1490331766Sken
1491331766Sken	*value = 0;
1492331766Sken
1493331766Sken	switch (prop) {
1494331766Sken	case OCS_HW_N_IO:
1495331766Sken		*value = hw->config.n_io;
1496331766Sken		break;
1497331766Sken	case OCS_HW_N_SGL:
1498331766Sken		*value = (hw->config.n_sgl - SLI4_SGE_MAX_RESERVED);
1499331766Sken		break;
1500331766Sken	case OCS_HW_MAX_IO:
1501331766Sken		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
1502331766Sken		break;
1503331766Sken	case OCS_HW_MAX_NODES:
1504331766Sken		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1505331766Sken		break;
1506331766Sken	case OCS_HW_MAX_RQ_ENTRIES:
1507331766Sken		*value = hw->num_qentries[SLI_QTYPE_RQ];
1508331766Sken		break;
1509331766Sken	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1510331766Sken		*value = hw->config.rq_default_buffer_size;
1511331766Sken		break;
1512331766Sken	case OCS_HW_AUTO_XFER_RDY_CAPABLE:
1513331766Sken		*value = sli_get_auto_xfer_rdy_capable(&hw->sli);
1514331766Sken		break;
1515331766Sken	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1516331766Sken		*value = hw->config.auto_xfer_rdy_xri_cnt;
1517331766Sken		break;
1518331766Sken	case OCS_HW_AUTO_XFER_RDY_SIZE:
1519331766Sken		*value = hw->config.auto_xfer_rdy_size;
1520331766Sken		break;
1521331766Sken	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1522331766Sken		switch (hw->config.auto_xfer_rdy_blk_size_chip) {
1523331766Sken		case 0:
1524331766Sken			*value = 512;
1525331766Sken			break;
1526331766Sken		case 1:
1527331766Sken			*value = 1024;
1528331766Sken			break;
1529331766Sken		case 2:
1530331766Sken			*value = 2048;
1531331766Sken			break;
1532331766Sken		case 3:
1533331766Sken			*value = 4096;
1534331766Sken			break;
1535331766Sken		case 4:
1536331766Sken			*value = 520;
1537331766Sken			break;
1538331766Sken		default:
1539331766Sken			*value = 0;
1540331766Sken			rc = OCS_HW_RTN_ERROR;
1541331766Sken			break;
1542331766Sken		}
1543331766Sken		break;
1544331766Sken	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1545331766Sken		*value = hw->config.auto_xfer_rdy_t10_enable;
1546331766Sken		break;
1547331766Sken	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1548331766Sken		*value = hw->config.auto_xfer_rdy_p_type;
1549331766Sken		break;
1550331766Sken	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1551331766Sken		*value = hw->config.auto_xfer_rdy_ref_tag_is_lba;
1552331766Sken		break;
1553331766Sken	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1554331766Sken		*value = hw->config.auto_xfer_rdy_app_tag_valid;
1555331766Sken		break;
1556331766Sken	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1557331766Sken		*value = hw->config.auto_xfer_rdy_app_tag_value;
1558331766Sken		break;
1559331766Sken	case OCS_HW_MAX_SGE:
1560331766Sken		*value = sli_get_max_sge(&hw->sli);
1561331766Sken		break;
1562331766Sken	case OCS_HW_MAX_SGL:
1563331766Sken		*value = sli_get_max_sgl(&hw->sli);
1564331766Sken		break;
1565331766Sken	case OCS_HW_TOPOLOGY:
1566331766Sken		/*
1567331766Sken		 * Infer link.status based on link.speed.
1568331766Sken		 * Report OCS_HW_TOPOLOGY_NONE if the link is down.
1569331766Sken		 */
1570331766Sken		if (hw->link.speed == 0) {
1571331766Sken			*value = OCS_HW_TOPOLOGY_NONE;
1572331766Sken			break;
1573331766Sken		}
1574331766Sken		switch (hw->link.topology) {
1575331766Sken		case SLI_LINK_TOPO_NPORT:
1576331766Sken			*value = OCS_HW_TOPOLOGY_NPORT;
1577331766Sken			break;
1578331766Sken		case SLI_LINK_TOPO_LOOP:
1579331766Sken			*value = OCS_HW_TOPOLOGY_LOOP;
1580331766Sken			break;
1581331766Sken		case SLI_LINK_TOPO_NONE:
1582331766Sken			*value = OCS_HW_TOPOLOGY_NONE;
1583331766Sken			break;
1584331766Sken		default:
1585331766Sken			ocs_log_test(hw->os, "unsupported topology %#x\n", hw->link.topology);
1586331766Sken			rc = OCS_HW_RTN_ERROR;
1587331766Sken			break;
1588331766Sken		}
1589331766Sken		break;
1590331766Sken	case OCS_HW_CONFIG_TOPOLOGY:
1591331766Sken		*value = hw->config.topology;
1592331766Sken		break;
1593331766Sken	case OCS_HW_LINK_SPEED:
1594331766Sken		*value = hw->link.speed;
1595331766Sken		break;
1596331766Sken	case OCS_HW_LINK_CONFIG_SPEED:
1597331766Sken		switch (hw->config.speed) {
1598331766Sken		case FC_LINK_SPEED_10G:
1599331766Sken			*value = 10000;
1600331766Sken			break;
1601331766Sken		case FC_LINK_SPEED_AUTO_16_8_4:
1602331766Sken			*value = 0;
1603331766Sken			break;
1604331766Sken		case FC_LINK_SPEED_2G:
1605331766Sken			*value = 2000;
1606331766Sken			break;
1607331766Sken		case FC_LINK_SPEED_4G:
1608331766Sken			*value = 4000;
1609331766Sken			break;
1610331766Sken		case FC_LINK_SPEED_8G:
1611331766Sken			*value = 8000;
1612331766Sken			break;
1613331766Sken		case FC_LINK_SPEED_16G:
1614331766Sken			*value = 16000;
1615331766Sken			break;
1616331766Sken		case FC_LINK_SPEED_32G:
1617331766Sken			*value = 32000;
1618331766Sken			break;
1619331766Sken		default:
1620331766Sken			ocs_log_test(hw->os, "unsupported speed %#x\n", hw->config.speed);
1621331766Sken			rc = OCS_HW_RTN_ERROR;
1622331766Sken			break;
1623331766Sken		}
1624331766Sken		break;
1625331766Sken	case OCS_HW_IF_TYPE:
1626331766Sken		*value = sli_get_if_type(&hw->sli);
1627331766Sken		break;
1628331766Sken	case OCS_HW_SLI_REV:
1629331766Sken		*value = sli_get_sli_rev(&hw->sli);
1630331766Sken		break;
1631331766Sken	case OCS_HW_SLI_FAMILY:
1632331766Sken		*value = sli_get_sli_family(&hw->sli);
1633331766Sken		break;
1634331766Sken	case OCS_HW_DIF_CAPABLE:
1635331766Sken		*value = sli_get_dif_capable(&hw->sli);
1636331766Sken		break;
1637331766Sken	case OCS_HW_DIF_SEED:
1638331766Sken		*value = hw->config.dif_seed;
1639331766Sken		break;
1640331766Sken	case OCS_HW_DIF_MODE:
1641331766Sken		*value = hw->config.dif_mode;
1642331766Sken		break;
1643331766Sken	case OCS_HW_DIF_MULTI_SEPARATE:
1644331766Sken		/* Lancer supports multiple DIF separates */
1645331766Sken		if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
1646331766Sken			*value = TRUE;
1647331766Sken		} else {
1648331766Sken			*value = FALSE;
1649331766Sken		}
1650331766Sken		break;
1651331766Sken	case OCS_HW_DUMP_MAX_SIZE:
1652331766Sken		*value = hw->dump_size;
1653331766Sken		break;
1654331766Sken	case OCS_HW_DUMP_READY:
1655331766Sken		*value = sli_dump_is_ready(&hw->sli);
1656331766Sken		break;
1657331766Sken	case OCS_HW_DUMP_PRESENT:
1658331766Sken		*value = sli_dump_is_present(&hw->sli);
1659331766Sken		break;
1660331766Sken	case OCS_HW_RESET_REQUIRED:
1661331766Sken		tmp = sli_reset_required(&hw->sli);
1662331766Sken		if(tmp < 0) {
1663331766Sken			rc = OCS_HW_RTN_ERROR;
1664331766Sken		} else {
1665331766Sken			*value = tmp;
1666331766Sken		}
1667331766Sken		break;
1668331766Sken	case OCS_HW_FW_ERROR:
1669331766Sken		*value = sli_fw_error_status(&hw->sli);
1670331766Sken		break;
1671331766Sken	case OCS_HW_FW_READY:
1672331766Sken		*value = sli_fw_ready(&hw->sli);
1673331766Sken		break;
1674331766Sken	case OCS_HW_FW_TIMED_OUT:
1675331766Sken		*value = ocs_hw_get_fw_timed_out(hw);
1676331766Sken		break;
1677331766Sken	case OCS_HW_HIGH_LOGIN_MODE:
1678331766Sken		*value = sli_get_hlm_capable(&hw->sli);
1679331766Sken		break;
1680331766Sken	case OCS_HW_PREREGISTER_SGL:
1681331766Sken		*value = sli_get_sgl_preregister_required(&hw->sli);
1682331766Sken		break;
1683331766Sken	case OCS_HW_HW_REV1:
1684331766Sken		*value = sli_get_hw_revision(&hw->sli, 0);
1685331766Sken		break;
1686331766Sken	case OCS_HW_HW_REV2:
1687331766Sken		*value = sli_get_hw_revision(&hw->sli, 1);
1688331766Sken		break;
1689331766Sken	case OCS_HW_HW_REV3:
1690331766Sken		*value = sli_get_hw_revision(&hw->sli, 2);
1691331766Sken		break;
1692331766Sken	case OCS_HW_LINKCFG:
1693331766Sken		*value = hw->linkcfg;
1694331766Sken		break;
1695331766Sken	case OCS_HW_ETH_LICENSE:
1696331766Sken		*value = hw->eth_license;
1697331766Sken		break;
1698331766Sken	case OCS_HW_LINK_MODULE_TYPE:
1699331766Sken		*value = sli_get_link_module_type(&hw->sli);
1700331766Sken		break;
1701331766Sken	case OCS_HW_NUM_CHUTES:
1702331766Sken		*value = ocs_hw_get_num_chutes(hw);
1703331766Sken		break;
1704331766Sken	case OCS_HW_DISABLE_AR_TGT_DIF:
1705331766Sken		*value = hw->workaround.disable_ar_tgt_dif;
1706331766Sken		break;
1707331766Sken	case OCS_HW_EMULATE_I_ONLY_AAB:
1708331766Sken		*value = hw->config.i_only_aab;
1709331766Sken		break;
1710331766Sken	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
1711331766Sken		*value = hw->config.emulate_tgt_wqe_timeout;
1712331766Sken		break;
1713331766Sken	case OCS_HW_VPD_LEN:
1714331766Sken		*value = sli_get_vpd_len(&hw->sli);
1715331766Sken		break;
1716331766Sken	case OCS_HW_SGL_CHAINING_CAPABLE:
1717331766Sken		*value = sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported;
1718331766Sken		break;
1719331766Sken	case OCS_HW_SGL_CHAINING_ALLOWED:
1720331766Sken		/*
1721331766Sken		 * SGL Chaining is allowed in the following cases:
1722331766Sken		 *   1. Lancer with host SGL Lists
1723331766Sken		 *   2. Skyhawk with pre-registered SGL Lists
1724331766Sken		 */
1725331766Sken		*value = FALSE;
1726331766Sken		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1727331766Sken		    !sli_get_sgl_preregister(&hw->sli) &&
1728331766Sken		    SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)) {
1729331766Sken			*value = TRUE;
1730331766Sken		}
1731331766Sken
1732331766Sken		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1733331766Sken		    sli_get_sgl_preregister(&hw->sli) &&
1734331766Sken		    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
1735331766Sken			(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
1736331766Sken			*value = TRUE;
1737331766Sken		}
1738331766Sken		break;
1739331766Sken	case OCS_HW_SGL_CHAINING_HOST_ALLOCATED:
1740331766Sken		/* Only lancer supports host allocated SGL Chaining buffers. */
1741331766Sken		*value = ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1742331766Sken			  (SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)));
1743331766Sken		break;
1744331766Sken	case OCS_HW_SEND_FRAME_CAPABLE:
1745331766Sken		if (hw->workaround.ignore_send_frame) {
1746331766Sken			*value = 0;
1747331766Sken		} else {
1748331766Sken			/* Only lancer is capable */
1749331766Sken			*value = sli_get_if_type(&hw->sli) == SLI4_IF_TYPE_LANCER_FC_ETH;
1750331766Sken		}
1751331766Sken		break;
1752331766Sken	case OCS_HW_RQ_SELECTION_POLICY:
1753331766Sken		*value = hw->config.rq_selection_policy;
1754331766Sken		break;
1755331766Sken	case OCS_HW_RR_QUANTA:
1756331766Sken		*value = hw->config.rr_quanta;
1757331766Sken		break;
1758331766Sken	case OCS_HW_MAX_VPORTS:
1759331766Sken		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_VPI);
1760331766Sken	default:
1761331766Sken		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1762331766Sken		rc = OCS_HW_RTN_ERROR;
1763331766Sken	}
1764331766Sken
1765331766Sken	return rc;
1766331766Sken}
1767331766Sken
1768331766Skenvoid *
1769331766Skenocs_hw_get_ptr(ocs_hw_t *hw, ocs_hw_property_e prop)
1770331766Sken{
1771331766Sken	void	*rc = NULL;
1772331766Sken
1773331766Sken	switch (prop) {
1774331766Sken	case OCS_HW_WWN_NODE:
1775331766Sken		rc = sli_get_wwn_node(&hw->sli);
1776331766Sken		break;
1777331766Sken	case OCS_HW_WWN_PORT:
1778331766Sken		rc = sli_get_wwn_port(&hw->sli);
1779331766Sken		break;
1780331766Sken	case OCS_HW_VPD:
1781331766Sken		/* make sure VPD length is non-zero */
1782331766Sken		if (sli_get_vpd_len(&hw->sli)) {
1783331766Sken			rc = sli_get_vpd(&hw->sli);
1784331766Sken		}
1785331766Sken		break;
1786331766Sken	case OCS_HW_FW_REV:
1787331766Sken		rc = sli_get_fw_name(&hw->sli, 0);
1788331766Sken		break;
1789331766Sken	case OCS_HW_FW_REV2:
1790331766Sken		rc = sli_get_fw_name(&hw->sli, 1);
1791331766Sken		break;
1792331766Sken	case OCS_HW_IPL:
1793331766Sken		rc = sli_get_ipl_name(&hw->sli);
1794331766Sken		break;
1795331766Sken	case OCS_HW_PORTNUM:
1796331766Sken		rc = sli_get_portnum(&hw->sli);
1797331766Sken		break;
1798331766Sken	case OCS_HW_BIOS_VERSION_STRING:
1799331766Sken		rc = sli_get_bios_version_string(&hw->sli);
1800331766Sken		break;
1801331766Sken	default:
1802331766Sken		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1803331766Sken	}
1804331766Sken
1805331766Sken	return rc;
1806331766Sken}
1807331766Sken
1808331766Sken
1809331766Sken
1810331766Skenocs_hw_rtn_e
1811331766Skenocs_hw_set(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t value)
1812331766Sken{
1813331766Sken	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1814331766Sken
1815331766Sken	switch (prop) {
1816331766Sken	case OCS_HW_N_IO:
1817331766Sken		if (value > sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI) ||
1818331766Sken		    value == 0) {
1819331766Sken			ocs_log_test(hw->os, "IO value out of range %d vs %d\n",
1820331766Sken					value, sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI));
1821331766Sken			rc = OCS_HW_RTN_ERROR;
1822331766Sken		} else {
1823331766Sken			hw->config.n_io = value;
1824331766Sken		}
1825331766Sken		break;
1826331766Sken	case OCS_HW_N_SGL:
1827331766Sken		value += SLI4_SGE_MAX_RESERVED;
1828331766Sken		if (value > sli_get_max_sgl(&hw->sli)) {
1829331766Sken			ocs_log_test(hw->os, "SGL value out of range %d vs %d\n",
1830331766Sken					value, sli_get_max_sgl(&hw->sli));
1831331766Sken			rc = OCS_HW_RTN_ERROR;
1832331766Sken		} else {
1833331766Sken			hw->config.n_sgl = value;
1834331766Sken		}
1835331766Sken		break;
1836331766Sken	case OCS_HW_TOPOLOGY:
1837331766Sken		if ((sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) &&
1838331766Sken				(value != OCS_HW_TOPOLOGY_AUTO)) {
1839331766Sken			ocs_log_test(hw->os, "unsupported topology=%#x medium=%#x\n",
1840331766Sken					value, sli_get_medium(&hw->sli));
1841331766Sken			rc = OCS_HW_RTN_ERROR;
1842331766Sken			break;
1843331766Sken		}
1844331766Sken
1845331766Sken		switch (value) {
1846331766Sken		case OCS_HW_TOPOLOGY_AUTO:
1847331766Sken			if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
1848331766Sken				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC);
1849331766Sken			} else {
1850331766Sken				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FCOE);
1851331766Sken			}
1852331766Sken			break;
1853331766Sken		case OCS_HW_TOPOLOGY_NPORT:
1854331766Sken			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_DA);
1855331766Sken			break;
1856331766Sken		case OCS_HW_TOPOLOGY_LOOP:
1857331766Sken			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_AL);
1858331766Sken			break;
1859331766Sken		default:
1860331766Sken			ocs_log_test(hw->os, "unsupported topology %#x\n", value);
1861331766Sken			rc = OCS_HW_RTN_ERROR;
1862331766Sken		}
1863331766Sken		hw->config.topology = value;
1864331766Sken		break;
1865331766Sken	case OCS_HW_LINK_SPEED:
1866331766Sken		if (sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) {
1867331766Sken			switch (value) {
1868331766Sken			case 0: 	/* Auto-speed negotiation */
1869331766Sken			case 10000:	/* FCoE speed */
1870331766Sken				hw->config.speed = FC_LINK_SPEED_10G;
1871331766Sken				break;
1872331766Sken			default:
1873331766Sken				ocs_log_test(hw->os, "unsupported speed=%#x medium=%#x\n",
1874331766Sken						value, sli_get_medium(&hw->sli));
1875331766Sken				rc = OCS_HW_RTN_ERROR;
1876331766Sken			}
1877331766Sken			break;
1878331766Sken		}
1879331766Sken
1880331766Sken		switch (value) {
1881331766Sken		case 0:		/* Auto-speed negotiation */
1882331766Sken			hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
1883331766Sken			break;
1884331766Sken		case 2000:	/* FC speeds */
1885331766Sken			hw->config.speed = FC_LINK_SPEED_2G;
1886331766Sken			break;
1887331766Sken		case 4000:
1888331766Sken			hw->config.speed = FC_LINK_SPEED_4G;
1889331766Sken			break;
1890331766Sken		case 8000:
1891331766Sken			hw->config.speed = FC_LINK_SPEED_8G;
1892331766Sken			break;
1893331766Sken		case 16000:
1894331766Sken			hw->config.speed = FC_LINK_SPEED_16G;
1895331766Sken			break;
1896331766Sken		case 32000:
1897331766Sken			hw->config.speed = FC_LINK_SPEED_32G;
1898331766Sken			break;
1899331766Sken		default:
1900331766Sken			ocs_log_test(hw->os, "unsupported speed %d\n", value);
1901331766Sken			rc = OCS_HW_RTN_ERROR;
1902331766Sken		}
1903331766Sken		break;
1904331766Sken	case OCS_HW_DIF_SEED:
1905331766Sken		/* Set the DIF seed - only for lancer right now */
1906331766Sken		if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
1907331766Sken			ocs_log_test(hw->os, "DIF seed not supported for this device\n");
1908331766Sken			rc = OCS_HW_RTN_ERROR;
1909331766Sken		} else {
1910331766Sken			hw->config.dif_seed = value;
1911331766Sken		}
1912331766Sken		break;
1913331766Sken	case OCS_HW_DIF_MODE:
1914331766Sken		switch (value) {
1915331766Sken		case OCS_HW_DIF_MODE_INLINE:
1916331766Sken			/*
1917331766Sken			 *  Make sure we support inline DIF.
1918331766Sken			 *
1919331766Sken			 * Note: Having both bits clear means that we have old
1920331766Sken			 *	FW that doesn't set the bits.
1921331766Sken			 */
1922331766Sken			if (sli_is_dif_inline_capable(&hw->sli)) {
1923331766Sken				hw->config.dif_mode = value;
1924331766Sken			} else {
1925331766Sken				ocs_log_test(hw->os, "chip does not support DIF inline\n");
1926331766Sken				rc = OCS_HW_RTN_ERROR;
1927331766Sken			}
1928331766Sken			break;
1929331766Sken		case OCS_HW_DIF_MODE_SEPARATE:
1930331766Sken			/* Make sure we support DIF separates. */
1931331766Sken			if (sli_is_dif_separate_capable(&hw->sli)) {
1932331766Sken				hw->config.dif_mode = value;
1933331766Sken			} else {
1934331766Sken				ocs_log_test(hw->os, "chip does not support DIF separate\n");
1935331766Sken				rc = OCS_HW_RTN_ERROR;
1936331766Sken			}
1937331766Sken		}
1938331766Sken		break;
1939331766Sken	case OCS_HW_RQ_PROCESS_LIMIT: {
1940331766Sken		hw_rq_t *rq;
1941331766Sken		uint32_t i;
1942331766Sken
1943331766Sken		/* For each hw_rq object, set its parent CQ limit value */
1944331766Sken		for (i = 0; i < hw->hw_rq_count; i++) {
1945331766Sken			rq = hw->hw_rq[i];
1946331766Sken			hw->cq[rq->cq->instance].proc_limit = value;
1947331766Sken		}
1948331766Sken		break;
1949331766Sken	}
1950331766Sken	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1951331766Sken		hw->config.rq_default_buffer_size = value;
1952331766Sken		break;
1953331766Sken	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1954331766Sken		hw->config.auto_xfer_rdy_xri_cnt = value;
1955331766Sken		break;
1956331766Sken	case OCS_HW_AUTO_XFER_RDY_SIZE:
1957331766Sken		hw->config.auto_xfer_rdy_size = value;
1958331766Sken		break;
1959331766Sken	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1960331766Sken		switch (value) {
1961331766Sken		case 512:
1962331766Sken			hw->config.auto_xfer_rdy_blk_size_chip = 0;
1963331766Sken			break;
1964331766Sken		case 1024:
1965331766Sken			hw->config.auto_xfer_rdy_blk_size_chip = 1;
1966331766Sken			break;
1967331766Sken		case 2048:
1968331766Sken			hw->config.auto_xfer_rdy_blk_size_chip = 2;
1969331766Sken			break;
1970331766Sken		case 4096:
1971331766Sken			hw->config.auto_xfer_rdy_blk_size_chip = 3;
1972331766Sken			break;
1973331766Sken		case 520:
1974331766Sken			hw->config.auto_xfer_rdy_blk_size_chip = 4;
1975331766Sken			break;
1976331766Sken		default:
1977331766Sken			ocs_log_err(hw->os, "Invalid block size %d\n",
1978331766Sken				    value);
1979331766Sken			rc = OCS_HW_RTN_ERROR;
1980331766Sken		}
1981331766Sken		break;
1982331766Sken	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1983331766Sken		hw->config.auto_xfer_rdy_t10_enable = value;
1984331766Sken		break;
1985331766Sken	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1986331766Sken		hw->config.auto_xfer_rdy_p_type = value;
1987331766Sken		break;
1988331766Sken	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1989331766Sken		hw->config.auto_xfer_rdy_ref_tag_is_lba = value;
1990331766Sken		break;
1991331766Sken	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1992331766Sken		hw->config.auto_xfer_rdy_app_tag_valid = value;
1993331766Sken		break;
1994331766Sken	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1995331766Sken		hw->config.auto_xfer_rdy_app_tag_value = value;
1996331766Sken		break;
1997331766Sken	case OCS_ESOC:
1998331766Sken		hw->config.esoc = value;
1999331766Sken	case OCS_HW_HIGH_LOGIN_MODE:
2000331766Sken		rc = sli_set_hlm(&hw->sli, value);
2001331766Sken		break;
2002331766Sken	case OCS_HW_PREREGISTER_SGL:
2003331766Sken		rc = sli_set_sgl_preregister(&hw->sli, value);
2004331766Sken		break;
2005331766Sken	case OCS_HW_ETH_LICENSE:
2006331766Sken		hw->eth_license = value;
2007331766Sken		break;
2008331766Sken	case OCS_HW_EMULATE_I_ONLY_AAB:
2009331766Sken		hw->config.i_only_aab = value;
2010331766Sken		break;
2011331766Sken	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
2012331766Sken		hw->config.emulate_tgt_wqe_timeout = value;
2013331766Sken		break;
2014331766Sken	case OCS_HW_BOUNCE:
2015331766Sken		hw->config.bounce = value;
2016331766Sken		break;
2017331766Sken	case OCS_HW_RQ_SELECTION_POLICY:
2018331766Sken		hw->config.rq_selection_policy = value;
2019331766Sken		break;
2020331766Sken	case OCS_HW_RR_QUANTA:
2021331766Sken		hw->config.rr_quanta = value;
2022331766Sken		break;
2023331766Sken	default:
2024331766Sken		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2025331766Sken		rc = OCS_HW_RTN_ERROR;
2026331766Sken	}
2027331766Sken
2028331766Sken	return rc;
2029331766Sken}
2030331766Sken
2031331766Sken
2032331766Skenocs_hw_rtn_e
2033331766Skenocs_hw_set_ptr(ocs_hw_t *hw, ocs_hw_property_e prop, void *value)
2034331766Sken{
2035331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2036331766Sken
2037331766Sken	switch (prop) {
2038331766Sken	case OCS_HW_WAR_VERSION:
2039331766Sken		hw->hw_war_version = value;
2040331766Sken		break;
2041331766Sken	case OCS_HW_FILTER_DEF: {
2042331766Sken		char *p = value;
2043331766Sken		uint32_t idx = 0;
2044331766Sken
2045331766Sken		for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) {
2046331766Sken			hw->config.filter_def[idx] = 0;
2047331766Sken		}
2048331766Sken
2049331766Sken		for (idx = 0; (idx < ARRAY_SIZE(hw->config.filter_def)) && (p != NULL) && *p; ) {
2050331766Sken			hw->config.filter_def[idx++] = ocs_strtoul(p, 0, 0);
2051331766Sken			p = ocs_strchr(p, ',');
2052331766Sken			if (p != NULL) {
2053331766Sken				p++;
2054331766Sken			}
2055331766Sken		}
2056331766Sken
2057331766Sken		break;
2058331766Sken	}
2059331766Sken	default:
2060331766Sken		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2061331766Sken		rc = OCS_HW_RTN_ERROR;
2062331766Sken		break;
2063331766Sken	}
2064331766Sken	return rc;
2065331766Sken}
2066331766Sken/**
2067331766Sken * @ingroup interrupt
2068331766Sken * @brief Check for the events associated with the interrupt vector.
2069331766Sken *
2070331766Sken * @param hw Hardware context.
2071331766Sken * @param vector Zero-based interrupt vector number.
2072331766Sken *
2073331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2074331766Sken */
2075331766Skenint32_t
2076331766Skenocs_hw_event_check(ocs_hw_t *hw, uint32_t vector)
2077331766Sken{
2078331766Sken	int32_t rc = 0;
2079331766Sken
2080331766Sken	if (!hw) {
2081331766Sken		ocs_log_err(NULL, "HW context NULL?!?\n");
2082331766Sken		return -1;
2083331766Sken	}
2084331766Sken
2085331766Sken	if (vector > hw->eq_count) {
2086331766Sken		ocs_log_err(hw->os, "vector %d. max %d\n",
2087331766Sken				vector, hw->eq_count);
2088331766Sken		return -1;
2089331766Sken	}
2090331766Sken
2091331766Sken	/*
2092331766Sken	 * The caller should disable interrupts if they wish to prevent us
2093331766Sken	 * from processing during a shutdown. The following states are defined:
2094331766Sken	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2095331766Sken	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2096331766Sken	 *                                    queues are cleared.
2097331766Sken	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2098331766Sken	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2099331766Sken	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2100331766Sken	 *                                        completions.
2101331766Sken	 */
2102331766Sken	if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
2103331766Sken		rc = sli_queue_is_empty(&hw->sli, &hw->eq[vector]);
2104331766Sken
2105331766Sken		/* Re-arm queue if there are no entries */
2106331766Sken		if (rc != 0) {
2107331766Sken			sli_queue_arm(&hw->sli, &hw->eq[vector], TRUE);
2108331766Sken		}
2109331766Sken	}
2110331766Sken	return rc;
2111331766Sken}
2112331766Sken
2113331766Skenvoid
2114331766Skenocs_hw_unsol_process_bounce(void *arg)
2115331766Sken{
2116331766Sken	ocs_hw_sequence_t *seq = arg;
2117331766Sken	ocs_hw_t *hw = seq->hw;
2118331766Sken
2119331766Sken	ocs_hw_assert(hw != NULL);
2120331766Sken	ocs_hw_assert(hw->callback.unsolicited != NULL);
2121331766Sken
2122331766Sken	hw->callback.unsolicited(hw->args.unsolicited, seq);
2123331766Sken}
2124331766Sken
2125331766Skenint32_t
2126331766Skenocs_hw_process(ocs_hw_t *hw, uint32_t vector, uint32_t max_isr_time_msec)
2127331766Sken{
2128331766Sken	hw_eq_t *eq;
2129331766Sken	int32_t rc = 0;
2130331766Sken
2131331766Sken	CPUTRACE("");
2132331766Sken
2133331766Sken	/*
2134331766Sken	 * The caller should disable interrupts if they wish to prevent us
2135331766Sken	 * from processing during a shutdown. The following states are defined:
2136331766Sken	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2137331766Sken	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2138331766Sken	 *                                    queues are cleared.
2139331766Sken	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2140331766Sken	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2141331766Sken	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2142331766Sken	 *                                        completions.
2143331766Sken	 */
2144331766Sken	if (hw->state == OCS_HW_STATE_UNINITIALIZED) {
2145331766Sken		return 0;
2146331766Sken	}
2147331766Sken
2148331766Sken	/* Get pointer to hw_eq_t */
2149331766Sken	eq = hw->hw_eq[vector];
2150331766Sken
2151331766Sken	OCS_STAT(eq->use_count++);
2152331766Sken
2153331766Sken	rc = ocs_hw_eq_process(hw, eq, max_isr_time_msec);
2154331766Sken
2155331766Sken	return rc;
2156331766Sken}
2157331766Sken
2158331766Sken/**
2159331766Sken * @ingroup interrupt
2160331766Sken * @brief Process events associated with an EQ.
2161331766Sken *
2162331766Sken * @par Description
2163331766Sken * Loop termination:
2164331766Sken * @n @n Without a mechanism to terminate the completion processing loop, it
2165331766Sken * is possible under some workload conditions for the loop to never terminate
2166331766Sken * (or at least take longer than the OS is happy to have an interrupt handler
2167331766Sken * or kernel thread context hold a CPU without yielding).
2168331766Sken * @n @n The approach taken here is to periodically check how much time
2169331766Sken * we have been in this
2170331766Sken * processing loop, and if we exceed a predetermined time (multiple seconds), the
2171331766Sken * loop is terminated, and ocs_hw_process() returns.
2172331766Sken *
2173331766Sken * @param hw Hardware context.
2174331766Sken * @param eq Pointer to HW EQ object.
2175331766Sken * @param max_isr_time_msec Maximum time in msec to stay in this function.
2176331766Sken *
2177331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2178331766Sken */
2179331766Skenint32_t
2180331766Skenocs_hw_eq_process(ocs_hw_t *hw, hw_eq_t *eq, uint32_t max_isr_time_msec)
2181331766Sken{
2182331766Sken	uint8_t		eqe[sizeof(sli4_eqe_t)] = { 0 };
2183331766Sken	uint32_t	done = FALSE;
2184331766Sken	uint32_t	tcheck_count;
2185331766Sken	time_t		tstart;
2186331766Sken	time_t		telapsed;
2187331766Sken
2188331766Sken	tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2189331766Sken	tstart = ocs_msectime();
2190331766Sken
2191331766Sken	CPUTRACE("");
2192331766Sken
2193331766Sken	while (!done && !sli_queue_read(&hw->sli, eq->queue, eqe)) {
2194331766Sken		uint16_t	cq_id = 0;
2195331766Sken		int32_t		rc;
2196331766Sken
2197331766Sken		rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
2198331766Sken		if (unlikely(rc)) {
2199331766Sken			if (rc > 0) {
2200331766Sken				uint32_t i;
2201331766Sken
2202331766Sken				/*
2203331766Sken				 * Received a sentinel EQE indicating the EQ is full.
2204331766Sken				 * Process all CQs
2205331766Sken				 */
2206331766Sken				for (i = 0; i < hw->cq_count; i++) {
2207331766Sken					ocs_hw_cq_process(hw, hw->hw_cq[i]);
2208331766Sken				}
2209331766Sken				continue;
2210331766Sken			} else {
2211331766Sken				return rc;
2212331766Sken			}
2213331766Sken		} else {
2214331766Sken			int32_t index = ocs_hw_queue_hash_find(hw->cq_hash, cq_id);
2215331766Sken			if (likely(index >= 0)) {
2216331766Sken				ocs_hw_cq_process(hw, hw->hw_cq[index]);
2217331766Sken			} else {
2218331766Sken				ocs_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
2219331766Sken			}
2220331766Sken		}
2221331766Sken
2222331766Sken
2223331766Sken		if (eq->queue->n_posted > (eq->queue->posted_limit)) {
2224331766Sken			sli_queue_arm(&hw->sli, eq->queue, FALSE);
2225331766Sken		}
2226331766Sken
2227331766Sken		if (tcheck_count && (--tcheck_count == 0)) {
2228331766Sken			tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2229331766Sken			telapsed = ocs_msectime() - tstart;
2230331766Sken			if (telapsed >= max_isr_time_msec) {
2231331766Sken				done = TRUE;
2232331766Sken			}
2233331766Sken		}
2234331766Sken	}
2235331766Sken	sli_queue_eq_arm(&hw->sli, eq->queue, TRUE);
2236331766Sken
2237331766Sken	return 0;
2238331766Sken}
2239331766Sken
2240331766Sken/**
2241331766Sken * @brief Submit queued (pending) mbx commands.
2242331766Sken *
2243331766Sken * @par Description
2244331766Sken * Submit queued mailbox commands.
2245331766Sken * --- Assumes that hw->cmd_lock is held ---
2246331766Sken *
2247331766Sken * @param hw Hardware context.
2248331766Sken *
2249331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2250331766Sken */
2251331766Skenstatic int32_t
2252331766Skenocs_hw_cmd_submit_pending(ocs_hw_t *hw)
2253331766Sken{
2254331766Sken	ocs_command_ctx_t *ctx;
2255331766Sken	int32_t rc = 0;
2256331766Sken
2257331766Sken	/* Assumes lock held */
2258331766Sken
2259331766Sken	/* Only submit MQE if there's room */
2260331766Sken	while (hw->cmd_head_count < (OCS_HW_MQ_DEPTH - 1)) {
2261331766Sken		ctx = ocs_list_remove_head(&hw->cmd_pending);
2262331766Sken		if (ctx == NULL) {
2263331766Sken			break;
2264331766Sken		}
2265331766Sken		ocs_list_add_tail(&hw->cmd_head, ctx);
2266331766Sken		hw->cmd_head_count++;
2267331766Sken		if (sli_queue_write(&hw->sli, hw->mq, ctx->buf) < 0) {
2268331766Sken			ocs_log_test(hw->os, "sli_queue_write failed: %d\n", rc);
2269331766Sken			rc = -1;
2270331766Sken			break;
2271331766Sken		}
2272331766Sken	}
2273331766Sken	return rc;
2274331766Sken}
2275331766Sken
2276331766Sken/**
2277331766Sken * @ingroup io
2278331766Sken * @brief Issue a SLI command.
2279331766Sken *
2280331766Sken * @par Description
2281331766Sken * Send a mailbox command to the hardware, and either wait for a completion
2282331766Sken * (OCS_CMD_POLL) or get an optional asynchronous completion (OCS_CMD_NOWAIT).
2283331766Sken *
2284331766Sken * @param hw Hardware context.
2285331766Sken * @param cmd Buffer containing a formatted command and results.
2286331766Sken * @param opts Command options:
2287331766Sken *  - OCS_CMD_POLL - Command executes synchronously and busy-waits for the completion.
2288331766Sken *  - OCS_CMD_NOWAIT - Command executes asynchronously. Uses callback.
2289331766Sken * @param cb Function callback used for asynchronous mode. May be NULL.
2290331766Sken * @n Prototype is <tt>(*cb)(void *arg, uint8_t *cmd)</tt>.
2291331766Sken * @n @n @b Note: If the
2292331766Sken * callback function pointer is NULL, the results of the command are silently
2293331766Sken * discarded, allowing this pointer to exist solely on the stack.
2294331766Sken * @param arg Argument passed to an asynchronous callback.
2295331766Sken *
2296331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2297331766Sken */
2298331766Skenocs_hw_rtn_e
2299331766Skenocs_hw_command(ocs_hw_t *hw, uint8_t *cmd, uint32_t opts, void *cb, void *arg)
2300331766Sken{
2301331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2302331766Sken
2303331766Sken	/*
2304331766Sken	 * If the chip is in an error state (UE'd) then reject this mailbox
2305331766Sken	 *  command.
2306331766Sken	 */
2307331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2308331766Sken		uint32_t err1 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1);
2309331766Sken		uint32_t err2 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2);
2310331766Sken		if (hw->expiration_logged == 0 && err1 == 0x2 && err2 == 0x10) {
2311331766Sken			hw->expiration_logged = 1;
2312331766Sken			ocs_log_crit(hw->os,"Emulex: Heartbeat expired after %d seconds\n",
2313331766Sken					hw->watchdog_timeout);
2314331766Sken		}
2315331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2316331766Sken		ocs_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
2317331766Sken			sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_STATUS),
2318331766Sken			err1, err2);
2319331766Sken
2320331766Sken		return OCS_HW_RTN_ERROR;
2321331766Sken	}
2322331766Sken
2323331766Sken	if (OCS_CMD_POLL == opts) {
2324331766Sken
2325331766Sken		ocs_lock(&hw->cmd_lock);
2326331766Sken		if (hw->mq->length && !sli_queue_is_empty(&hw->sli, hw->mq)) {
2327331766Sken			/*
2328331766Sken			 * Can't issue Boot-strap mailbox command with other
2329331766Sken			 * mail-queue commands pending as this interaction is
2330331766Sken			 * undefined
2331331766Sken			 */
2332331766Sken			rc = OCS_HW_RTN_ERROR;
2333331766Sken		} else {
2334331766Sken			void *bmbx = hw->sli.bmbx.virt;
2335331766Sken
2336331766Sken			ocs_memset(bmbx, 0, SLI4_BMBX_SIZE);
2337331766Sken			ocs_memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
2338331766Sken
2339331766Sken			if (sli_bmbx_command(&hw->sli) == 0) {
2340331766Sken				rc = OCS_HW_RTN_SUCCESS;
2341331766Sken				ocs_memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
2342331766Sken			}
2343331766Sken		}
2344331766Sken		ocs_unlock(&hw->cmd_lock);
2345331766Sken	} else if (OCS_CMD_NOWAIT == opts) {
2346331766Sken		ocs_command_ctx_t	*ctx = NULL;
2347331766Sken
2348331766Sken		ctx = ocs_malloc(hw->os, sizeof(ocs_command_ctx_t), OCS_M_ZERO | OCS_M_NOWAIT);
2349331766Sken		if (!ctx) {
2350331766Sken			ocs_log_err(hw->os, "can't allocate command context\n");
2351331766Sken			return OCS_HW_RTN_NO_RESOURCES;
2352331766Sken		}
2353331766Sken
2354331766Sken		if (hw->state != OCS_HW_STATE_ACTIVE) {
2355331766Sken			ocs_log_err(hw->os, "Can't send command, HW state=%d\n", hw->state);
2356331766Sken			ocs_free(hw->os, ctx, sizeof(*ctx));
2357331766Sken			return OCS_HW_RTN_ERROR;
2358331766Sken		}
2359331766Sken
2360331766Sken		if (cb) {
2361331766Sken			ctx->cb = cb;
2362331766Sken			ctx->arg = arg;
2363331766Sken		}
2364331766Sken		ctx->buf = cmd;
2365331766Sken		ctx->ctx = hw;
2366331766Sken
2367331766Sken		ocs_lock(&hw->cmd_lock);
2368331766Sken
2369331766Sken			/* Add to pending list */
2370331766Sken			ocs_list_add_tail(&hw->cmd_pending, ctx);
2371331766Sken
2372331766Sken			/* Submit as much of the pending list as we can */
2373331766Sken			if (ocs_hw_cmd_submit_pending(hw) == 0) {
2374331766Sken				rc = OCS_HW_RTN_SUCCESS;
2375331766Sken			}
2376331766Sken
2377331766Sken		ocs_unlock(&hw->cmd_lock);
2378331766Sken	}
2379331766Sken
2380331766Sken	return rc;
2381331766Sken}
2382331766Sken
2383331766Sken/**
2384331766Sken * @ingroup devInitShutdown
2385331766Sken * @brief Register a callback for the given event.
2386331766Sken *
2387331766Sken * @param hw Hardware context.
2388331766Sken * @param which Event of interest.
2389331766Sken * @param func Function to call when the event occurs.
2390331766Sken * @param arg Argument passed to the callback function.
2391331766Sken *
2392331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2393331766Sken */
2394331766Skenocs_hw_rtn_e
2395331766Skenocs_hw_callback(ocs_hw_t *hw, ocs_hw_callback_e which, void *func, void *arg)
2396331766Sken{
2397331766Sken
2398331766Sken	if (!hw || !func || (which >= OCS_HW_CB_MAX)) {
2399331766Sken		ocs_log_err(NULL, "bad parameter hw=%p which=%#x func=%p\n",
2400331766Sken			    hw, which, func);
2401331766Sken		return OCS_HW_RTN_ERROR;
2402331766Sken	}
2403331766Sken
2404331766Sken	switch (which) {
2405331766Sken	case OCS_HW_CB_DOMAIN:
2406331766Sken		hw->callback.domain = func;
2407331766Sken		hw->args.domain = arg;
2408331766Sken		break;
2409331766Sken	case OCS_HW_CB_PORT:
2410331766Sken		hw->callback.port = func;
2411331766Sken		hw->args.port = arg;
2412331766Sken		break;
2413331766Sken	case OCS_HW_CB_UNSOLICITED:
2414331766Sken		hw->callback.unsolicited = func;
2415331766Sken		hw->args.unsolicited = arg;
2416331766Sken		break;
2417331766Sken	case OCS_HW_CB_REMOTE_NODE:
2418331766Sken		hw->callback.rnode = func;
2419331766Sken		hw->args.rnode = arg;
2420331766Sken		break;
2421331766Sken	case OCS_HW_CB_BOUNCE:
2422331766Sken		hw->callback.bounce = func;
2423331766Sken		hw->args.bounce = arg;
2424331766Sken		break;
2425331766Sken	default:
2426331766Sken		ocs_log_test(hw->os, "unknown callback %#x\n", which);
2427331766Sken		return OCS_HW_RTN_ERROR;
2428331766Sken	}
2429331766Sken
2430331766Sken	return OCS_HW_RTN_SUCCESS;
2431331766Sken}
2432331766Sken
2433331766Sken/**
2434331766Sken * @ingroup port
2435331766Sken * @brief Allocate a port object.
2436331766Sken *
2437331766Sken * @par Description
2438331766Sken * This function allocates a VPI object for the port and stores it in the
2439331766Sken * indicator field of the port object.
2440331766Sken *
2441331766Sken * @param hw Hardware context.
2442331766Sken * @param sport SLI port object used to connect to the domain.
2443331766Sken * @param domain Domain object associated with this port (may be NULL).
2444331766Sken * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
2445331766Sken *
2446331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2447331766Sken */
2448331766Skenocs_hw_rtn_e
2449331766Skenocs_hw_port_alloc(ocs_hw_t *hw, ocs_sli_port_t *sport, ocs_domain_t *domain,
2450331766Sken		uint8_t *wwpn)
2451331766Sken{
2452331766Sken	uint8_t	*cmd = NULL;
2453331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2454331766Sken	uint32_t index;
2455331766Sken
2456331766Sken	sport->indicator = UINT32_MAX;
2457331766Sken	sport->hw = hw;
2458331766Sken	sport->ctx.app = sport;
2459331766Sken	sport->sm_free_req_pending = 0;
2460331766Sken
2461331766Sken	/*
2462331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2463331766Sken	 */
2464331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2465331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2466331766Sken		return OCS_HW_RTN_ERROR;
2467331766Sken	}
2468331766Sken
2469331766Sken	if (wwpn) {
2470331766Sken		ocs_memcpy(&sport->sli_wwpn, wwpn, sizeof(sport->sli_wwpn));
2471331766Sken	}
2472331766Sken
2473331766Sken	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VPI, &sport->indicator, &index)) {
2474331766Sken		ocs_log_err(hw->os, "FCOE_VPI allocation failure\n");
2475331766Sken		return OCS_HW_RTN_ERROR;
2476331766Sken	}
2477331766Sken
2478331766Sken	if (domain != NULL) {
2479331766Sken		ocs_sm_function_t	next = NULL;
2480331766Sken
2481331766Sken		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2482331766Sken		if (!cmd) {
2483331766Sken			ocs_log_err(hw->os, "command memory allocation failed\n");
2484331766Sken			rc = OCS_HW_RTN_NO_MEMORY;
2485331766Sken			goto ocs_hw_port_alloc_out;
2486331766Sken		}
2487331766Sken
2488331766Sken		/* If the WWPN is NULL, fetch the default WWPN and WWNN before
2489331766Sken		 * initializing the VPI
2490331766Sken		 */
2491331766Sken		if (!wwpn) {
2492331766Sken			next = __ocs_hw_port_alloc_read_sparm64;
2493331766Sken		} else {
2494331766Sken			next = __ocs_hw_port_alloc_init_vpi;
2495331766Sken		}
2496331766Sken
2497331766Sken		ocs_sm_transition(&sport->ctx, next, cmd);
2498331766Sken	} else if (!wwpn) {
2499331766Sken		/* This is the convention for the HW, not SLI */
2500331766Sken		ocs_log_test(hw->os, "need WWN for physical port\n");
2501331766Sken		rc = OCS_HW_RTN_ERROR;
2502331766Sken	} else {
2503331766Sken		/* domain NULL and wwpn non-NULL */
2504331766Sken		ocs_sm_transition(&sport->ctx, __ocs_hw_port_alloc_init, NULL);
2505331766Sken	}
2506331766Sken
2507331766Skenocs_hw_port_alloc_out:
2508331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
2509331766Sken		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2510331766Sken
2511331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
2512331766Sken	}
2513331766Sken
2514331766Sken	return rc;
2515331766Sken}
2516331766Sken
2517331766Sken/**
2518331766Sken * @ingroup port
2519331766Sken * @brief Attach a physical/virtual SLI port to a domain.
2520331766Sken *
2521331766Sken * @par Description
2522331766Sken * This function registers a previously-allocated VPI with the
2523331766Sken * device.
2524331766Sken *
2525331766Sken * @param hw Hardware context.
2526331766Sken * @param sport Pointer to the SLI port object.
2527331766Sken * @param fc_id Fibre Channel ID to associate with this port.
2528331766Sken *
2529331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success, or an error code on failure.
2530331766Sken */
2531331766Skenocs_hw_rtn_e
2532331766Skenocs_hw_port_attach(ocs_hw_t *hw, ocs_sli_port_t *sport, uint32_t fc_id)
2533331766Sken{
2534331766Sken	uint8_t	*buf = NULL;
2535331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2536331766Sken
2537331766Sken	if (!hw || !sport) {
2538331766Sken		ocs_log_err(hw ? hw->os : NULL,
2539331766Sken			"bad parameter(s) hw=%p sport=%p\n", hw,
2540331766Sken			sport);
2541331766Sken		return OCS_HW_RTN_ERROR;
2542331766Sken	}
2543331766Sken
2544331766Sken	/*
2545331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2546331766Sken	 */
2547331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2548331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2549331766Sken		return OCS_HW_RTN_ERROR;
2550331766Sken	}
2551331766Sken
2552331766Sken	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2553331766Sken	if (!buf) {
2554331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
2555331766Sken		return OCS_HW_RTN_NO_MEMORY;
2556331766Sken	}
2557331766Sken
2558331766Sken	sport->fc_id = fc_id;
2559331766Sken	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_ATTACH, buf);
2560331766Sken	return rc;
2561331766Sken}
2562331766Sken
2563331766Sken/**
2564331766Sken * @brief Called when the port control command completes.
2565331766Sken *
2566331766Sken * @par Description
2567331766Sken * We only need to free the mailbox command buffer.
2568331766Sken *
2569331766Sken * @param hw Hardware context.
2570331766Sken * @param status Status field from the mbox completion.
2571331766Sken * @param mqe Mailbox response structure.
2572331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
2573331766Sken *
2574331766Sken * @return Returns 0.
2575331766Sken */
2576331766Skenstatic int32_t
2577331766Skenocs_hw_cb_port_control(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
2578331766Sken{
2579331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
2580331766Sken	return 0;
2581331766Sken}
2582331766Sken
2583331766Sken/**
2584331766Sken * @ingroup port
2585331766Sken * @brief Control a port (initialize, shutdown, or set link configuration).
2586331766Sken *
2587331766Sken * @par Description
2588331766Sken * This function controls a port depending on the @c ctrl parameter:
2589331766Sken * - @b OCS_HW_PORT_INIT -
2590331766Sken * Issues the CONFIG_LINK and INIT_LINK commands for the specified port.
2591331766Sken * The HW generates an OCS_HW_DOMAIN_FOUND event when the link comes up.
2592331766Sken * .
2593331766Sken * - @b OCS_HW_PORT_SHUTDOWN -
2594331766Sken * Issues the DOWN_LINK command for the specified port.
2595331766Sken * The HW generates an OCS_HW_DOMAIN_LOST event when the link is down.
2596331766Sken * .
2597331766Sken * - @b OCS_HW_PORT_SET_LINK_CONFIG -
2598331766Sken * Sets the link configuration.
2599331766Sken *
2600331766Sken * @param hw Hardware context.
2601331766Sken * @param ctrl Specifies the operation:
2602331766Sken * - OCS_HW_PORT_INIT
2603331766Sken * - OCS_HW_PORT_SHUTDOWN
2604331766Sken * - OCS_HW_PORT_SET_LINK_CONFIG
2605331766Sken *
2606331766Sken * @param value Operation-specific value.
2607331766Sken * - OCS_HW_PORT_INIT - Selective reset AL_PA
2608331766Sken * - OCS_HW_PORT_SHUTDOWN - N/A
2609331766Sken * - OCS_HW_PORT_SET_LINK_CONFIG - An enum #ocs_hw_linkcfg_e value.
2610331766Sken *
2611331766Sken * @param cb Callback function to invoke the following operation.
2612331766Sken * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2613331766Sken * are handled by the OCS_HW_CB_DOMAIN callbacks).
2614331766Sken * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2615331766Sken * completes.
2616331766Sken *
2617331766Sken * @param arg Callback argument invoked after the command completes.
2618331766Sken * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2619331766Sken * are handled by the OCS_HW_CB_DOMAIN callbacks).
2620331766Sken * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2621331766Sken * completes.
2622331766Sken *
2623331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2624331766Sken */
2625331766Skenocs_hw_rtn_e
2626331766Skenocs_hw_port_control(ocs_hw_t *hw, ocs_hw_port_e ctrl, uintptr_t value, ocs_hw_port_control_cb_t cb, void *arg)
2627331766Sken{
2628331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2629331766Sken
2630331766Sken	switch (ctrl) {
2631331766Sken	case OCS_HW_PORT_INIT:
2632331766Sken	{
2633331766Sken		uint8_t	*init_link;
2634331766Sken		uint32_t speed = 0;
2635331766Sken		uint8_t reset_alpa = 0;
2636331766Sken
2637331766Sken		if (SLI_LINK_MEDIUM_FC == sli_get_medium(&hw->sli)) {
2638331766Sken			uint8_t	*cfg_link;
2639331766Sken
2640331766Sken			cfg_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2641331766Sken			if (cfg_link == NULL) {
2642331766Sken				ocs_log_err(hw->os, "no buffer for command\n");
2643331766Sken				return OCS_HW_RTN_NO_MEMORY;
2644331766Sken			}
2645331766Sken
2646331766Sken			if (sli_cmd_config_link(&hw->sli, cfg_link, SLI4_BMBX_SIZE)) {
2647331766Sken				rc = ocs_hw_command(hw, cfg_link, OCS_CMD_NOWAIT,
2648331766Sken							ocs_hw_cb_port_control, NULL);
2649331766Sken			}
2650331766Sken
2651331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
2652331766Sken				ocs_free(hw->os, cfg_link, SLI4_BMBX_SIZE);
2653331766Sken				ocs_log_err(hw->os, "CONFIG_LINK failed\n");
2654331766Sken				break;
2655331766Sken			}
2656331766Sken			speed = hw->config.speed;
2657331766Sken			reset_alpa = (uint8_t)(value & 0xff);
2658331766Sken		} else {
2659331766Sken			speed = FC_LINK_SPEED_10G;
2660331766Sken		}
2661331766Sken
2662331766Sken		/*
2663331766Sken		 * Bring link up, unless FW version is not supported
2664331766Sken		 */
2665331766Sken		if (hw->workaround.fw_version_too_low) {
2666331766Sken			if (SLI4_IF_TYPE_LANCER_FC_ETH == hw->sli.if_type) {
2667331766Sken				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2668331766Sken					OCS_FW_VER_STR(OCS_MIN_FW_VER_LANCER), (char *) sli_get_fw_name(&hw->sli,0));
2669331766Sken			} else {
2670331766Sken				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2671331766Sken					OCS_FW_VER_STR(OCS_MIN_FW_VER_SKYHAWK), (char *) sli_get_fw_name(&hw->sli, 0));
2672331766Sken			}
2673331766Sken
2674331766Sken			return OCS_HW_RTN_ERROR;
2675331766Sken		}
2676331766Sken
2677331766Sken		rc = OCS_HW_RTN_ERROR;
2678331766Sken
2679331766Sken		/* Allocate a new buffer for the init_link command */
2680331766Sken		init_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2681331766Sken		if (init_link == NULL) {
2682331766Sken			ocs_log_err(hw->os, "no buffer for command\n");
2683331766Sken			return OCS_HW_RTN_NO_MEMORY;
2684331766Sken		}
2685331766Sken
2686331766Sken
2687331766Sken		if (sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE, speed, reset_alpa)) {
2688331766Sken			rc = ocs_hw_command(hw, init_link, OCS_CMD_NOWAIT,
2689331766Sken						ocs_hw_cb_port_control, NULL);
2690331766Sken		}
2691331766Sken		/* Free buffer on error, since no callback is coming */
2692331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
2693331766Sken			ocs_free(hw->os, init_link, SLI4_BMBX_SIZE);
2694331766Sken			ocs_log_err(hw->os, "INIT_LINK failed\n");
2695331766Sken		}
2696331766Sken		break;
2697331766Sken	}
2698331766Sken	case OCS_HW_PORT_SHUTDOWN:
2699331766Sken	{
2700331766Sken		uint8_t	*down_link;
2701331766Sken
2702331766Sken		down_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2703331766Sken		if (down_link == NULL) {
2704331766Sken			ocs_log_err(hw->os, "no buffer for command\n");
2705331766Sken			return OCS_HW_RTN_NO_MEMORY;
2706331766Sken		}
2707331766Sken		if (sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE)) {
2708331766Sken			rc = ocs_hw_command(hw, down_link, OCS_CMD_NOWAIT,
2709331766Sken						ocs_hw_cb_port_control, NULL);
2710331766Sken		}
2711331766Sken		/* Free buffer on error, since no callback is coming */
2712331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
2713331766Sken			ocs_free(hw->os, down_link, SLI4_BMBX_SIZE);
2714331766Sken			ocs_log_err(hw->os, "DOWN_LINK failed\n");
2715331766Sken		}
2716331766Sken		break;
2717331766Sken	}
2718331766Sken	case OCS_HW_PORT_SET_LINK_CONFIG:
2719331766Sken		rc = ocs_hw_set_linkcfg(hw, (ocs_hw_linkcfg_e)value, OCS_CMD_NOWAIT, cb, arg);
2720331766Sken		break;
2721331766Sken	default:
2722331766Sken		ocs_log_test(hw->os, "unhandled control %#x\n", ctrl);
2723331766Sken		break;
2724331766Sken	}
2725331766Sken
2726331766Sken	return rc;
2727331766Sken}
2728331766Sken
2729331766Sken
2730331766Sken/**
2731331766Sken * @ingroup port
2732331766Sken * @brief Free port resources.
2733331766Sken *
2734331766Sken * @par Description
2735331766Sken * Issue the UNREG_VPI command to free the assigned VPI context.
2736331766Sken *
2737331766Sken * @param hw Hardware context.
2738331766Sken * @param sport SLI port object used to connect to the domain.
2739331766Sken *
2740331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2741331766Sken */
2742331766Skenocs_hw_rtn_e
2743331766Skenocs_hw_port_free(ocs_hw_t *hw, ocs_sli_port_t *sport)
2744331766Sken{
2745331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2746331766Sken
2747331766Sken	if (!hw || !sport) {
2748331766Sken		ocs_log_err(hw ? hw->os : NULL,
2749331766Sken			"bad parameter(s) hw=%p sport=%p\n", hw,
2750331766Sken			sport);
2751331766Sken		return OCS_HW_RTN_ERROR;
2752331766Sken	}
2753331766Sken
2754331766Sken	/*
2755331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2756331766Sken	 */
2757331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2758331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2759331766Sken		return OCS_HW_RTN_ERROR;
2760331766Sken	}
2761331766Sken
2762331766Sken	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_FREE, NULL);
2763331766Sken	return rc;
2764331766Sken}
2765331766Sken
2766331766Sken/**
2767331766Sken * @ingroup domain
2768331766Sken * @brief Allocate a fabric domain object.
2769331766Sken *
2770331766Sken * @par Description
2771331766Sken * This function starts a series of commands needed to connect to the domain, including
2772331766Sken *   - REG_FCFI
2773331766Sken *   - INIT_VFI
2774331766Sken *   - READ_SPARMS
2775331766Sken *   .
2776331766Sken * @b Note: Not all SLI interface types use all of the above commands.
2777331766Sken * @n @n Upon successful allocation, the HW generates a OCS_HW_DOMAIN_ALLOC_OK
2778331766Sken * event. On failure, it generates a OCS_HW_DOMAIN_ALLOC_FAIL event.
2779331766Sken *
2780331766Sken * @param hw Hardware context.
2781331766Sken * @param domain Pointer to the domain object.
2782331766Sken * @param fcf FCF index.
2783331766Sken * @param vlan VLAN ID.
2784331766Sken *
2785331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2786331766Sken */
2787331766Skenocs_hw_rtn_e
2788331766Skenocs_hw_domain_alloc(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fcf, uint32_t vlan)
2789331766Sken{
2790331766Sken	uint8_t		*cmd = NULL;
2791331766Sken	uint32_t	index;
2792331766Sken
2793331766Sken	if (!hw || !domain || !domain->sport) {
2794331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p sport=%p\n",
2795331766Sken				hw, domain, domain ? domain->sport : NULL);
2796331766Sken		return OCS_HW_RTN_ERROR;
2797331766Sken	}
2798331766Sken
2799331766Sken	/*
2800331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2801331766Sken	 */
2802331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2803331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2804331766Sken		return OCS_HW_RTN_ERROR;
2805331766Sken	}
2806331766Sken
2807331766Sken	cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2808331766Sken	if (!cmd) {
2809331766Sken		ocs_log_err(hw->os, "command memory allocation failed\n");
2810331766Sken		return OCS_HW_RTN_NO_MEMORY;
2811331766Sken	}
2812331766Sken
2813331766Sken	domain->dma = hw->domain_dmem;
2814331766Sken
2815331766Sken	domain->hw = hw;
2816331766Sken	domain->sm.app = domain;
2817331766Sken	domain->fcf = fcf;
2818331766Sken	domain->fcf_indicator = UINT32_MAX;
2819331766Sken	domain->vlan_id = vlan;
2820331766Sken	domain->indicator = UINT32_MAX;
2821331766Sken
2822331766Sken	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VFI, &domain->indicator, &index)) {
2823331766Sken		ocs_log_err(hw->os, "FCOE_VFI allocation failure\n");
2824331766Sken
2825331766Sken		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2826331766Sken
2827331766Sken		return OCS_HW_RTN_ERROR;
2828331766Sken	}
2829331766Sken
2830331766Sken	ocs_sm_transition(&domain->sm, __ocs_hw_domain_init, cmd);
2831331766Sken	return OCS_HW_RTN_SUCCESS;
2832331766Sken}
2833331766Sken
2834331766Sken/**
2835331766Sken * @ingroup domain
2836331766Sken * @brief Attach a SLI port to a domain.
2837331766Sken *
2838331766Sken * @param hw Hardware context.
2839331766Sken * @param domain Pointer to the domain object.
2840331766Sken * @param fc_id Fibre Channel ID to associate with this port.
2841331766Sken *
2842331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2843331766Sken */
2844331766Skenocs_hw_rtn_e
2845331766Skenocs_hw_domain_attach(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fc_id)
2846331766Sken{
2847331766Sken	uint8_t	*buf = NULL;
2848331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2849331766Sken
2850331766Sken	if (!hw || !domain) {
2851331766Sken		ocs_log_err(hw ? hw->os : NULL,
2852331766Sken			"bad parameter(s) hw=%p domain=%p\n",
2853331766Sken			hw, domain);
2854331766Sken		return OCS_HW_RTN_ERROR;
2855331766Sken	}
2856331766Sken
2857331766Sken	/*
2858331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2859331766Sken	 */
2860331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2861331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2862331766Sken		return OCS_HW_RTN_ERROR;
2863331766Sken	}
2864331766Sken
2865331766Sken	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2866331766Sken	if (!buf) {
2867331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
2868331766Sken		return OCS_HW_RTN_NO_MEMORY;
2869331766Sken	}
2870331766Sken
2871331766Sken	domain->sport->fc_id = fc_id;
2872331766Sken	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_ATTACH, buf);
2873331766Sken	return rc;
2874331766Sken}
2875331766Sken
2876331766Sken/**
2877331766Sken * @ingroup domain
2878331766Sken * @brief Free a fabric domain object.
2879331766Sken *
2880331766Sken * @par Description
2881331766Sken * Free both the driver and SLI port resources associated with the domain.
2882331766Sken *
2883331766Sken * @param hw Hardware context.
2884331766Sken * @param domain Pointer to the domain object.
2885331766Sken *
2886331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2887331766Sken */
2888331766Skenocs_hw_rtn_e
2889331766Skenocs_hw_domain_free(ocs_hw_t *hw, ocs_domain_t *domain)
2890331766Sken{
2891331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2892331766Sken
2893331766Sken	if (!hw || !domain) {
2894331766Sken		ocs_log_err(hw ? hw->os : NULL,
2895331766Sken			"bad parameter(s) hw=%p domain=%p\n",
2896331766Sken			hw, domain);
2897331766Sken		return OCS_HW_RTN_ERROR;
2898331766Sken	}
2899331766Sken
2900331766Sken	/*
2901331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2902331766Sken	 */
2903331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2904331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2905331766Sken		return OCS_HW_RTN_ERROR;
2906331766Sken	}
2907331766Sken
2908331766Sken	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_FREE, NULL);
2909331766Sken	return rc;
2910331766Sken}
2911331766Sken
2912331766Sken/**
2913331766Sken * @ingroup domain
2914331766Sken * @brief Free a fabric domain object.
2915331766Sken *
2916331766Sken * @par Description
2917331766Sken * Free the driver resources associated with the domain. The difference between
2918331766Sken * this call and ocs_hw_domain_free() is that this call assumes resources no longer
2919331766Sken * exist on the SLI port, due to a reset or after some error conditions.
2920331766Sken *
2921331766Sken * @param hw Hardware context.
2922331766Sken * @param domain Pointer to the domain object.
2923331766Sken *
2924331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2925331766Sken */
2926331766Skenocs_hw_rtn_e
2927331766Skenocs_hw_domain_force_free(ocs_hw_t *hw, ocs_domain_t *domain)
2928331766Sken{
2929331766Sken	if (!hw || !domain) {
2930331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p\n", hw, domain);
2931331766Sken		return OCS_HW_RTN_ERROR;
2932331766Sken	}
2933331766Sken
2934331766Sken	sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
2935331766Sken
2936331766Sken	return OCS_HW_RTN_SUCCESS;
2937331766Sken}
2938331766Sken
2939331766Sken/**
2940331766Sken * @ingroup node
2941331766Sken * @brief Allocate a remote node object.
2942331766Sken *
2943331766Sken * @param hw Hardware context.
2944331766Sken * @param rnode Allocated remote node object to initialize.
2945331766Sken * @param fc_addr FC address of the remote node.
2946331766Sken * @param sport SLI port used to connect to remote node.
2947331766Sken *
2948331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2949331766Sken */
2950331766Skenocs_hw_rtn_e
2951331766Skenocs_hw_node_alloc(ocs_hw_t *hw, ocs_remote_node_t *rnode, uint32_t fc_addr,
2952331766Sken		ocs_sli_port_t *sport)
2953331766Sken{
2954331766Sken	/* Check for invalid indicator */
2955331766Sken	if (UINT32_MAX != rnode->indicator) {
2956331766Sken		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x rpi=%#x\n",
2957331766Sken				fc_addr, rnode->indicator);
2958331766Sken		return OCS_HW_RTN_ERROR;
2959331766Sken	}
2960331766Sken
2961331766Sken	/*
2962331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
2963331766Sken	 */
2964331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
2965331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2966331766Sken		return OCS_HW_RTN_ERROR;
2967331766Sken	}
2968331766Sken
2969331766Sken	/* NULL SLI port indicates an unallocated remote node */
2970331766Sken	rnode->sport = NULL;
2971331766Sken
2972331766Sken	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &rnode->indicator, &rnode->index)) {
2973331766Sken		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
2974331766Sken				fc_addr);
2975331766Sken		return OCS_HW_RTN_ERROR;
2976331766Sken	}
2977331766Sken
2978331766Sken	rnode->fc_id = fc_addr;
2979331766Sken	rnode->sport = sport;
2980331766Sken
2981331766Sken	return OCS_HW_RTN_SUCCESS;
2982331766Sken}
2983331766Sken
2984331766Sken/**
2985331766Sken * @ingroup node
2986331766Sken * @brief Update a remote node object with the remote port's service parameters.
2987331766Sken *
2988331766Sken * @param hw Hardware context.
2989331766Sken * @param rnode Allocated remote node object to initialize.
2990331766Sken * @param sparms DMA buffer containing the remote port's service parameters.
2991331766Sken *
2992331766Sken * @return Returns 0 on success, or a non-zero value on failure.
2993331766Sken */
2994331766Skenocs_hw_rtn_e
2995331766Skenocs_hw_node_attach(ocs_hw_t *hw, ocs_remote_node_t *rnode, ocs_dma_t *sparms)
2996331766Sken{
2997331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
2998331766Sken	uint8_t		*buf = NULL;
2999331766Sken	uint32_t	count = 0;
3000331766Sken
3001331766Sken	if (!hw || !rnode || !sparms) {
3002331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p sparms=%p\n",
3003331766Sken			    hw, rnode, sparms);
3004331766Sken		return OCS_HW_RTN_ERROR;
3005331766Sken	}
3006331766Sken
3007331766Sken	/*
3008331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
3009331766Sken	 */
3010331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
3011331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3012331766Sken		return OCS_HW_RTN_ERROR;
3013331766Sken	}
3014331766Sken
3015331766Sken	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3016331766Sken	if (!buf) {
3017331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
3018331766Sken		return OCS_HW_RTN_NO_MEMORY;
3019331766Sken	}
3020331766Sken
3021331766Sken	/*
3022331766Sken	 * If the attach count is non-zero, this RPI has already been registered.
3023331766Sken	 * Otherwise, register the RPI
3024331766Sken	 */
3025331766Sken	if (rnode->index == UINT32_MAX) {
3026331766Sken		ocs_log_err(NULL, "bad parameter rnode->index invalid\n");
3027331766Sken		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3028331766Sken		return OCS_HW_RTN_ERROR;
3029331766Sken	}
3030331766Sken	count = ocs_atomic_add_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3031331766Sken	if (count) {
3032331766Sken		/*
3033331766Sken		 * Can't attach multiple FC_ID's to a node unless High Login
3034331766Sken		 * Mode is enabled
3035331766Sken		 */
3036331766Sken		if (sli_get_hlm(&hw->sli) == FALSE) {
3037331766Sken			ocs_log_test(hw->os, "attach to already attached node HLM=%d count=%d\n",
3038331766Sken					sli_get_hlm(&hw->sli), count);
3039331766Sken			rc = OCS_HW_RTN_SUCCESS;
3040331766Sken		} else {
3041331766Sken			rnode->node_group = TRUE;
3042331766Sken			rnode->attached = ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_attached);
3043331766Sken			rc = rnode->attached  ? OCS_HW_RTN_SUCCESS_SYNC : OCS_HW_RTN_SUCCESS;
3044331766Sken		}
3045331766Sken	} else {
3046331766Sken		rnode->node_group = FALSE;
3047331766Sken
3048331766Sken		ocs_display_sparams("", "reg rpi", 0, NULL, sparms->virt);
3049331766Sken		if (sli_cmd_reg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->fc_id,
3050331766Sken					rnode->indicator, rnode->sport->indicator,
3051331766Sken					sparms, 0, (hw->auto_xfer_rdy_enabled && hw->config.auto_xfer_rdy_t10_enable))) {
3052331766Sken			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT,
3053331766Sken					ocs_hw_cb_node_attach, rnode);
3054331766Sken		}
3055331766Sken	}
3056331766Sken
3057331766Sken	if (count || rc) {
3058331766Sken		if (rc < OCS_HW_RTN_SUCCESS) {
3059331766Sken			ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3060331766Sken			ocs_log_err(hw->os, "%s error\n", count ? "HLM" : "REG_RPI");
3061331766Sken		}
3062331766Sken		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3063331766Sken	}
3064331766Sken
3065331766Sken	return rc;
3066331766Sken}
3067331766Sken
3068331766Sken/**
3069331766Sken * @ingroup node
3070331766Sken * @brief Free a remote node resource.
3071331766Sken *
3072331766Sken * @param hw Hardware context.
3073331766Sken * @param rnode Remote node object to free.
3074331766Sken *
3075331766Sken * @return Returns 0 on success, or a non-zero value on failure.
3076331766Sken */
3077331766Skenocs_hw_rtn_e
3078331766Skenocs_hw_node_free_resources(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3079331766Sken{
3080331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3081331766Sken
3082331766Sken	if (!hw || !rnode) {
3083331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3084331766Sken			    hw, rnode);
3085331766Sken		return OCS_HW_RTN_ERROR;
3086331766Sken	}
3087331766Sken
3088331766Sken	if (rnode->sport) {
3089331766Sken		if (!rnode->attached) {
3090331766Sken			if (rnode->indicator != UINT32_MAX) {
3091331766Sken				if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3092331766Sken					ocs_log_err(hw->os, "FCOE_RPI free failure RPI %d addr=%#x\n",
3093331766Sken						    rnode->indicator, rnode->fc_id);
3094331766Sken					rc = OCS_HW_RTN_ERROR;
3095331766Sken				} else {
3096331766Sken					rnode->node_group = FALSE;
3097331766Sken					rnode->indicator = UINT32_MAX;
3098331766Sken					rnode->index = UINT32_MAX;
3099331766Sken					rnode->free_group = FALSE;
3100331766Sken				}
3101331766Sken			}
3102331766Sken		} else {
3103331766Sken			ocs_log_err(hw->os, "Error: rnode is still attached\n");
3104331766Sken			rc = OCS_HW_RTN_ERROR;
3105331766Sken		}
3106331766Sken	}
3107331766Sken
3108331766Sken	return rc;
3109331766Sken}
3110331766Sken
3111331766Sken
3112331766Sken/**
3113331766Sken * @ingroup node
3114331766Sken * @brief Free a remote node object.
3115331766Sken *
3116331766Sken * @param hw Hardware context.
3117331766Sken * @param rnode Remote node object to free.
3118331766Sken *
3119331766Sken * @return Returns 0 on success, or a non-zero value on failure.
3120331766Sken */
3121331766Skenocs_hw_rtn_e
3122331766Skenocs_hw_node_detach(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3123331766Sken{
3124331766Sken	uint8_t	*buf = NULL;
3125331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS_SYNC;
3126331766Sken	uint32_t	index = UINT32_MAX;
3127331766Sken
3128331766Sken	if (!hw || !rnode) {
3129331766Sken		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3130331766Sken			    hw, rnode);
3131331766Sken		return OCS_HW_RTN_ERROR;
3132331766Sken	}
3133331766Sken
3134331766Sken	/*
3135331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
3136331766Sken	 */
3137331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
3138331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3139331766Sken		return OCS_HW_RTN_ERROR;
3140331766Sken	}
3141331766Sken
3142331766Sken	index = rnode->index;
3143331766Sken
3144331766Sken	if (rnode->sport) {
3145331766Sken		uint32_t	count = 0;
3146331766Sken		uint32_t	fc_id;
3147331766Sken
3148331766Sken		if (!rnode->attached) {
3149331766Sken			return OCS_HW_RTN_SUCCESS_SYNC;
3150331766Sken		}
3151331766Sken
3152331766Sken		buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3153331766Sken		if (!buf) {
3154331766Sken			ocs_log_err(hw->os, "no buffer for command\n");
3155331766Sken			return OCS_HW_RTN_NO_MEMORY;
3156331766Sken		}
3157331766Sken
3158331766Sken		count = ocs_atomic_sub_return(&hw->rpi_ref[index].rpi_count, 1);
3159331766Sken
3160331766Sken		if (count <= 1) {
3161331766Sken			/* There are no other references to this RPI
3162331766Sken			 * so unregister it and free the resource. */
3163331766Sken			fc_id = UINT32_MAX;
3164331766Sken			rnode->node_group = FALSE;
3165331766Sken			rnode->free_group = TRUE;
3166331766Sken		} else {
3167331766Sken			if (sli_get_hlm(&hw->sli) == FALSE) {
3168331766Sken				ocs_log_test(hw->os, "Invalid count with HLM disabled, count=%d\n",
3169331766Sken						count);
3170331766Sken			}
3171331766Sken			fc_id = rnode->fc_id & 0x00ffffff;
3172331766Sken		}
3173331766Sken
3174331766Sken		rc = OCS_HW_RTN_ERROR;
3175331766Sken
3176331766Sken		if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->indicator,
3177331766Sken					SLI_RSRC_FCOE_RPI, fc_id)) {
3178331766Sken			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free, rnode);
3179331766Sken		}
3180331766Sken
3181331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
3182331766Sken			ocs_log_err(hw->os, "UNREG_RPI failed\n");
3183331766Sken			ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3184331766Sken			rc = OCS_HW_RTN_ERROR;
3185331766Sken		}
3186331766Sken	}
3187331766Sken
3188331766Sken	return rc;
3189331766Sken}
3190331766Sken
3191331766Sken/**
3192331766Sken * @ingroup node
3193331766Sken * @brief Free all remote node objects.
3194331766Sken *
3195331766Sken * @param hw Hardware context.
3196331766Sken *
3197331766Sken * @return Returns 0 on success, or a non-zero value on failure.
3198331766Sken */
3199331766Skenocs_hw_rtn_e
3200331766Skenocs_hw_node_free_all(ocs_hw_t *hw)
3201331766Sken{
3202331766Sken	uint8_t	*buf = NULL;
3203331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
3204331766Sken
3205331766Sken	if (!hw) {
3206331766Sken		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
3207331766Sken		return OCS_HW_RTN_ERROR;
3208331766Sken	}
3209331766Sken
3210331766Sken	/*
3211331766Sken	 * Check if the chip is in an error state (UE'd) before proceeding.
3212331766Sken	 */
3213331766Sken	if (sli_fw_error_status(&hw->sli) > 0) {
3214331766Sken		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3215331766Sken		return OCS_HW_RTN_ERROR;
3216331766Sken	}
3217331766Sken
3218331766Sken	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3219331766Sken	if (!buf) {
3220331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
3221331766Sken		return OCS_HW_RTN_NO_MEMORY;
3222331766Sken	}
3223331766Sken
3224331766Sken	if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, 0xffff,
3225331766Sken				SLI_RSRC_FCOE_FCFI, UINT32_MAX)) {
3226331766Sken		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free_all,
3227331766Sken				NULL);
3228331766Sken	}
3229331766Sken
3230331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
3231331766Sken		ocs_log_err(hw->os, "UNREG_RPI failed\n");
3232331766Sken		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3233331766Sken		rc = OCS_HW_RTN_ERROR;
3234331766Sken	}
3235331766Sken
3236331766Sken	return rc;
3237331766Sken}
3238331766Sken
3239331766Skenocs_hw_rtn_e
3240331766Skenocs_hw_node_group_alloc(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3241331766Sken{
3242331766Sken
3243331766Sken	if (!hw || !ngroup) {
3244331766Sken		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3245331766Sken				hw, ngroup);
3246331766Sken		return OCS_HW_RTN_ERROR;
3247331766Sken	}
3248331766Sken
3249331766Sken	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &ngroup->indicator,
3250331766Sken				&ngroup->index)) {
3251331766Sken		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
3252331766Sken				ngroup->indicator);
3253331766Sken		return OCS_HW_RTN_ERROR;
3254331766Sken	}
3255331766Sken
3256331766Sken	return OCS_HW_RTN_SUCCESS;
3257331766Sken}
3258331766Sken
3259331766Skenocs_hw_rtn_e
3260331766Skenocs_hw_node_group_attach(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup, ocs_remote_node_t *rnode)
3261331766Sken{
3262331766Sken
3263331766Sken	if (!hw || !ngroup || !rnode) {
3264331766Sken		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p rnode=%p\n",
3265331766Sken			    hw, ngroup, rnode);
3266331766Sken		return OCS_HW_RTN_ERROR;
3267331766Sken	}
3268331766Sken
3269331766Sken	if (rnode->attached) {
3270331766Sken		ocs_log_err(hw->os, "node already attached RPI=%#x addr=%#x\n",
3271331766Sken			    rnode->indicator, rnode->fc_id);
3272331766Sken		return OCS_HW_RTN_ERROR;
3273331766Sken	}
3274331766Sken
3275331766Sken	if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3276331766Sken		ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3277331766Sken				rnode->indicator);
3278331766Sken		return OCS_HW_RTN_ERROR;
3279331766Sken	}
3280331766Sken
3281331766Sken	rnode->indicator = ngroup->indicator;
3282331766Sken	rnode->index = ngroup->index;
3283331766Sken
3284331766Sken	return OCS_HW_RTN_SUCCESS;
3285331766Sken}
3286331766Sken
3287331766Skenocs_hw_rtn_e
3288331766Skenocs_hw_node_group_free(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3289331766Sken{
3290331766Sken	int	ref;
3291331766Sken
3292331766Sken	if (!hw || !ngroup) {
3293331766Sken		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3294331766Sken				hw, ngroup);
3295331766Sken		return OCS_HW_RTN_ERROR;
3296331766Sken	}
3297331766Sken
3298331766Sken	ref = ocs_atomic_read(&hw->rpi_ref[ngroup->index].rpi_count);
3299331766Sken	if (ref) {
3300331766Sken		/* Hmmm, the reference count is non-zero */
3301331766Sken		ocs_log_debug(hw->os, "node group reference=%d (RPI=%#x)\n",
3302331766Sken				ref, ngroup->indicator);
3303331766Sken
3304331766Sken		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, ngroup->indicator)) {
3305331766Sken			ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3306331766Sken				    ngroup->indicator);
3307331766Sken			return OCS_HW_RTN_ERROR;
3308331766Sken		}
3309331766Sken
3310331766Sken		ocs_atomic_set(&hw->rpi_ref[ngroup->index].rpi_count, 0);
3311331766Sken	}
3312331766Sken
3313331766Sken	ngroup->indicator = UINT32_MAX;
3314331766Sken	ngroup->index = UINT32_MAX;
3315331766Sken
3316331766Sken	return OCS_HW_RTN_SUCCESS;
3317331766Sken}
3318331766Sken
3319331766Sken/**
3320331766Sken * @brief Initialize IO fields on each free call.
3321331766Sken *
3322331766Sken * @n @b Note: This is done on each free call (as opposed to each
3323331766Sken * alloc call) because port-owned XRIs are not
3324331766Sken * allocated with ocs_hw_io_alloc() but are freed with this
3325331766Sken * function.
3326331766Sken *
3327331766Sken * @param io Pointer to HW IO.
3328331766Sken */
3329331766Skenstatic inline void
3330331766Skenocs_hw_init_free_io(ocs_hw_io_t *io)
3331331766Sken{
3332331766Sken	/*
3333331766Sken	 * Set io->done to NULL, to avoid any callbacks, should
3334331766Sken	 * a completion be received for one of these IOs
3335331766Sken	 */
3336331766Sken	io->done = NULL;
3337331766Sken	io->abort_done = NULL;
3338331766Sken	io->status_saved = 0;
3339331766Sken	io->abort_in_progress = FALSE;
3340331766Sken	io->port_owned_abort_count = 0;
3341331766Sken	io->rnode = NULL;
3342331766Sken	io->type = 0xFFFF;
3343331766Sken	io->wq = NULL;
3344331766Sken	io->ul_io = NULL;
3345331766Sken	io->tgt_wqe_timeout = 0;
3346331766Sken}
3347331766Sken
3348331766Sken/**
3349331766Sken * @ingroup io
3350331766Sken * @brief Lockless allocate a HW IO object.
3351331766Sken *
3352331766Sken * @par Description
3353331766Sken * Assume that hw->ocs_lock is held. This function is only used if
3354331766Sken * use_dif_sec_xri workaround is being used.
3355331766Sken *
3356331766Sken * @param hw Hardware context.
3357331766Sken *
3358331766Sken * @return Returns a pointer to an object on success, or NULL on failure.
3359331766Sken */
3360331766Skenstatic inline ocs_hw_io_t *
3361331766Sken_ocs_hw_io_alloc(ocs_hw_t *hw)
3362331766Sken{
3363331766Sken	ocs_hw_io_t	*io = NULL;
3364331766Sken
3365331766Sken	if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
3366331766Sken		ocs_list_add_tail(&hw->io_inuse, io);
3367331766Sken		io->state = OCS_HW_IO_STATE_INUSE;
3368331766Sken		io->quarantine = FALSE;
3369331766Sken		io->quarantine_first_phase = TRUE;
3370331766Sken		io->abort_reqtag = UINT32_MAX;
3371331766Sken		ocs_ref_init(&io->ref, ocs_hw_io_free_internal, io);
3372331766Sken	} else {
3373331766Sken		ocs_atomic_add_return(&hw->io_alloc_failed_count, 1);
3374331766Sken	}
3375331766Sken
3376331766Sken	return io;
3377331766Sken}
3378331766Sken/**
3379331766Sken * @ingroup io
3380331766Sken * @brief Allocate a HW IO object.
3381331766Sken *
3382331766Sken * @par Description
3383331766Sken * @n @b Note: This function applies to non-port owned XRIs
3384331766Sken * only.
3385331766Sken *
3386331766Sken * @param hw Hardware context.
3387331766Sken *
3388331766Sken * @return Returns a pointer to an object on success, or NULL on failure.
3389331766Sken */
3390331766Skenocs_hw_io_t *
3391331766Skenocs_hw_io_alloc(ocs_hw_t *hw)
3392331766Sken{
3393331766Sken	ocs_hw_io_t	*io = NULL;
3394331766Sken
3395331766Sken	ocs_lock(&hw->io_lock);
3396331766Sken		io = _ocs_hw_io_alloc(hw);
3397331766Sken	ocs_unlock(&hw->io_lock);
3398331766Sken
3399331766Sken	return io;
3400331766Sken}
3401331766Sken
3402331766Sken/**
3403331766Sken * @ingroup io
3404331766Sken * @brief Allocate/Activate a port owned HW IO object.
3405331766Sken *
3406331766Sken * @par Description
3407331766Sken * This function is called by the transport layer when an XRI is
3408331766Sken * allocated by the SLI-Port. This will "activate" the HW IO
3409331766Sken * associated with the XRI received from the SLI-Port to mirror
3410331766Sken * the state of the XRI.
3411331766Sken * @n @n @b Note: This function applies to port owned XRIs only.
3412331766Sken *
3413331766Sken * @param hw Hardware context.
3414331766Sken * @param io Pointer HW IO to activate/allocate.
3415331766Sken *
3416331766Sken * @return Returns a pointer to an object on success, or NULL on failure.
3417331766Sken */
3418331766Skenocs_hw_io_t *
3419331766Skenocs_hw_io_activate_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
3420331766Sken{
3421331766Sken	if (ocs_ref_read_count(&io->ref) > 0) {
3422331766Sken		ocs_log_err(hw->os, "Bad parameter: refcount > 0\n");
3423331766Sken		return NULL;
3424331766Sken	}
3425331766Sken
3426331766Sken	if (io->wq != NULL) {
3427331766Sken		ocs_log_err(hw->os, "XRI %x already in use\n", io->indicator);
3428331766Sken		return NULL;
3429331766Sken	}
3430331766Sken
3431331766Sken	ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3432331766Sken	io->xbusy = TRUE;
3433331766Sken
3434331766Sken	return io;
3435331766Sken}
3436331766Sken
3437331766Sken/**
3438331766Sken * @ingroup io
3439331766Sken * @brief When an IO is freed, depending on the exchange busy flag, and other
3440331766Sken * workarounds, move it to the correct list.
3441331766Sken *
3442331766Sken * @par Description
3443331766Sken * @n @b Note: Assumes that the hw->io_lock is held and the item has been removed
3444331766Sken * from the busy or wait_free list.
3445331766Sken *
3446331766Sken * @param hw Hardware context.
3447331766Sken * @param io Pointer to the IO object to move.
3448331766Sken */
3449331766Skenstatic void
3450331766Skenocs_hw_io_free_move_correct_list(ocs_hw_t *hw, ocs_hw_io_t *io)
3451331766Sken{
3452331766Sken	if (io->xbusy) {
3453331766Sken		/* add to wait_free list and wait for XRI_ABORTED CQEs to clean up */
3454331766Sken		ocs_list_add_tail(&hw->io_wait_free, io);
3455331766Sken		io->state = OCS_HW_IO_STATE_WAIT_FREE;
3456331766Sken	} else {
3457331766Sken		/* IO not busy, add to free list */
3458331766Sken		ocs_list_add_tail(&hw->io_free, io);
3459331766Sken		io->state = OCS_HW_IO_STATE_FREE;
3460331766Sken	}
3461331766Sken
3462331766Sken	/* BZ 161832 workaround */
3463331766Sken	if (hw->workaround.use_dif_sec_xri) {
3464331766Sken		ocs_hw_check_sec_hio_list(hw);
3465331766Sken	}
3466331766Sken}
3467331766Sken
3468331766Sken/**
3469331766Sken * @ingroup io
3470331766Sken * @brief Free a HW IO object. Perform cleanup common to
3471331766Sken * port and host-owned IOs.
3472331766Sken *
3473331766Sken * @param hw Hardware context.
3474331766Sken * @param io Pointer to the HW IO object.
3475331766Sken */
3476331766Skenstatic inline void
3477331766Skenocs_hw_io_free_common(ocs_hw_t *hw, ocs_hw_io_t *io)
3478331766Sken{
3479331766Sken	/* initialize IO fields */
3480331766Sken	ocs_hw_init_free_io(io);
3481331766Sken
3482331766Sken	/* Restore default SGL */
3483331766Sken	ocs_hw_io_restore_sgl(hw, io);
3484331766Sken}
3485331766Sken
3486331766Sken/**
3487331766Sken * @ingroup io
3488331766Sken * @brief Free a HW IO object associated with a port-owned XRI.
3489331766Sken *
3490331766Sken * @param arg Pointer to the HW IO object.
3491331766Sken */
3492331766Skenstatic void
3493331766Skenocs_hw_io_free_port_owned(void *arg)
3494331766Sken{
3495331766Sken	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3496331766Sken	ocs_hw_t *hw = io->hw;
3497331766Sken
3498331766Sken	/*
3499331766Sken	 * For auto xfer rdy, if the dnrx bit is set, then add it to the list of XRIs
3500331766Sken	 * waiting for buffers.
3501331766Sken	 */
3502331766Sken	if (io->auto_xfer_rdy_dnrx) {
3503331766Sken		ocs_lock(&hw->io_lock);
3504331766Sken			/* take a reference count because we still own the IO until the buffer is posted */
3505331766Sken			ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3506331766Sken			ocs_list_add_tail(&hw->io_port_dnrx, io);
3507331766Sken		ocs_unlock(&hw->io_lock);
3508331766Sken	}
3509331766Sken
3510331766Sken	/* perform common cleanup */
3511331766Sken	ocs_hw_io_free_common(hw, io);
3512331766Sken}
3513331766Sken
3514331766Sken/**
3515331766Sken * @ingroup io
3516331766Sken * @brief Free a previously-allocated HW IO object. Called when
3517331766Sken * IO refcount goes to zero (host-owned IOs only).
3518331766Sken *
3519331766Sken * @param arg Pointer to the HW IO object.
3520331766Sken */
3521331766Skenstatic void
3522331766Skenocs_hw_io_free_internal(void *arg)
3523331766Sken{
3524331766Sken	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3525331766Sken	ocs_hw_t *hw = io->hw;
3526331766Sken
3527331766Sken	/* perform common cleanup */
3528331766Sken	ocs_hw_io_free_common(hw, io);
3529331766Sken
3530331766Sken	ocs_lock(&hw->io_lock);
3531331766Sken		/* remove from in-use list */
3532331766Sken		ocs_list_remove(&hw->io_inuse, io);
3533331766Sken		ocs_hw_io_free_move_correct_list(hw, io);
3534331766Sken	ocs_unlock(&hw->io_lock);
3535331766Sken}
3536331766Sken
3537331766Sken/**
3538331766Sken * @ingroup io
3539331766Sken * @brief Free a previously-allocated HW IO object.
3540331766Sken *
3541331766Sken * @par Description
3542331766Sken * @n @b Note: This function applies to port and host owned XRIs.
3543331766Sken *
3544331766Sken * @param hw Hardware context.
3545331766Sken * @param io Pointer to the HW IO object.
3546331766Sken *
3547331766Sken * @return Returns a non-zero value if HW IO was freed, 0 if references
3548331766Sken * on the IO still exist, or a negative value if an error occurred.
3549331766Sken */
3550331766Skenint32_t
3551331766Skenocs_hw_io_free(ocs_hw_t *hw, ocs_hw_io_t *io)
3552331766Sken{
3553331766Sken	/* just put refcount */
3554331766Sken	if (ocs_ref_read_count(&io->ref) <= 0) {
3555331766Sken		ocs_log_err(hw->os, "Bad parameter: refcount <= 0 xri=%x tag=%x\n",
3556331766Sken			    io->indicator, io->reqtag);
3557331766Sken		return -1;
3558331766Sken	}
3559331766Sken
3560331766Sken	return ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_hw_io_alloc() */
3561331766Sken}
3562331766Sken
3563331766Sken/**
3564331766Sken * @ingroup io
3565331766Sken * @brief Check if given HW IO is in-use
3566331766Sken *
3567331766Sken * @par Description
3568331766Sken * This function returns TRUE if the given HW IO has been
3569331766Sken * allocated and is in-use, and FALSE otherwise. It applies to
3570331766Sken * port and host owned XRIs.
3571331766Sken *
3572331766Sken * @param hw Hardware context.
3573331766Sken * @param io Pointer to the HW IO object.
3574331766Sken *
3575331766Sken * @return TRUE if an IO is in use, or FALSE otherwise.
3576331766Sken */
3577331766Skenuint8_t
3578331766Skenocs_hw_io_inuse(ocs_hw_t *hw, ocs_hw_io_t *io)
3579331766Sken{
3580331766Sken	return (ocs_ref_read_count(&io->ref) > 0);
3581331766Sken}
3582331766Sken
3583331766Sken/**
3584331766Sken * @brief Write a HW IO to a work queue.
3585331766Sken *
3586331766Sken * @par Description
3587331766Sken * A HW IO is written to a work queue.
3588331766Sken *
3589331766Sken * @param wq Pointer to work queue.
3590331766Sken * @param wqe Pointer to WQ entry.
3591331766Sken *
3592331766Sken * @n @b Note: Assumes the SLI-4 queue lock is held.
3593331766Sken *
3594331766Sken * @return Returns 0 on success, or a negative error code value on failure.
3595331766Sken */
3596331766Skenstatic int32_t
3597331766Sken_hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3598331766Sken{
3599331766Sken	int32_t rc;
3600331766Sken	int32_t queue_rc;
3601331766Sken
3602331766Sken	/* Every so often, set the wqec bit to generate comsummed completions */
3603331766Sken	if (wq->wqec_count) {
3604331766Sken		wq->wqec_count--;
3605331766Sken	}
3606331766Sken	if (wq->wqec_count == 0) {
3607331766Sken		sli4_generic_wqe_t *genwqe = (void*)wqe->wqebuf;
3608331766Sken		genwqe->wqec = 1;
3609331766Sken		wq->wqec_count = wq->wqec_set_count;
3610331766Sken	}
3611331766Sken
3612331766Sken	/* Decrement WQ free count */
3613331766Sken	wq->free_count--;
3614331766Sken
3615331766Sken	queue_rc = _sli_queue_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
3616331766Sken
3617331766Sken	if (queue_rc < 0) {
3618331766Sken		rc = -1;
3619331766Sken	} else {
3620331766Sken		rc = 0;
3621331766Sken		ocs_queue_history_wq(&wq->hw->q_hist, (void *) wqe->wqebuf, wq->queue->id, queue_rc);
3622331766Sken	}
3623331766Sken
3624331766Sken	return rc;
3625331766Sken}
3626331766Sken
3627331766Sken/**
3628331766Sken * @brief Write a HW IO to a work queue.
3629331766Sken *
3630331766Sken * @par Description
3631331766Sken * A HW IO is written to a work queue.
3632331766Sken *
3633331766Sken * @param wq Pointer to work queue.
3634331766Sken * @param wqe Pointer to WQE entry.
3635331766Sken *
3636331766Sken * @n @b Note: Takes the SLI-4 queue lock.
3637331766Sken *
3638331766Sken * @return Returns 0 on success, or a negative error code value on failure.
3639331766Sken */
3640331766Skenint32_t
3641331766Skenhw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3642331766Sken{
3643331766Sken	int32_t rc = 0;
3644331766Sken
3645331766Sken	sli_queue_lock(wq->queue);
3646331766Sken		if ( ! ocs_list_empty(&wq->pending_list)) {
3647331766Sken			ocs_list_add_tail(&wq->pending_list, wqe);
3648331766Sken			OCS_STAT(wq->wq_pending_count++;)
3649331766Sken			while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3650331766Sken				rc = _hw_wq_write(wq, wqe);
3651331766Sken				if (rc < 0) {
3652331766Sken					break;
3653331766Sken				}
3654331766Sken				if (wqe->abort_wqe_submit_needed) {
3655331766Sken					wqe->abort_wqe_submit_needed = 0;
3656331766Sken					sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3657331766Sken							wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT );
3658331766Sken					ocs_list_add_tail(&wq->pending_list, wqe);
3659331766Sken					OCS_STAT(wq->wq_pending_count++;)
3660331766Sken				}
3661331766Sken			}
3662331766Sken		} else {
3663331766Sken			if (wq->free_count > 0) {
3664331766Sken				rc = _hw_wq_write(wq, wqe);
3665331766Sken			} else {
3666331766Sken				ocs_list_add_tail(&wq->pending_list, wqe);
3667331766Sken				OCS_STAT(wq->wq_pending_count++;)
3668331766Sken			}
3669331766Sken		}
3670331766Sken
3671331766Sken	sli_queue_unlock(wq->queue);
3672331766Sken
3673331766Sken	return rc;
3674331766Sken
3675331766Sken}
3676331766Sken
3677331766Sken/**
3678331766Sken * @brief Update free count and submit any pending HW IOs
3679331766Sken *
3680331766Sken * @par Description
3681331766Sken * The WQ free count is updated, and any pending HW IOs are submitted that
3682331766Sken * will fit in the queue.
3683331766Sken *
3684331766Sken * @param wq Pointer to work queue.
3685331766Sken * @param update_free_count Value added to WQs free count.
3686331766Sken *
3687331766Sken * @return None.
3688331766Sken */
3689331766Skenstatic void
3690331766Skenhw_wq_submit_pending(hw_wq_t *wq, uint32_t update_free_count)
3691331766Sken{
3692331766Sken	ocs_hw_wqe_t *wqe;
3693331766Sken
3694331766Sken	sli_queue_lock(wq->queue);
3695331766Sken
3696331766Sken		/* Update free count with value passed in */
3697331766Sken		wq->free_count += update_free_count;
3698331766Sken
3699331766Sken		while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3700331766Sken			_hw_wq_write(wq, wqe);
3701331766Sken
3702331766Sken			if (wqe->abort_wqe_submit_needed) {
3703331766Sken				wqe->abort_wqe_submit_needed = 0;
3704331766Sken				sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3705331766Sken						wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT);
3706331766Sken				ocs_list_add_tail(&wq->pending_list, wqe);
3707331766Sken				OCS_STAT(wq->wq_pending_count++;)
3708331766Sken			}
3709331766Sken		}
3710331766Sken
3711331766Sken	sli_queue_unlock(wq->queue);
3712331766Sken}
3713331766Sken
3714331766Sken/**
3715331766Sken * @brief Check to see if there are any BZ 161832 workaround waiting IOs
3716331766Sken *
3717331766Sken * @par Description
3718331766Sken * Checks hw->sec_hio_wait_list, if an IO is waiting for a HW IO, then try
3719331766Sken * to allocate a secondary HW io, and dispatch it.
3720331766Sken *
3721331766Sken * @n @b Note: hw->io_lock MUST be taken when called.
3722331766Sken *
3723331766Sken * @param hw pointer to HW object
3724331766Sken *
3725331766Sken * @return none
3726331766Sken */
3727331766Skenstatic void
3728331766Skenocs_hw_check_sec_hio_list(ocs_hw_t *hw)
3729331766Sken{
3730331766Sken	ocs_hw_io_t *io;
3731331766Sken	ocs_hw_io_t *sec_io;
3732331766Sken	int rc = 0;
3733331766Sken
3734331766Sken	while (!ocs_list_empty(&hw->sec_hio_wait_list)) {
3735331766Sken		uint16_t flags;
3736331766Sken
3737331766Sken		sec_io = _ocs_hw_io_alloc(hw);
3738331766Sken		if (sec_io == NULL) {
3739331766Sken			break;
3740331766Sken		}
3741331766Sken
3742331766Sken		io = ocs_list_remove_head(&hw->sec_hio_wait_list);
3743331766Sken		ocs_list_add_tail(&hw->io_inuse, io);
3744331766Sken		io->state = OCS_HW_IO_STATE_INUSE;
3745331766Sken		io->sec_hio = sec_io;
3746331766Sken
3747331766Sken		/* mark secondary XRI for second and subsequent data phase as quarantine */
3748331766Sken		if (io->xbusy) {
3749331766Sken			sec_io->quarantine = TRUE;
3750331766Sken		}
3751331766Sken
3752331766Sken		flags = io->sec_iparam.fcp_tgt.flags;
3753331766Sken		if (io->xbusy) {
3754331766Sken			flags |= SLI4_IO_CONTINUATION;
3755331766Sken		} else {
3756331766Sken			flags &= ~SLI4_IO_CONTINUATION;
3757331766Sken		}
3758331766Sken
3759331766Sken		io->tgt_wqe_timeout = io->sec_iparam.fcp_tgt.timeout;
3760331766Sken
3761331766Sken		/* Complete (continue) TRECV IO */
3762331766Sken		if (io->xbusy) {
3763331766Sken			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3764331766Sken				io->first_data_sge,
3765331766Sken				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator, io->sec_hio->indicator,
3766331766Sken				io->reqtag, SLI4_CQ_DEFAULT,
3767331766Sken				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3768331766Sken				flags,
3769331766Sken				io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size, io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3770331766Sken					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3771331766Sken					break;
3772331766Sken			}
3773331766Sken		} else {
3774331766Sken			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3775331766Sken				io->first_data_sge,
3776331766Sken				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator,
3777331766Sken				io->reqtag, SLI4_CQ_DEFAULT,
3778331766Sken				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3779331766Sken				flags,
3780331766Sken				io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size,
3781331766Sken				io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3782331766Sken					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3783331766Sken					break;
3784331766Sken			}
3785331766Sken		}
3786331766Sken
3787331766Sken		if (io->wq == NULL) {
3788331766Sken			io->wq = ocs_hw_queue_next_wq(hw, io);
3789331766Sken			ocs_hw_assert(io->wq != NULL);
3790331766Sken		}
3791331766Sken		io->xbusy = TRUE;
3792331766Sken
3793331766Sken		/*
3794331766Sken		 * Add IO to active io wqe list before submitting, in case the
3795331766Sken		 * wcqe processing preempts this thread.
3796331766Sken		 */
3797331766Sken		ocs_hw_add_io_timed_wqe(hw, io);
3798331766Sken		rc = hw_wq_write(io->wq, &io->wqe);
3799331766Sken		if (rc >= 0) {
3800331766Sken			/* non-negative return is success */
3801331766Sken			rc = 0;
3802331766Sken		} else {
3803331766Sken			/* failed to write wqe, remove from active wqe list */
3804331766Sken			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
3805331766Sken			io->xbusy = FALSE;
3806331766Sken			ocs_hw_remove_io_timed_wqe(hw, io);
3807331766Sken		}
3808331766Sken	}
3809331766Sken}
3810331766Sken
3811331766Sken/**
3812331766Sken * @ingroup io
3813331766Sken * @brief Send a Single Request/Response Sequence (SRRS).
3814331766Sken *
3815331766Sken * @par Description
3816331766Sken * This routine supports communication sequences consisting of a single
3817331766Sken * request and single response between two endpoints. Examples include:
3818331766Sken *  - Sending an ELS request.
3819331766Sken *  - Sending an ELS response - To send an ELS reponse, the caller must provide
3820331766Sken * the OX_ID from the received request.
3821331766Sken *  - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
3822331766Sken * the caller must provide the R_CTL, TYPE, and DF_CTL
3823331766Sken * values to place in the FC frame header.
3824331766Sken *  .
3825331766Sken * @n @b Note: The caller is expected to provide both send and receive
3826331766Sken * buffers for requests. In the case of sending a response, no receive buffer
3827331766Sken * is necessary and the caller may pass in a NULL pointer.
3828331766Sken *
3829331766Sken * @param hw Hardware context.
3830331766Sken * @param type Type of sequence (ELS request/response, FC-CT).
3831331766Sken * @param io Previously-allocated HW IO object.
3832331766Sken * @param send DMA memory holding data to send (for example, ELS request, BLS response).
3833331766Sken * @param len Length, in bytes, of data to send.
3834331766Sken * @param receive Optional DMA memory to hold a response.
3835331766Sken * @param rnode Destination of data (that is, a remote node).
3836331766Sken * @param iparam IO parameters (ELS response and FC-CT).
3837331766Sken * @param cb Function call upon completion of sending the data (may be NULL).
3838331766Sken * @param arg Argument to pass to IO completion function.
3839331766Sken *
3840331766Sken * @return Returns 0 on success, or a non-zero on failure.
3841331766Sken */
3842331766Skenocs_hw_rtn_e
3843331766Skenocs_hw_srrs_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
3844331766Sken		  ocs_dma_t *send, uint32_t len, ocs_dma_t *receive,
3845331766Sken		  ocs_remote_node_t *rnode, ocs_hw_io_param_t *iparam,
3846331766Sken		  ocs_hw_srrs_cb_t cb, void *arg)
3847331766Sken{
3848331766Sken	sli4_sge_t	*sge = NULL;
3849331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3850331766Sken	uint16_t	local_flags = 0;
3851331766Sken
3852331766Sken	if (!hw || !io || !rnode || !iparam) {
3853331766Sken		ocs_log_err(NULL, "bad parm hw=%p io=%p send=%p receive=%p rnode=%p iparam=%p\n",
3854331766Sken			    hw, io, send, receive, rnode, iparam);
3855331766Sken		return OCS_HW_RTN_ERROR;
3856331766Sken	}
3857331766Sken
3858331766Sken	if (hw->state != OCS_HW_STATE_ACTIVE) {
3859331766Sken		ocs_log_test(hw->os, "cannot send SRRS, HW state=%d\n", hw->state);
3860331766Sken		return OCS_HW_RTN_ERROR;
3861331766Sken	}
3862331766Sken
3863331766Sken	if (ocs_hw_is_xri_port_owned(hw, io->indicator)) {
3864331766Sken		/* We must set the XC bit for port owned XRIs */
3865331766Sken		local_flags |= SLI4_IO_CONTINUATION;
3866331766Sken	}
3867331766Sken	io->rnode = rnode;
3868331766Sken	io->type  = type;
3869331766Sken	io->done = cb;
3870331766Sken	io->arg  = arg;
3871331766Sken
3872331766Sken	sge = io->sgl->virt;
3873331766Sken
3874331766Sken	/* clear both SGE */
3875331766Sken	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
3876331766Sken
3877331766Sken	if (send) {
3878331766Sken		sge[0].buffer_address_high = ocs_addr32_hi(send->phys);
3879331766Sken		sge[0].buffer_address_low  = ocs_addr32_lo(send->phys);
3880331766Sken		sge[0].sge_type = SLI4_SGE_TYPE_DATA;
3881331766Sken		sge[0].buffer_length = len;
3882331766Sken	}
3883331766Sken
3884331766Sken	if ((OCS_HW_ELS_REQ == type) || (OCS_HW_FC_CT == type)) {
3885331766Sken		sge[1].buffer_address_high = ocs_addr32_hi(receive->phys);
3886331766Sken		sge[1].buffer_address_low  = ocs_addr32_lo(receive->phys);
3887331766Sken		sge[1].sge_type = SLI4_SGE_TYPE_DATA;
3888331766Sken		sge[1].buffer_length = receive->size;
3889331766Sken		sge[1].last = TRUE;
3890331766Sken	} else {
3891331766Sken		sge[0].last = TRUE;
3892331766Sken	}
3893331766Sken
3894331766Sken	switch (type) {
3895331766Sken	case OCS_HW_ELS_REQ:
3896331766Sken		if ( (!send) || sli_els_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl,
3897331766Sken							*((uint8_t *)(send->virt)), /* req_type */
3898331766Sken							len, receive->size,
3899331766Sken							iparam->els.timeout, io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rnode)) {
3900331766Sken			ocs_log_err(hw->os, "REQ WQE error\n");
3901331766Sken			rc = OCS_HW_RTN_ERROR;
3902331766Sken		}
3903331766Sken		break;
3904331766Sken	case OCS_HW_ELS_RSP:
3905331766Sken		if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3906331766Sken					   io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3907331766Sken					   iparam->els.ox_id,
3908331766Sken							rnode, local_flags, UINT32_MAX)) {
3909331766Sken			ocs_log_err(hw->os, "RSP WQE error\n");
3910331766Sken			rc = OCS_HW_RTN_ERROR;
3911331766Sken		}
3912331766Sken		break;
3913331766Sken	case OCS_HW_ELS_RSP_SID:
3914331766Sken		if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3915331766Sken					   io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3916331766Sken					   iparam->els_sid.ox_id,
3917331766Sken							rnode, local_flags, iparam->els_sid.s_id)) {
3918331766Sken			ocs_log_err(hw->os, "RSP (SID) WQE error\n");
3919331766Sken			rc = OCS_HW_RTN_ERROR;
3920331766Sken		}
3921331766Sken		break;
3922331766Sken	case OCS_HW_FC_CT:
3923331766Sken		if ( (!send) || sli_gen_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3924331766Sken					  receive->size, iparam->fc_ct.timeout, io->indicator,
3925331766Sken					  io->reqtag, SLI4_CQ_DEFAULT, rnode, iparam->fc_ct.r_ctl,
3926331766Sken					  iparam->fc_ct.type, iparam->fc_ct.df_ctl)) {
3927331766Sken			ocs_log_err(hw->os, "GEN WQE error\n");
3928331766Sken			rc = OCS_HW_RTN_ERROR;
3929331766Sken		}
3930331766Sken		break;
3931331766Sken	case OCS_HW_FC_CT_RSP:
3932331766Sken		if ( (!send) || sli_xmit_sequence64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3933331766Sken					  iparam->fc_ct_rsp.timeout, iparam->fc_ct_rsp.ox_id, io->indicator,
3934331766Sken					  io->reqtag, rnode, iparam->fc_ct_rsp.r_ctl,
3935331766Sken					  iparam->fc_ct_rsp.type, iparam->fc_ct_rsp.df_ctl)) {
3936331766Sken			ocs_log_err(hw->os, "XMIT SEQ WQE error\n");
3937331766Sken			rc = OCS_HW_RTN_ERROR;
3938331766Sken		}
3939331766Sken		break;
3940331766Sken	case OCS_HW_BLS_ACC:
3941331766Sken	case OCS_HW_BLS_RJT:
3942331766Sken	{
3943331766Sken		sli_bls_payload_t	bls;
3944331766Sken
3945331766Sken		if (OCS_HW_BLS_ACC == type) {
3946331766Sken			bls.type = SLI_BLS_ACC;
3947331766Sken			ocs_memcpy(&bls.u.acc, iparam->bls.payload, sizeof(bls.u.acc));
3948331766Sken		} else {
3949331766Sken			bls.type = SLI_BLS_RJT;
3950331766Sken			ocs_memcpy(&bls.u.rjt, iparam->bls.payload, sizeof(bls.u.rjt));
3951331766Sken		}
3952331766Sken
3953331766Sken		bls.ox_id = iparam->bls.ox_id;
3954331766Sken		bls.rx_id = iparam->bls.rx_id;
3955331766Sken
3956331766Sken		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3957331766Sken					   io->indicator, io->reqtag,
3958331766Sken					   SLI4_CQ_DEFAULT,
3959331766Sken					   rnode, UINT32_MAX)) {
3960331766Sken			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
3961331766Sken			rc = OCS_HW_RTN_ERROR;
3962331766Sken		}
3963331766Sken		break;
3964331766Sken	}
3965331766Sken	case OCS_HW_BLS_ACC_SID:
3966331766Sken	{
3967331766Sken		sli_bls_payload_t	bls;
3968331766Sken
3969331766Sken		bls.type = SLI_BLS_ACC;
3970331766Sken		ocs_memcpy(&bls.u.acc, iparam->bls_sid.payload, sizeof(bls.u.acc));
3971331766Sken
3972331766Sken		bls.ox_id = iparam->bls_sid.ox_id;
3973331766Sken		bls.rx_id = iparam->bls_sid.rx_id;
3974331766Sken
3975331766Sken		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3976331766Sken					   io->indicator, io->reqtag,
3977331766Sken					   SLI4_CQ_DEFAULT,
3978331766Sken					   rnode, iparam->bls_sid.s_id)) {
3979331766Sken			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE SID error\n");
3980331766Sken			rc = OCS_HW_RTN_ERROR;
3981331766Sken		}
3982331766Sken		break;
3983331766Sken	}
3984331766Sken	case OCS_HW_BCAST:
3985331766Sken		if ( (!send) || sli_xmit_bcast64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3986331766Sken					iparam->bcast.timeout, io->indicator, io->reqtag,
3987331766Sken					SLI4_CQ_DEFAULT, rnode,
3988331766Sken					iparam->bcast.r_ctl, iparam->bcast.type, iparam->bcast.df_ctl)) {
3989331766Sken			ocs_log_err(hw->os, "XMIT_BCAST64 WQE error\n");
3990331766Sken			rc = OCS_HW_RTN_ERROR;
3991331766Sken		}
3992331766Sken		break;
3993331766Sken	default:
3994331766Sken		ocs_log_err(hw->os, "bad SRRS type %#x\n", type);
3995331766Sken		rc = OCS_HW_RTN_ERROR;
3996331766Sken	}
3997331766Sken
3998331766Sken	if (OCS_HW_RTN_SUCCESS == rc) {
3999331766Sken		if (io->wq == NULL) {
4000331766Sken			io->wq = ocs_hw_queue_next_wq(hw, io);
4001331766Sken			ocs_hw_assert(io->wq != NULL);
4002331766Sken		}
4003331766Sken		io->xbusy = TRUE;
4004331766Sken
4005331766Sken		/*
4006331766Sken		 * Add IO to active io wqe list before submitting, in case the
4007331766Sken		 * wcqe processing preempts this thread.
4008331766Sken		 */
4009331766Sken		OCS_STAT(io->wq->use_count++);
4010331766Sken		ocs_hw_add_io_timed_wqe(hw, io);
4011331766Sken		rc = hw_wq_write(io->wq, &io->wqe);
4012331766Sken		if (rc >= 0) {
4013331766Sken			/* non-negative return is success */
4014331766Sken			rc = 0;
4015331766Sken		} else {
4016331766Sken			/* failed to write wqe, remove from active wqe list */
4017331766Sken			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4018331766Sken			io->xbusy = FALSE;
4019331766Sken			ocs_hw_remove_io_timed_wqe(hw, io);
4020331766Sken		}
4021331766Sken	}
4022331766Sken
4023331766Sken	return rc;
4024331766Sken}
4025331766Sken
4026331766Sken/**
4027331766Sken * @ingroup io
4028331766Sken * @brief Send a read, write, or response IO.
4029331766Sken *
4030331766Sken * @par Description
4031331766Sken * This routine supports sending a higher-level IO (for example, FCP) between two endpoints
4032331766Sken * as a target or initiator. Examples include:
4033331766Sken *  - Sending read data and good response (target).
4034331766Sken *  - Sending a response (target with no data or after receiving write data).
4035331766Sken *  .
4036331766Sken * This routine assumes all IOs use the SGL associated with the HW IO. Prior to
4037331766Sken * calling this routine, the data should be loaded using ocs_hw_io_add_sge().
4038331766Sken *
4039331766Sken * @param hw Hardware context.
4040331766Sken * @param type Type of IO (target read, target response, and so on).
4041331766Sken * @param io Previously-allocated HW IO object.
4042331766Sken * @param len Length, in bytes, of data to send.
4043331766Sken * @param iparam IO parameters.
4044331766Sken * @param rnode Destination of data (that is, a remote node).
4045331766Sken * @param cb Function call upon completion of sending data (may be NULL).
4046331766Sken * @param arg Argument to pass to IO completion function.
4047331766Sken *
4048331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4049331766Sken *
4050331766Sken * @todo
4051331766Sken *  - Support specifiying relative offset.
4052331766Sken *  - Use a WQ other than 0.
4053331766Sken */
4054331766Skenocs_hw_rtn_e
4055331766Skenocs_hw_io_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
4056331766Sken		uint32_t len, ocs_hw_io_param_t *iparam, ocs_remote_node_t *rnode,
4057331766Sken		void *cb, void *arg)
4058331766Sken{
4059331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4060331766Sken	uint32_t	rpi;
4061331766Sken	uint8_t		send_wqe = TRUE;
4062331766Sken
4063331766Sken	CPUTRACE("");
4064331766Sken
4065331766Sken	if (!hw || !io || !rnode || !iparam) {
4066331766Sken		ocs_log_err(NULL, "bad parm hw=%p io=%p iparam=%p rnode=%p\n",
4067331766Sken			    hw, io, iparam, rnode);
4068331766Sken		return OCS_HW_RTN_ERROR;
4069331766Sken	}
4070331766Sken
4071331766Sken	if (hw->state != OCS_HW_STATE_ACTIVE) {
4072331766Sken		ocs_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
4073331766Sken		return OCS_HW_RTN_ERROR;
4074331766Sken	}
4075331766Sken
4076331766Sken	rpi = rnode->indicator;
4077331766Sken
4078331766Sken	if (hw->workaround.use_unregistered_rpi && (rpi == UINT32_MAX)) {
4079331766Sken		rpi = hw->workaround.unregistered_rid;
4080331766Sken		ocs_log_test(hw->os, "using unregistered RPI: %d\n", rpi);
4081331766Sken	}
4082331766Sken
4083331766Sken	/*
4084331766Sken	 * Save state needed during later stages
4085331766Sken	 */
4086331766Sken	io->rnode = rnode;
4087331766Sken	io->type  = type;
4088331766Sken	io->done  = cb;
4089331766Sken	io->arg   = arg;
4090331766Sken
4091331766Sken	/*
4092331766Sken	 * Format the work queue entry used to send the IO
4093331766Sken	 */
4094331766Sken	switch (type) {
4095331766Sken	case OCS_HW_IO_INITIATOR_READ:
4096331766Sken		/*
4097331766Sken		 * If use_dif_quarantine workaround is in effect, and dif_separates then mark the
4098331766Sken		 * initiator read IO for quarantine
4099331766Sken		 */
4100331766Sken		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4101331766Sken		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4102331766Sken			io->quarantine = TRUE;
4103331766Sken		}
4104331766Sken
4105331766Sken		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4106331766Sken				iparam->fcp_ini.rsp);
4107331766Sken
4108331766Sken		if (sli_fcp_iread64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge, len,
4109331766Sken					io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rpi, rnode,
4110331766Sken					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4111331766Sken					iparam->fcp_ini.timeout)) {
4112331766Sken			ocs_log_err(hw->os, "IREAD WQE error\n");
4113331766Sken			rc = OCS_HW_RTN_ERROR;
4114331766Sken		}
4115331766Sken		break;
4116331766Sken	case OCS_HW_IO_INITIATOR_WRITE:
4117331766Sken		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4118331766Sken				iparam->fcp_ini.rsp);
4119331766Sken
4120331766Sken		if (sli_fcp_iwrite64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4121331766Sken					 len, iparam->fcp_ini.first_burst,
4122331766Sken					 io->indicator, io->reqtag,
4123331766Sken					SLI4_CQ_DEFAULT, rpi, rnode,
4124331766Sken					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4125331766Sken					iparam->fcp_ini.timeout)) {
4126331766Sken			ocs_log_err(hw->os, "IWRITE WQE error\n");
4127331766Sken			rc = OCS_HW_RTN_ERROR;
4128331766Sken		}
4129331766Sken		break;
4130331766Sken	case OCS_HW_IO_INITIATOR_NODATA:
4131331766Sken		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4132331766Sken				iparam->fcp_ini.rsp);
4133331766Sken
4134331766Sken		if (sli_fcp_icmnd64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
4135331766Sken					io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
4136331766Sken					rpi, rnode, iparam->fcp_ini.timeout)) {
4137331766Sken			ocs_log_err(hw->os, "ICMND WQE error\n");
4138331766Sken			rc = OCS_HW_RTN_ERROR;
4139331766Sken		}
4140331766Sken		break;
4141331766Sken	case OCS_HW_IO_TARGET_WRITE: {
4142331766Sken		uint16_t flags = iparam->fcp_tgt.flags;
4143331766Sken		fcp_xfer_rdy_iu_t *xfer = io->xfer_rdy.virt;
4144331766Sken
4145331766Sken		/*
4146331766Sken		 * Fill in the XFER_RDY for IF_TYPE 0 devices
4147331766Sken		 */
4148331766Sken		*((uint32_t *)xfer->fcp_data_ro) = ocs_htobe32(iparam->fcp_tgt.offset);
4149331766Sken		*((uint32_t *)xfer->fcp_burst_len) = ocs_htobe32(len);
4150331766Sken		*((uint32_t *)xfer->rsvd) = 0;
4151331766Sken
4152331766Sken		if (io->xbusy) {
4153331766Sken			flags |= SLI4_IO_CONTINUATION;
4154331766Sken		} else {
4155331766Sken			flags &= ~SLI4_IO_CONTINUATION;
4156331766Sken		}
4157331766Sken
4158331766Sken		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4159331766Sken
4160331766Sken		/*
4161331766Sken		 * If use_dif_quarantine workaround is in effect, and this is a DIF enabled IO
4162331766Sken		 * then mark the target write IO for quarantine
4163331766Sken		 */
4164331766Sken		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4165331766Sken		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4166331766Sken			io->quarantine = TRUE;
4167331766Sken		}
4168331766Sken
4169331766Sken		/*
4170331766Sken		 * BZ 161832 Workaround:
4171331766Sken		 * Check for use_dif_sec_xri workaround.  Note, even though the first dataphase
4172331766Sken		 * doesn't really need a secondary XRI, we allocate one anyway, as this avoids the
4173331766Sken		 * potential for deadlock where all XRI's are allocated as primaries to IOs that
4174331766Sken		 * are on hw->sec_hio_wait_list.   If this secondary XRI is not for the first
4175331766Sken		 * data phase, it is marked for quarantine.
4176331766Sken		 */
4177331766Sken		if (hw->workaround.use_dif_sec_xri && (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4178331766Sken
4179331766Sken			/*
4180331766Sken			 * If we have allocated a chained SGL for skyhawk, then
4181331766Sken			 * we can re-use this for the sec_hio.
4182331766Sken			 */
4183331766Sken			if (io->ovfl_io != NULL) {
4184331766Sken				io->sec_hio = io->ovfl_io;
4185331766Sken				io->sec_hio->quarantine = TRUE;
4186331766Sken			} else {
4187331766Sken				io->sec_hio = ocs_hw_io_alloc(hw);
4188331766Sken			}
4189331766Sken			if (io->sec_hio == NULL) {
4190331766Sken				/* Failed to allocate, so save full request context and put
4191331766Sken				 * this IO on the wait list
4192331766Sken				 */
4193331766Sken				io->sec_iparam = *iparam;
4194331766Sken				io->sec_len = len;
4195331766Sken				ocs_lock(&hw->io_lock);
4196331766Sken					ocs_list_remove(&hw->io_inuse,  io);
4197331766Sken					ocs_list_add_tail(&hw->sec_hio_wait_list, io);
4198331766Sken					io->state = OCS_HW_IO_STATE_WAIT_SEC_HIO;
4199331766Sken					hw->sec_hio_wait_count++;
4200331766Sken				ocs_unlock(&hw->io_lock);
4201331766Sken				send_wqe = FALSE;
4202331766Sken				/* Done */
4203331766Sken				break;
4204331766Sken			}
4205331766Sken			/* We quarantine the secondary IO if this is the second or subsequent data phase */
4206331766Sken			if (io->xbusy) {
4207331766Sken				io->sec_hio->quarantine = TRUE;
4208331766Sken			}
4209331766Sken		}
4210331766Sken
4211331766Sken		/*
4212331766Sken		 * If not the first data phase, and io->sec_hio has been allocated, then issue
4213331766Sken		 * FCP_CONT_TRECEIVE64 WQE, otherwise use the usual FCP_TRECEIVE64 WQE
4214331766Sken		 */
4215331766Sken		if (io->xbusy && (io->sec_hio != NULL)) {
4216331766Sken			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4217331766Sken						   iparam->fcp_tgt.offset, len, io->indicator, io->sec_hio->indicator,
4218331766Sken						   io->reqtag, SLI4_CQ_DEFAULT,
4219331766Sken						   iparam->fcp_tgt.ox_id, rpi, rnode,
4220331766Sken						   flags,
4221331766Sken						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4222331766Sken						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4223331766Sken				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4224331766Sken				rc = OCS_HW_RTN_ERROR;
4225331766Sken			}
4226331766Sken		} else {
4227331766Sken			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4228331766Sken						   iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4229331766Sken						   SLI4_CQ_DEFAULT,
4230331766Sken						   iparam->fcp_tgt.ox_id, rpi, rnode,
4231331766Sken						   flags,
4232331766Sken						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4233331766Sken						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4234331766Sken				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4235331766Sken				rc = OCS_HW_RTN_ERROR;
4236331766Sken			}
4237331766Sken		}
4238331766Sken		break;
4239331766Sken	}
4240331766Sken	case OCS_HW_IO_TARGET_READ: {
4241331766Sken		uint16_t flags = iparam->fcp_tgt.flags;
4242331766Sken
4243331766Sken		if (io->xbusy) {
4244331766Sken			flags |= SLI4_IO_CONTINUATION;
4245331766Sken		} else {
4246331766Sken			flags &= ~SLI4_IO_CONTINUATION;
4247331766Sken		}
4248331766Sken
4249331766Sken		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4250331766Sken		if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4251331766Sken					iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4252331766Sken					SLI4_CQ_DEFAULT,
4253331766Sken					iparam->fcp_tgt.ox_id, rpi, rnode,
4254331766Sken					flags,
4255331766Sken					iparam->fcp_tgt.dif_oper,
4256331766Sken					iparam->fcp_tgt.blk_size,
4257331766Sken					iparam->fcp_tgt.cs_ctl,
4258331766Sken					iparam->fcp_tgt.app_id)) {
4259331766Sken			ocs_log_err(hw->os, "TSEND WQE error\n");
4260331766Sken			rc = OCS_HW_RTN_ERROR;
4261331766Sken		} else if (hw->workaround.retain_tsend_io_length) {
4262331766Sken			io->length = len;
4263331766Sken		}
4264331766Sken		break;
4265331766Sken	}
4266331766Sken	case OCS_HW_IO_TARGET_RSP: {
4267331766Sken		uint16_t flags = iparam->fcp_tgt.flags;
4268331766Sken
4269331766Sken		if (io->xbusy) {
4270331766Sken			flags |= SLI4_IO_CONTINUATION;
4271331766Sken		} else {
4272331766Sken			flags &= ~SLI4_IO_CONTINUATION;
4273331766Sken		}
4274331766Sken
4275331766Sken		/* post a new auto xfer ready buffer */
4276331766Sken		if (hw->auto_xfer_rdy_enabled && io->is_port_owned) {
4277331766Sken			if ((io->auto_xfer_rdy_dnrx = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1))) {
4278331766Sken				flags |= SLI4_IO_DNRX;
4279331766Sken			}
4280331766Sken		}
4281331766Sken
4282331766Sken		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4283331766Sken		if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size,
4284331766Sken					&io->def_sgl,
4285331766Sken					len,
4286331766Sken					io->indicator, io->reqtag,
4287331766Sken					SLI4_CQ_DEFAULT,
4288331766Sken					iparam->fcp_tgt.ox_id,
4289331766Sken					rpi, rnode,
4290331766Sken					flags, iparam->fcp_tgt.cs_ctl,
4291331766Sken					io->is_port_owned,
4292331766Sken					iparam->fcp_tgt.app_id)) {
4293331766Sken			ocs_log_err(hw->os, "TRSP WQE error\n");
4294331766Sken			rc = OCS_HW_RTN_ERROR;
4295331766Sken		}
4296331766Sken
4297331766Sken		break;
4298331766Sken	}
4299331766Sken	default:
4300331766Sken		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4301331766Sken		rc = OCS_HW_RTN_ERROR;
4302331766Sken	}
4303331766Sken
4304331766Sken	if (send_wqe && (OCS_HW_RTN_SUCCESS == rc)) {
4305331766Sken		if (io->wq == NULL) {
4306331766Sken			io->wq = ocs_hw_queue_next_wq(hw, io);
4307331766Sken			ocs_hw_assert(io->wq != NULL);
4308331766Sken		}
4309331766Sken
4310331766Sken		io->xbusy = TRUE;
4311331766Sken
4312331766Sken		/*
4313331766Sken		 * Add IO to active io wqe list before submitting, in case the
4314331766Sken		 * wcqe processing preempts this thread.
4315331766Sken		 */
4316331766Sken		OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
4317331766Sken		OCS_STAT(io->wq->use_count++);
4318331766Sken		ocs_hw_add_io_timed_wqe(hw, io);
4319331766Sken		rc = hw_wq_write(io->wq, &io->wqe);
4320331766Sken		if (rc >= 0) {
4321331766Sken			/* non-negative return is success */
4322331766Sken			rc = 0;
4323331766Sken		} else {
4324331766Sken			/* failed to write wqe, remove from active wqe list */
4325331766Sken			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4326331766Sken			io->xbusy = FALSE;
4327331766Sken			ocs_hw_remove_io_timed_wqe(hw, io);
4328331766Sken		}
4329331766Sken	}
4330331766Sken
4331331766Sken	return rc;
4332331766Sken}
4333331766Sken
4334331766Sken/**
4335331766Sken * @brief Send a raw frame
4336331766Sken *
4337331766Sken * @par Description
4338331766Sken * Using the SEND_FRAME_WQE, a frame consisting of header and payload is sent.
4339331766Sken *
4340331766Sken * @param hw Pointer to HW object.
4341331766Sken * @param hdr Pointer to a little endian formatted FC header.
4342331766Sken * @param sof Value to use as the frame SOF.
4343331766Sken * @param eof Value to use as the frame EOF.
4344331766Sken * @param payload Pointer to payload DMA buffer.
4345331766Sken * @param ctx Pointer to caller provided send frame context.
4346331766Sken * @param callback Callback function.
4347331766Sken * @param arg Callback function argument.
4348331766Sken *
4349331766Sken * @return Returns 0 on success, or a negative error code value on failure.
4350331766Sken */
4351331766Skenocs_hw_rtn_e
4352331766Skenocs_hw_send_frame(ocs_hw_t *hw, fc_header_le_t *hdr, uint8_t sof, uint8_t eof, ocs_dma_t *payload,
4353331766Sken		   ocs_hw_send_frame_context_t *ctx, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
4354331766Sken{
4355331766Sken	int32_t rc;
4356331766Sken	ocs_hw_wqe_t *wqe;
4357331766Sken	uint32_t xri;
4358331766Sken	hw_wq_t *wq;
4359331766Sken
4360331766Sken	wqe = &ctx->wqe;
4361331766Sken
4362331766Sken	/* populate the callback object */
4363331766Sken	ctx->hw = hw;
4364331766Sken
4365331766Sken	/* Fetch and populate request tag */
4366331766Sken	ctx->wqcb = ocs_hw_reqtag_alloc(hw, callback, arg);
4367331766Sken	if (ctx->wqcb == NULL) {
4368331766Sken		ocs_log_err(hw->os, "can't allocate request tag\n");
4369331766Sken		return OCS_HW_RTN_NO_RESOURCES;
4370331766Sken	}
4371331766Sken
4372331766Sken	/* Choose a work queue, first look for a class[1] wq, otherwise just use wq[0] */
4373331766Sken	wq = ocs_varray_iter_next(hw->wq_class_array[1]);
4374331766Sken	if (wq == NULL) {
4375331766Sken		wq = hw->hw_wq[0];
4376331766Sken	}
4377331766Sken
4378331766Sken	/* Set XRI and RX_ID in the header based on which WQ, and which send_frame_io we are using */
4379331766Sken	xri = wq->send_frame_io->indicator;
4380331766Sken
4381331766Sken	/* Build the send frame WQE */
4382331766Sken	rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, hw->sli.config.wqe_size, sof, eof, (uint32_t*) hdr, payload,
4383331766Sken				payload->len, OCS_HW_SEND_FRAME_TIMEOUT, xri, ctx->wqcb->instance_index);
4384331766Sken	if (rc) {
4385331766Sken		ocs_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
4386331766Sken		return OCS_HW_RTN_ERROR;
4387331766Sken	}
4388331766Sken
4389331766Sken	/* Write to WQ */
4390331766Sken	rc = hw_wq_write(wq, wqe);
4391331766Sken	if (rc) {
4392331766Sken		ocs_log_err(hw->os, "hw_wq_write failed: %d\n", rc);
4393331766Sken		return OCS_HW_RTN_ERROR;
4394331766Sken	}
4395331766Sken
4396331766Sken	OCS_STAT(wq->use_count++);
4397331766Sken
4398331766Sken	return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
4399331766Sken}
4400331766Sken
4401331766Skenocs_hw_rtn_e
4402331766Skenocs_hw_io_register_sgl(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *sgl, uint32_t sgl_count)
4403331766Sken{
4404331766Sken	if (sli_get_sgl_preregister(&hw->sli)) {
4405331766Sken		ocs_log_err(hw->os, "can't use temporary SGL with pre-registered SGLs\n");
4406331766Sken		return OCS_HW_RTN_ERROR;
4407331766Sken	}
4408331766Sken	io->ovfl_sgl = sgl;
4409331766Sken	io->ovfl_sgl_count = sgl_count;
4410331766Sken	io->ovfl_io = NULL;
4411331766Sken
4412331766Sken	return OCS_HW_RTN_SUCCESS;
4413331766Sken}
4414331766Sken
4415331766Skenstatic void
4416331766Skenocs_hw_io_restore_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4417331766Sken{
4418331766Sken	/* Restore the default */
4419331766Sken	io->sgl = &io->def_sgl;
4420331766Sken	io->sgl_count = io->def_sgl_count;
4421331766Sken
4422331766Sken	/*
4423331766Sken	 * For skyhawk, we need to free the IO allocated for the chained
4424331766Sken	 * SGL. For all devices, clear the overflow fields on the IO.
4425331766Sken	 *
4426331766Sken	 * Note: For DIF IOs, we may be using the same XRI for the sec_hio and
4427331766Sken	 *       the chained SGLs. If so, then we clear the ovfl_io field
4428331766Sken	 *       when the sec_hio is freed.
4429331766Sken	 */
4430331766Sken	if (io->ovfl_io != NULL) {
4431331766Sken		ocs_hw_io_free(hw, io->ovfl_io);
4432331766Sken		io->ovfl_io = NULL;
4433331766Sken	}
4434331766Sken
4435331766Sken	/* Clear the overflow SGL */
4436331766Sken	io->ovfl_sgl = NULL;
4437331766Sken	io->ovfl_sgl_count = 0;
4438331766Sken	io->ovfl_lsp = NULL;
4439331766Sken}
4440331766Sken
4441331766Sken/**
4442331766Sken * @ingroup io
4443331766Sken * @brief Initialize the scatter gather list entries of an IO.
4444331766Sken *
4445331766Sken * @param hw Hardware context.
4446331766Sken * @param io Previously-allocated HW IO object.
4447331766Sken * @param type Type of IO (target read, target response, and so on).
4448331766Sken *
4449331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4450331766Sken */
4451331766Skenocs_hw_rtn_e
4452331766Skenocs_hw_io_init_sges(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_io_type_e type)
4453331766Sken{
4454331766Sken	sli4_sge_t	*data = NULL;
4455331766Sken	uint32_t	i = 0;
4456331766Sken	uint32_t	skips = 0;
4457331766Sken
4458331766Sken	if (!hw || !io) {
4459331766Sken		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p\n",
4460331766Sken			    hw, io);
4461331766Sken		return OCS_HW_RTN_ERROR;
4462331766Sken	}
4463331766Sken
4464331766Sken	/* Clear / reset the scatter-gather list */
4465331766Sken	io->sgl = &io->def_sgl;
4466331766Sken	io->sgl_count = io->def_sgl_count;
4467331766Sken	io->first_data_sge = 0;
4468331766Sken
4469331766Sken	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
4470331766Sken	io->n_sge = 0;
4471331766Sken	io->sge_offset = 0;
4472331766Sken
4473331766Sken	io->type = type;
4474331766Sken
4475331766Sken	data = io->sgl->virt;
4476331766Sken
4477331766Sken	/*
4478331766Sken	 * Some IO types have underlying hardware requirements on the order
4479331766Sken	 * of SGEs. Process all special entries here.
4480331766Sken	 */
4481331766Sken	switch (type) {
4482331766Sken	case OCS_HW_IO_INITIATOR_READ:
4483331766Sken	case OCS_HW_IO_INITIATOR_WRITE:
4484331766Sken	case OCS_HW_IO_INITIATOR_NODATA:
4485331766Sken		/*
4486331766Sken		 * No skips, 2 special for initiator I/Os
4487331766Sken		 * The addresses and length are written later
4488331766Sken		 */
4489331766Sken		/* setup command pointer */
4490331766Sken		data->sge_type = SLI4_SGE_TYPE_DATA;
4491331766Sken		data++;
4492331766Sken
4493331766Sken		/* setup response pointer */
4494331766Sken		data->sge_type = SLI4_SGE_TYPE_DATA;
4495331766Sken
4496331766Sken		if (OCS_HW_IO_INITIATOR_NODATA == type) {
4497331766Sken			data->last = TRUE;
4498331766Sken		}
4499331766Sken		data++;
4500331766Sken
4501331766Sken		io->n_sge = 2;
4502331766Sken		break;
4503331766Sken	case OCS_HW_IO_TARGET_WRITE:
4504331766Sken#define OCS_TARGET_WRITE_SKIPS	2
4505331766Sken		skips = OCS_TARGET_WRITE_SKIPS;
4506331766Sken
4507331766Sken		/* populate host resident XFER_RDY buffer */
4508331766Sken		data->sge_type = SLI4_SGE_TYPE_DATA;
4509331766Sken		data->buffer_address_high = ocs_addr32_hi(io->xfer_rdy.phys);
4510331766Sken		data->buffer_address_low  = ocs_addr32_lo(io->xfer_rdy.phys);
4511331766Sken		data->buffer_length = io->xfer_rdy.size;
4512331766Sken		data++;
4513331766Sken
4514331766Sken		skips--;
4515331766Sken
4516331766Sken		io->n_sge = 1;
4517331766Sken		break;
4518331766Sken	case OCS_HW_IO_TARGET_READ:
4519331766Sken		/*
4520331766Sken		 * For FCP_TSEND64, the first 2 entries are SKIP SGE's
4521331766Sken		 */
4522331766Sken#define OCS_TARGET_READ_SKIPS	2
4523331766Sken		skips = OCS_TARGET_READ_SKIPS;
4524331766Sken		break;
4525331766Sken	case OCS_HW_IO_TARGET_RSP:
4526331766Sken		/*
4527331766Sken		 * No skips, etc. for FCP_TRSP64
4528331766Sken		 */
4529331766Sken		break;
4530331766Sken	default:
4531331766Sken		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4532331766Sken		return OCS_HW_RTN_ERROR;
4533331766Sken	}
4534331766Sken
4535331766Sken	/*
4536331766Sken	 * Write skip entries
4537331766Sken	 */
4538331766Sken	for (i = 0; i < skips; i++) {
4539331766Sken		data->sge_type = SLI4_SGE_TYPE_SKIP;
4540331766Sken		data++;
4541331766Sken	}
4542331766Sken
4543331766Sken	io->n_sge += skips;
4544331766Sken
4545331766Sken	/*
4546331766Sken	 * Set last
4547331766Sken	 */
4548331766Sken	data->last = TRUE;
4549331766Sken
4550331766Sken	return OCS_HW_RTN_SUCCESS;
4551331766Sken}
4552331766Sken
4553331766Sken/**
4554331766Sken * @ingroup io
4555331766Sken * @brief Add a T10 PI seed scatter gather list entry.
4556331766Sken *
4557331766Sken * @param hw Hardware context.
4558331766Sken * @param io Previously-allocated HW IO object.
4559331766Sken * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
4560331766Sken *
4561331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4562331766Sken */
4563331766Skenocs_hw_rtn_e
4564331766Skenocs_hw_io_add_seed_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_dif_info_t *dif_info)
4565331766Sken{
4566331766Sken	sli4_sge_t	*data = NULL;
4567331766Sken	sli4_diseed_sge_t *dif_seed;
4568331766Sken
4569331766Sken	/* If no dif_info, or dif_oper is disabled, then just return success */
4570331766Sken	if ((dif_info == NULL) || (dif_info->dif_oper == OCS_HW_DIF_OPER_DISABLED)) {
4571331766Sken		return OCS_HW_RTN_SUCCESS;
4572331766Sken	}
4573331766Sken
4574331766Sken	if (!hw || !io) {
4575331766Sken		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p dif_info=%p\n",
4576331766Sken			    hw, io, dif_info);
4577331766Sken		return OCS_HW_RTN_ERROR;
4578331766Sken	}
4579331766Sken
4580331766Sken	data = io->sgl->virt;
4581331766Sken	data += io->n_sge;
4582331766Sken
4583331766Sken	/* If we are doing T10 DIF add the DIF Seed SGE */
4584331766Sken	ocs_memset(data, 0, sizeof(sli4_diseed_sge_t));
4585331766Sken	dif_seed = (sli4_diseed_sge_t *)data;
4586331766Sken	dif_seed->ref_tag_cmp = dif_info->ref_tag_cmp;
4587331766Sken	dif_seed->ref_tag_repl = dif_info->ref_tag_repl;
4588331766Sken	dif_seed->app_tag_repl = dif_info->app_tag_repl;
4589331766Sken	dif_seed->repl_app_tag = dif_info->repl_app_tag;
4590331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) {
4591331766Sken		dif_seed->atrt = dif_info->disable_app_ref_ffff;
4592331766Sken		dif_seed->at = dif_info->disable_app_ffff;
4593331766Sken	}
4594331766Sken	dif_seed->sge_type = SLI4_SGE_TYPE_DISEED;
4595331766Sken	/* Workaround for SKH (BZ157233) */
4596331766Sken	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4597331766Sken		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) && dif_info->dif_separate) {
4598331766Sken		dif_seed->sge_type = SLI4_SGE_TYPE_SKIP;
4599331766Sken	}
4600331766Sken
4601331766Sken	dif_seed->app_tag_cmp = dif_info->app_tag_cmp;
4602331766Sken	dif_seed->dif_blk_size = dif_info->blk_size;
4603331766Sken	dif_seed->auto_incr_ref_tag = dif_info->auto_incr_ref_tag;
4604331766Sken	dif_seed->check_app_tag = dif_info->check_app_tag;
4605331766Sken	dif_seed->check_ref_tag = dif_info->check_ref_tag;
4606331766Sken	dif_seed->check_crc = dif_info->check_guard;
4607331766Sken	dif_seed->new_ref_tag = dif_info->repl_ref_tag;
4608331766Sken
4609331766Sken	switch(dif_info->dif_oper) {
4610331766Sken	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
4611331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4612331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4613331766Sken		break;
4614331766Sken	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
4615331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4616331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4617331766Sken		break;
4618331766Sken	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM:
4619331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4620331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4621331766Sken		break;
4622331766Sken	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF:
4623331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4624331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4625331766Sken		break;
4626331766Sken	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
4627331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4628331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4629331766Sken		break;
4630331766Sken	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM:
4631331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4632331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4633331766Sken		break;
4634331766Sken	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
4635331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4636331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4637331766Sken		break;
4638331766Sken	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
4639331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4640331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4641331766Sken		break;
4642331766Sken	case OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW:
4643331766Sken		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4644331766Sken		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4645331766Sken		break;
4646331766Sken	default:
4647331766Sken		ocs_log_err(hw->os, "unsupported DIF operation %#x\n",
4648331766Sken			    dif_info->dif_oper);
4649331766Sken		return OCS_HW_RTN_ERROR;
4650331766Sken	}
4651331766Sken
4652331766Sken	/*
4653331766Sken	 * Set last, clear previous last
4654331766Sken	 */
4655331766Sken	data->last = TRUE;
4656331766Sken	if (io->n_sge) {
4657331766Sken		data[-1].last = FALSE;
4658331766Sken	}
4659331766Sken
4660331766Sken	io->n_sge++;
4661331766Sken
4662331766Sken	return OCS_HW_RTN_SUCCESS;
4663331766Sken}
4664331766Sken
4665331766Skenstatic ocs_hw_rtn_e
4666331766Skenocs_hw_io_overflow_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4667331766Sken{
4668331766Sken	sli4_lsp_sge_t *lsp;
4669331766Sken
4670331766Sken	/* fail if we're already pointing to the overflow SGL */
4671331766Sken	if (io->sgl == io->ovfl_sgl) {
4672331766Sken		return OCS_HW_RTN_ERROR;
4673331766Sken	}
4674331766Sken
4675331766Sken	/*
4676331766Sken	 * For skyhawk, we can use another SGL to extend the SGL list. The
4677331766Sken	 * Chained entry must not be in the first 4 entries.
4678331766Sken	 *
4679331766Sken	 * Note: For DIF enabled IOs, we will use the ovfl_io for the sec_hio.
4680331766Sken	 */
4681331766Sken	if (sli_get_sgl_preregister(&hw->sli) &&
4682331766Sken	    io->def_sgl_count > 4 &&
4683331766Sken	    io->ovfl_io == NULL &&
4684331766Sken	    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4685331766Sken		(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
4686331766Sken		io->ovfl_io = ocs_hw_io_alloc(hw);
4687331766Sken		if (io->ovfl_io != NULL) {
4688331766Sken			/*
4689331766Sken			 * Note: We can't call ocs_hw_io_register_sgl() here
4690331766Sken			 * because it checks that SGLs are not pre-registered
4691331766Sken			 * and for shyhawk, preregistered SGLs are required.
4692331766Sken			 */
4693331766Sken			io->ovfl_sgl = &io->ovfl_io->def_sgl;
4694331766Sken			io->ovfl_sgl_count = io->ovfl_io->def_sgl_count;
4695331766Sken		}
4696331766Sken	}
4697331766Sken
4698331766Sken	/* fail if we don't have an overflow SGL registered */
4699331766Sken	if (io->ovfl_sgl == NULL) {
4700331766Sken		return OCS_HW_RTN_ERROR;
4701331766Sken	}
4702331766Sken
4703331766Sken	/*
4704331766Sken	 * Overflow, we need to put a link SGE in the last location of the current SGL, after
4705331766Sken	 * copying the the last SGE to the overflow SGL
4706331766Sken	 */
4707331766Sken
4708331766Sken	((sli4_sge_t*)io->ovfl_sgl->virt)[0] = ((sli4_sge_t*)io->sgl->virt)[io->n_sge - 1];
4709331766Sken
4710331766Sken	lsp = &((sli4_lsp_sge_t*)io->sgl->virt)[io->n_sge - 1];
4711331766Sken	ocs_memset(lsp, 0, sizeof(*lsp));
4712331766Sken
4713331766Sken	if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4714331766Sken	    (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
4715331766Sken		sli_skh_chain_sge_build(&hw->sli,
4716331766Sken					(sli4_sge_t*)lsp,
4717331766Sken					io->ovfl_io->indicator,
4718331766Sken					0, /* frag_num */
4719331766Sken					0); /* offset */
4720331766Sken	} else {
4721331766Sken		lsp->buffer_address_high = ocs_addr32_hi(io->ovfl_sgl->phys);
4722331766Sken		lsp->buffer_address_low  = ocs_addr32_lo(io->ovfl_sgl->phys);
4723331766Sken		lsp->sge_type = SLI4_SGE_TYPE_LSP;
4724331766Sken		lsp->last = 0;
4725331766Sken		io->ovfl_lsp = lsp;
4726331766Sken		io->ovfl_lsp->segment_length = sizeof(sli4_sge_t);
4727331766Sken	}
4728331766Sken
4729331766Sken	/* Update the current SGL pointer, and n_sgl */
4730331766Sken	io->sgl = io->ovfl_sgl;
4731331766Sken	io->sgl_count = io->ovfl_sgl_count;
4732331766Sken	io->n_sge = 1;
4733331766Sken
4734331766Sken	return OCS_HW_RTN_SUCCESS;
4735331766Sken}
4736331766Sken
4737331766Sken/**
4738331766Sken * @ingroup io
4739331766Sken * @brief Add a scatter gather list entry to an IO.
4740331766Sken *
4741331766Sken * @param hw Hardware context.
4742331766Sken * @param io Previously-allocated HW IO object.
4743331766Sken * @param addr Physical address.
4744331766Sken * @param length Length of memory pointed to by @c addr.
4745331766Sken *
4746331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4747331766Sken */
4748331766Skenocs_hw_rtn_e
4749331766Skenocs_hw_io_add_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr, uint32_t length)
4750331766Sken{
4751331766Sken	sli4_sge_t	*data = NULL;
4752331766Sken
4753331766Sken	if (!hw || !io || !addr || !length) {
4754331766Sken		ocs_log_err(hw ? hw->os : NULL,
4755331766Sken			    "bad parameter hw=%p io=%p addr=%lx length=%u\n",
4756331766Sken			    hw, io, addr, length);
4757331766Sken		return OCS_HW_RTN_ERROR;
4758331766Sken	}
4759331766Sken
4760331766Sken	if ((length != 0) && (io->n_sge + 1) > io->sgl_count) {
4761331766Sken		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_SUCCESS) {
4762331766Sken			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4763331766Sken			return OCS_HW_RTN_ERROR;
4764331766Sken		}
4765331766Sken	}
4766331766Sken
4767331766Sken	if (length > sli_get_max_sge(&hw->sli)) {
4768331766Sken		ocs_log_err(hw->os, "length of SGE %d bigger than allowed %d\n",
4769331766Sken			    length, sli_get_max_sge(&hw->sli));
4770331766Sken		return OCS_HW_RTN_ERROR;
4771331766Sken	}
4772331766Sken
4773331766Sken	data = io->sgl->virt;
4774331766Sken	data += io->n_sge;
4775331766Sken
4776331766Sken	data->sge_type = SLI4_SGE_TYPE_DATA;
4777331766Sken	data->buffer_address_high = ocs_addr32_hi(addr);
4778331766Sken	data->buffer_address_low  = ocs_addr32_lo(addr);
4779331766Sken	data->buffer_length = length;
4780331766Sken	data->data_offset = io->sge_offset;
4781331766Sken	/*
4782331766Sken	 * Always assume this is the last entry and mark as such.
4783331766Sken	 * If this is not the first entry unset the "last SGE"
4784331766Sken	 * indication for the previous entry
4785331766Sken	 */
4786331766Sken	data->last = TRUE;
4787331766Sken	if (io->n_sge) {
4788331766Sken		data[-1].last = FALSE;
4789331766Sken	}
4790331766Sken
4791331766Sken	/* Set first_data_bde if not previously set */
4792331766Sken	if (io->first_data_sge == 0) {
4793331766Sken		io->first_data_sge = io->n_sge;
4794331766Sken	}
4795331766Sken
4796331766Sken	io->sge_offset += length;
4797331766Sken	io->n_sge++;
4798331766Sken
4799331766Sken	/* Update the linked segment length (only executed after overflow has begun) */
4800331766Sken	if (io->ovfl_lsp != NULL) {
4801331766Sken		io->ovfl_lsp->segment_length = io->n_sge * sizeof(sli4_sge_t);
4802331766Sken	}
4803331766Sken
4804331766Sken	return OCS_HW_RTN_SUCCESS;
4805331766Sken}
4806331766Sken
4807331766Sken/**
4808331766Sken * @ingroup io
4809331766Sken * @brief Add a T10 DIF scatter gather list entry to an IO.
4810331766Sken *
4811331766Sken * @param hw Hardware context.
4812331766Sken * @param io Previously-allocated HW IO object.
4813331766Sken * @param addr DIF physical address.
4814331766Sken *
4815331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4816331766Sken */
4817331766Skenocs_hw_rtn_e
4818331766Skenocs_hw_io_add_dif_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr)
4819331766Sken{
4820331766Sken	sli4_dif_sge_t	*data = NULL;
4821331766Sken
4822331766Sken	if (!hw || !io || !addr) {
4823331766Sken		ocs_log_err(hw ? hw->os : NULL,
4824331766Sken			    "bad parameter hw=%p io=%p addr=%lx\n",
4825331766Sken			    hw, io, addr);
4826331766Sken		return OCS_HW_RTN_ERROR;
4827331766Sken	}
4828331766Sken
4829331766Sken	if ((io->n_sge + 1) > hw->config.n_sgl) {
4830331766Sken		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_ERROR) {
4831331766Sken			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4832331766Sken			return OCS_HW_RTN_ERROR;
4833331766Sken		}
4834331766Sken	}
4835331766Sken
4836331766Sken	data = io->sgl->virt;
4837331766Sken	data += io->n_sge;
4838331766Sken
4839331766Sken	data->sge_type = SLI4_SGE_TYPE_DIF;
4840331766Sken	/* Workaround for SKH (BZ157233) */
4841331766Sken	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4842331766Sken		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type)) {
4843331766Sken		data->sge_type = SLI4_SGE_TYPE_SKIP;
4844331766Sken	}
4845331766Sken
4846331766Sken	data->buffer_address_high = ocs_addr32_hi(addr);
4847331766Sken	data->buffer_address_low  = ocs_addr32_lo(addr);
4848331766Sken
4849331766Sken	/*
4850331766Sken	 * Always assume this is the last entry and mark as such.
4851331766Sken	 * If this is not the first entry unset the "last SGE"
4852331766Sken	 * indication for the previous entry
4853331766Sken	 */
4854331766Sken	data->last = TRUE;
4855331766Sken	if (io->n_sge) {
4856331766Sken		data[-1].last = FALSE;
4857331766Sken	}
4858331766Sken
4859331766Sken	io->n_sge++;
4860331766Sken
4861331766Sken	return OCS_HW_RTN_SUCCESS;
4862331766Sken}
4863331766Sken
4864331766Sken/**
4865331766Sken * @ingroup io
4866331766Sken * @brief Abort a previously-started IO.
4867331766Sken *
4868331766Sken * @param hw Hardware context.
4869331766Sken * @param io_to_abort The IO to abort.
4870331766Sken * @param send_abts Boolean to have the hardware automatically
4871331766Sken * generate an ABTS.
4872331766Sken * @param cb Function call upon completion of the abort (may be NULL).
4873331766Sken * @param arg Argument to pass to abort completion function.
4874331766Sken *
4875331766Sken * @return Returns 0 on success, or a non-zero value on failure.
4876331766Sken */
4877331766Skenocs_hw_rtn_e
4878331766Skenocs_hw_io_abort(ocs_hw_t *hw, ocs_hw_io_t *io_to_abort, uint32_t send_abts, void *cb, void *arg)
4879331766Sken{
4880331766Sken	sli4_abort_type_e atype = SLI_ABORT_MAX;
4881331766Sken	uint32_t	id = 0, mask = 0;
4882331766Sken	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4883331766Sken	hw_wq_callback_t *wqcb;
4884331766Sken
4885331766Sken	if (!hw || !io_to_abort) {
4886331766Sken		ocs_log_err(hw ? hw->os : NULL,
4887331766Sken			    "bad parameter hw=%p io=%p\n",
4888331766Sken			    hw, io_to_abort);
4889331766Sken		return OCS_HW_RTN_ERROR;
4890331766Sken	}
4891331766Sken
4892331766Sken	if (hw->state != OCS_HW_STATE_ACTIVE) {
4893331766Sken		ocs_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
4894331766Sken			    hw->state);
4895331766Sken		return OCS_HW_RTN_ERROR;
4896331766Sken	}
4897331766Sken
4898331766Sken	/* take a reference on IO being aborted */
4899331766Sken	if (ocs_ref_get_unless_zero(&io_to_abort->ref) == 0) {
4900331766Sken		/* command no longer active */
4901331766Sken		ocs_log_test(hw ? hw->os : NULL,
4902331766Sken				"io not active xri=0x%x tag=0x%x\n",
4903331766Sken				io_to_abort->indicator, io_to_abort->reqtag);
4904331766Sken		return OCS_HW_RTN_IO_NOT_ACTIVE;
4905331766Sken	}
4906331766Sken
4907331766Sken	/* non-port owned XRI checks */
4908331766Sken	/* Must have a valid WQ reference */
4909331766Sken	if (io_to_abort->wq == NULL) {
4910331766Sken		ocs_log_test(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
4911331766Sken				io_to_abort->indicator);
4912331766Sken		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4913331766Sken		return OCS_HW_RTN_IO_NOT_ACTIVE;
4914331766Sken	}
4915331766Sken
4916331766Sken	/* Validation checks complete; now check to see if already being aborted */
4917331766Sken	ocs_lock(&hw->io_abort_lock);
4918331766Sken		if (io_to_abort->abort_in_progress) {
4919331766Sken			ocs_unlock(&hw->io_abort_lock);
4920331766Sken			ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4921331766Sken			ocs_log_debug(hw ? hw->os : NULL,
4922331766Sken				"io already being aborted xri=0x%x tag=0x%x\n",
4923331766Sken				io_to_abort->indicator, io_to_abort->reqtag);
4924331766Sken			return OCS_HW_RTN_IO_ABORT_IN_PROGRESS;
4925331766Sken		}
4926331766Sken
4927331766Sken		/*
4928331766Sken		 * This IO is not already being aborted. Set flag so we won't try to
4929331766Sken		 * abort it again. After all, we only have one abort_done callback.
4930331766Sken		 */
4931331766Sken		io_to_abort->abort_in_progress = 1;
4932331766Sken	ocs_unlock(&hw->io_abort_lock);
4933331766Sken
4934331766Sken	/*
4935331766Sken	 * If we got here, the possibilities are:
4936331766Sken	 * - host owned xri
4937331766Sken	 *	- io_to_abort->wq_index != UINT32_MAX
4938331766Sken	 *		- submit ABORT_WQE to same WQ
4939331766Sken	 * - port owned xri:
4940331766Sken	 *	- rxri: io_to_abort->wq_index == UINT32_MAX
4941331766Sken	 *		- submit ABORT_WQE to any WQ
4942331766Sken	 *	- non-rxri
4943331766Sken	 *		- io_to_abort->index != UINT32_MAX
4944331766Sken	 *			- submit ABORT_WQE to same WQ
4945331766Sken	 *		- io_to_abort->index == UINT32_MAX
4946331766Sken	 *			- submit ABORT_WQE to any WQ
4947331766Sken	 */
4948331766Sken	io_to_abort->abort_done = cb;
4949331766Sken	io_to_abort->abort_arg  = arg;
4950331766Sken
4951331766Sken	atype = SLI_ABORT_XRI;
4952331766Sken	id = io_to_abort->indicator;
4953331766Sken
4954331766Sken	/* Allocate a request tag for the abort portion of this IO */
4955331766Sken	wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_abort, io_to_abort);
4956331766Sken	if (wqcb == NULL) {
4957331766Sken		ocs_log_err(hw->os, "can't allocate request tag\n");
4958331766Sken		return OCS_HW_RTN_NO_RESOURCES;
4959331766Sken	}
4960331766Sken	io_to_abort->abort_reqtag = wqcb->instance_index;
4961331766Sken
4962331766Sken	/*
4963331766Sken	 * If the wqe is on the pending list, then set this wqe to be
4964331766Sken	 * aborted when the IO's wqe is removed from the list.
4965331766Sken	 */
4966331766Sken	if (io_to_abort->wq != NULL) {
4967331766Sken		sli_queue_lock(io_to_abort->wq->queue);
4968331766Sken			if (ocs_list_on_list(&io_to_abort->wqe.link)) {
4969331766Sken				io_to_abort->wqe.abort_wqe_submit_needed = 1;
4970331766Sken				io_to_abort->wqe.send_abts = send_abts;
4971331766Sken				io_to_abort->wqe.id = id;
4972331766Sken				io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
4973331766Sken				sli_queue_unlock(io_to_abort->wq->queue);
4974331766Sken				return 0;
4975331766Sken		}
4976331766Sken		sli_queue_unlock(io_to_abort->wq->queue);
4977331766Sken	}
4978331766Sken
4979331766Sken	if (sli_abort_wqe(&hw->sli, io_to_abort->wqe.wqebuf, hw->sli.config.wqe_size, atype, send_abts, id, mask,
4980331766Sken			  io_to_abort->abort_reqtag, SLI4_CQ_DEFAULT)) {
4981331766Sken		ocs_log_err(hw->os, "ABORT WQE error\n");
4982331766Sken		io_to_abort->abort_reqtag = UINT32_MAX;
4983331766Sken		ocs_hw_reqtag_free(hw, wqcb);
4984331766Sken		rc = OCS_HW_RTN_ERROR;
4985331766Sken	}
4986331766Sken
4987331766Sken	if (OCS_HW_RTN_SUCCESS == rc) {
4988331766Sken		if (io_to_abort->wq == NULL) {
4989331766Sken			io_to_abort->wq = ocs_hw_queue_next_wq(hw, io_to_abort);
4990331766Sken			ocs_hw_assert(io_to_abort->wq != NULL);
4991331766Sken		}
4992331766Sken		/* ABORT_WQE does not actually utilize an XRI on the Port,
4993331766Sken		 * therefore, keep xbusy as-is to track the exchange's state,
4994331766Sken		 * not the ABORT_WQE's state
4995331766Sken		 */
4996331766Sken		rc = hw_wq_write(io_to_abort->wq, &io_to_abort->wqe);
4997331766Sken		if (rc > 0) {
4998331766Sken			/* non-negative return is success */
4999331766Sken			rc = 0;
5000331766Sken			/* can't abort an abort so skip adding to timed wqe list */
5001331766Sken		}
5002331766Sken	}
5003331766Sken
5004331766Sken	if (OCS_HW_RTN_SUCCESS != rc) {
5005331766Sken		ocs_lock(&hw->io_abort_lock);
5006331766Sken			io_to_abort->abort_in_progress = 0;
5007331766Sken		ocs_unlock(&hw->io_abort_lock);
5008331766Sken		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
5009331766Sken	}
5010331766Sken	return rc;
5011331766Sken}
5012331766Sken
5013331766Sken/**
5014331766Sken * @ingroup io
5015331766Sken * @brief Return the OX_ID/RX_ID of the IO.
5016331766Sken *
5017331766Sken * @param hw Hardware context.
5018331766Sken * @param io HW IO object.
5019331766Sken *
5020331766Sken * @return Returns X_ID on success, or -1 on failure.
5021331766Sken */
5022331766Skenint32_t
5023331766Skenocs_hw_io_get_xid(ocs_hw_t *hw, ocs_hw_io_t *io)
5024331766Sken{
5025331766Sken	if (!hw || !io) {
5026331766Sken		ocs_log_err(hw ? hw->os : NULL,
5027331766Sken			    "bad parameter hw=%p io=%p\n", hw, io);
5028331766Sken		return -1;
5029331766Sken	}
5030331766Sken
5031331766Sken	return io->indicator;
5032331766Sken}
5033331766Sken
5034331766Sken
5035331766Skentypedef struct ocs_hw_fw_write_cb_arg {
5036331766Sken	ocs_hw_fw_cb_t cb;
5037331766Sken	void *arg;
5038331766Sken} ocs_hw_fw_write_cb_arg_t;
5039331766Sken
5040331766Skentypedef struct ocs_hw_sfp_cb_arg {
5041331766Sken	ocs_hw_sfp_cb_t cb;
5042331766Sken	void *arg;
5043331766Sken	ocs_dma_t payload;
5044331766Sken} ocs_hw_sfp_cb_arg_t;
5045331766Sken
5046331766Skentypedef struct ocs_hw_temp_cb_arg {
5047331766Sken	ocs_hw_temp_cb_t cb;
5048331766Sken	void *arg;
5049331766Sken} ocs_hw_temp_cb_arg_t;
5050331766Sken
5051331766Skentypedef struct ocs_hw_link_stat_cb_arg {
5052331766Sken	ocs_hw_link_stat_cb_t cb;
5053331766Sken	void *arg;
5054331766Sken} ocs_hw_link_stat_cb_arg_t;
5055331766Sken
5056331766Skentypedef struct ocs_hw_host_stat_cb_arg {
5057331766Sken	ocs_hw_host_stat_cb_t cb;
5058331766Sken	void *arg;
5059331766Sken} ocs_hw_host_stat_cb_arg_t;
5060331766Sken
5061331766Skentypedef struct ocs_hw_dump_get_cb_arg {
5062331766Sken	ocs_hw_dump_get_cb_t cb;
5063331766Sken	void *arg;
5064331766Sken	void *mbox_cmd;
5065331766Sken} ocs_hw_dump_get_cb_arg_t;
5066331766Sken
5067331766Skentypedef struct ocs_hw_dump_clear_cb_arg {
5068331766Sken	ocs_hw_dump_clear_cb_t cb;
5069331766Sken	void *arg;
5070331766Sken	void *mbox_cmd;
5071331766Sken} ocs_hw_dump_clear_cb_arg_t;
5072331766Sken
5073331766Sken/**
5074331766Sken * @brief Write a portion of a firmware image to the device.
5075331766Sken *
5076331766Sken * @par Description
5077331766Sken * Calls the correct firmware write function based on the device type.
5078331766Sken *
5079331766Sken * @param hw Hardware context.
5080331766Sken * @param dma DMA structure containing the firmware image chunk.
5081331766Sken * @param size Size of the firmware image chunk.
5082331766Sken * @param offset Offset, in bytes, from the beginning of the firmware image.
5083331766Sken * @param last True if this is the last chunk of the image.
5084331766Sken * Causes the image to be committed to flash.
5085331766Sken * @param cb Pointer to a callback function that is called when the command completes.
5086331766Sken * The callback function prototype is
5087331766Sken * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5088331766Sken * @param arg Pointer to be passed to the callback function.
5089331766Sken *
5090331766Sken * @return Returns 0 on success, or a non-zero value on failure.
5091331766Sken */
5092331766Skenocs_hw_rtn_e
5093331766Skenocs_hw_firmware_write(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
5094331766Sken{
5095331766Sken	if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
5096331766Sken		return ocs_hw_firmware_write_lancer(hw, dma, size, offset, last, cb, arg);
5097331766Sken	} else {
5098331766Sken		/* Write firmware_write for BE3/Skyhawk not supported */
5099331766Sken		return -1;
5100331766Sken	}
5101331766Sken}
5102331766Sken
5103331766Sken/**
5104331766Sken * @brief Write a portion of a firmware image to the Emulex XE201 ASIC (Lancer).
5105331766Sken *
5106331766Sken * @par Description
5107331766Sken * Creates a SLI_CONFIG mailbox command, fills it with the correct values to write a
5108331766Sken * firmware image chunk, and then sends the command with ocs_hw_command(). On completion,
5109331766Sken * the callback function ocs_hw_fw_write_cb() gets called to free the mailbox
5110331766Sken * and to signal the caller that the write has completed.
5111331766Sken *
5112331766Sken * @param hw Hardware context.
5113331766Sken * @param dma DMA structure containing the firmware image chunk.
5114331766Sken * @param size Size of the firmware image chunk.
5115331766Sken * @param offset Offset, in bytes, from the beginning of the firmware image.
5116331766Sken * @param last True if this is the last chunk of the image. Causes the image to be committed to flash.
5117331766Sken * @param cb Pointer to a callback function that is called when the command completes.
5118331766Sken * The callback function prototype is
5119331766Sken * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5120331766Sken * @param arg Pointer to be passed to the callback function.
5121331766Sken *
5122331766Sken * @return Returns 0 on success, or a non-zero value on failure.
5123331766Sken */
5124331766Skenocs_hw_rtn_e
5125331766Skenocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
5126331766Sken{
5127331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5128331766Sken	uint8_t *mbxdata;
5129331766Sken	ocs_hw_fw_write_cb_arg_t *cb_arg;
5130331766Sken	int noc=0;	/* No Commit bit - set to 1 for testing */
5131331766Sken
5132331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
5133331766Sken		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
5134331766Sken		return OCS_HW_RTN_ERROR;
5135331766Sken	}
5136331766Sken
5137331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5138331766Sken	if (mbxdata == NULL) {
5139331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
5140331766Sken		return OCS_HW_RTN_NO_MEMORY;
5141331766Sken	}
5142331766Sken
5143331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_fw_write_cb_arg_t), OCS_M_NOWAIT);
5144331766Sken	if (cb_arg == NULL) {
5145331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5146331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5147331766Sken		return OCS_HW_RTN_NO_MEMORY;
5148331766Sken	}
5149331766Sken
5150331766Sken	cb_arg->cb = cb;
5151331766Sken	cb_arg->arg = arg;
5152331766Sken
5153331766Sken	if (sli_cmd_common_write_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE, noc, last,
5154331766Sken			size, offset, "/prg/", dma)) {
5155331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_fw_write, cb_arg);
5156331766Sken	}
5157331766Sken
5158331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5159331766Sken		ocs_log_test(hw->os, "COMMON_WRITE_OBJECT failed\n");
5160331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5161331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5162331766Sken	}
5163331766Sken
5164331766Sken	return rc;
5165331766Sken
5166331766Sken}
5167331766Sken
5168331766Sken/**
5169331766Sken * @brief Called when the WRITE OBJECT command completes.
5170331766Sken *
5171331766Sken * @par Description
5172331766Sken * Get the number of bytes actually written out of the response, free the mailbox
5173331766Sken * that was malloc'd by ocs_hw_firmware_write(),
5174331766Sken * then call the callback and pass the status and bytes written.
5175331766Sken *
5176331766Sken * @param hw Hardware context.
5177331766Sken * @param status Status field from the mbox completion.
5178331766Sken * @param mqe Mailbox response structure.
5179331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
5180331766Sken * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_written)</tt>.
5181331766Sken *
5182331766Sken * @return Returns 0.
5183331766Sken */
5184331766Skenstatic int32_t
5185331766Skenocs_hw_cb_fw_write(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5186331766Sken{
5187331766Sken
5188331766Sken	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
5189331766Sken	sli4_res_common_write_object_t* wr_obj_rsp = (sli4_res_common_write_object_t*) &(mbox_rsp->payload.embed);
5190331766Sken	ocs_hw_fw_write_cb_arg_t *cb_arg = arg;
5191331766Sken	uint32_t bytes_written;
5192331766Sken	uint16_t mbox_status;
5193331766Sken	uint32_t change_status;
5194331766Sken
5195331766Sken	bytes_written = wr_obj_rsp->actual_write_length;
5196331766Sken	mbox_status = mbox_rsp->hdr.status;
5197331766Sken	change_status = wr_obj_rsp->change_status;
5198331766Sken
5199331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5200331766Sken
5201331766Sken	if (cb_arg) {
5202331766Sken		if (cb_arg->cb) {
5203331766Sken			if ((status == 0) && mbox_status) {
5204331766Sken				status = mbox_status;
5205331766Sken			}
5206331766Sken			cb_arg->cb(status, bytes_written, change_status, cb_arg->arg);
5207331766Sken		}
5208331766Sken
5209331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5210331766Sken	}
5211331766Sken
5212331766Sken	return 0;
5213331766Sken
5214331766Sken}
5215331766Sken
5216331766Sken/**
5217331766Sken * @brief Called when the READ_TRANSCEIVER_DATA command completes.
5218331766Sken *
5219331766Sken * @par Description
5220331766Sken * Get the number of bytes read out of the response, free the mailbox that was malloc'd
5221331766Sken * by ocs_hw_get_sfp(), then call the callback and pass the status and bytes written.
5222331766Sken *
5223331766Sken * @param hw Hardware context.
5224331766Sken * @param status Status field from the mbox completion.
5225331766Sken * @param mqe Mailbox response structure.
5226331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
5227331766Sken * The callback function prototype is
5228331766Sken * <tt>void cb(int32_t status, uint32_t bytes_written, uint32_t *data, void *arg)</tt>.
5229331766Sken *
5230331766Sken * @return Returns 0.
5231331766Sken */
5232331766Skenstatic int32_t
5233331766Skenocs_hw_cb_sfp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5234331766Sken{
5235331766Sken
5236331766Sken	ocs_hw_sfp_cb_arg_t *cb_arg = arg;
5237331766Sken	ocs_dma_t *payload = NULL;
5238331766Sken	sli4_res_common_read_transceiver_data_t* mbox_rsp = NULL;
5239331766Sken	uint32_t bytes_written;
5240331766Sken
5241331766Sken	if (cb_arg) {
5242331766Sken		payload = &(cb_arg->payload);
5243331766Sken		if (cb_arg->cb) {
5244331766Sken			mbox_rsp = (sli4_res_common_read_transceiver_data_t*) payload->virt;
5245331766Sken			bytes_written = mbox_rsp->hdr.response_length;
5246331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
5247331766Sken				status = mbox_rsp->hdr.status;
5248331766Sken			}
5249331766Sken			cb_arg->cb(hw->os, status, bytes_written, mbox_rsp->page_data, cb_arg->arg);
5250331766Sken		}
5251331766Sken
5252331766Sken		ocs_dma_free(hw->os, &cb_arg->payload);
5253331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5254331766Sken	}
5255331766Sken
5256331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5257331766Sken	return 0;
5258331766Sken}
5259331766Sken
5260331766Sken/**
5261331766Sken * @ingroup io
5262331766Sken * @brief Function to retrieve the SFP information.
5263331766Sken *
5264331766Sken * @param hw Hardware context.
5265331766Sken * @param page The page of SFP data to retrieve (0xa0 or 0xa2).
5266331766Sken * @param cb Function call upon completion of sending the data (may be NULL).
5267331766Sken * @param arg Argument to pass to IO completion function.
5268331766Sken *
5269331766Sken * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5270331766Sken */
5271331766Skenocs_hw_rtn_e
5272331766Skenocs_hw_get_sfp(ocs_hw_t *hw, uint16_t page, ocs_hw_sfp_cb_t cb, void *arg)
5273331766Sken{
5274331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5275331766Sken	ocs_hw_sfp_cb_arg_t *cb_arg;
5276331766Sken	uint8_t *mbxdata;
5277331766Sken
5278331766Sken	/* mbxdata holds the header of the command */
5279331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5280331766Sken	if (mbxdata == NULL) {
5281331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
5282331766Sken		return OCS_HW_RTN_NO_MEMORY;
5283331766Sken	}
5284331766Sken
5285331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
5286331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_sfp_cb_arg_t), OCS_M_NOWAIT);
5287331766Sken	if (cb_arg == NULL) {
5288331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5289331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5290331766Sken		return OCS_HW_RTN_NO_MEMORY;
5291331766Sken	}
5292331766Sken
5293331766Sken	cb_arg->cb = cb;
5294331766Sken	cb_arg->arg = arg;
5295331766Sken
5296331766Sken	/* payload holds the non-embedded portion */
5297331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_read_transceiver_data_t),
5298331766Sken			  OCS_MIN_DMA_ALIGNMENT)) {
5299331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
5300331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5301331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5302331766Sken		return OCS_HW_RTN_NO_MEMORY;
5303331766Sken	}
5304331766Sken
5305331766Sken	/* Send the HW command */
5306331766Sken	if (sli_cmd_common_read_transceiver_data(&hw->sli, mbxdata, SLI4_BMBX_SIZE, page,
5307331766Sken	    &cb_arg->payload)) {
5308331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_sfp, cb_arg);
5309331766Sken	}
5310331766Sken
5311331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5312331766Sken		ocs_log_test(hw->os, "READ_TRANSCEIVER_DATA failed with status %d\n",
5313331766Sken				rc);
5314331766Sken		ocs_dma_free(hw->os, &cb_arg->payload);
5315331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5316331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5317331766Sken	}
5318331766Sken
5319331766Sken	return rc;
5320331766Sken}
5321331766Sken
5322331766Sken/**
5323331766Sken * @brief Function to retrieve the temperature information.
5324331766Sken *
5325331766Sken * @param hw Hardware context.
5326331766Sken * @param cb Function call upon completion of sending the data (may be NULL).
5327331766Sken * @param arg Argument to pass to IO completion function.
5328331766Sken *
5329331766Sken * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5330331766Sken */
5331331766Skenocs_hw_rtn_e
5332331766Skenocs_hw_get_temperature(ocs_hw_t *hw, ocs_hw_temp_cb_t cb, void *arg)
5333331766Sken{
5334331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5335331766Sken	ocs_hw_temp_cb_arg_t *cb_arg;
5336331766Sken	uint8_t *mbxdata;
5337331766Sken
5338331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5339331766Sken	if (mbxdata == NULL) {
5340331766Sken		ocs_log_err(hw->os, "failed to malloc mbox");
5341331766Sken		return OCS_HW_RTN_NO_MEMORY;
5342331766Sken	}
5343331766Sken
5344331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_temp_cb_arg_t), OCS_M_NOWAIT);
5345331766Sken	if (cb_arg == NULL) {
5346331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
5347331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5348331766Sken		return OCS_HW_RTN_NO_MEMORY;
5349331766Sken	}
5350331766Sken
5351331766Sken	cb_arg->cb = cb;
5352331766Sken	cb_arg->arg = arg;
5353331766Sken
5354331766Sken	if (sli_cmd_dump_type4(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5355331766Sken				SLI4_WKI_TAG_SAT_TEM)) {
5356331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_temp, cb_arg);
5357331766Sken	}
5358331766Sken
5359331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5360331766Sken		ocs_log_test(hw->os, "DUMP_TYPE4 failed\n");
5361331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5362331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5363331766Sken	}
5364331766Sken
5365331766Sken	return rc;
5366331766Sken}
5367331766Sken
5368331766Sken/**
5369331766Sken * @brief Called when the DUMP command completes.
5370331766Sken *
5371331766Sken * @par Description
5372331766Sken * Get the temperature data out of the response, free the mailbox that was malloc'd
5373331766Sken * by ocs_hw_get_temperature(), then call the callback and pass the status and data.
5374331766Sken *
5375331766Sken * @param hw Hardware context.
5376331766Sken * @param status Status field from the mbox completion.
5377331766Sken * @param mqe Mailbox response structure.
5378331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
5379331766Sken * The callback function prototype is defined by ocs_hw_temp_cb_t.
5380331766Sken *
5381331766Sken * @return Returns 0.
5382331766Sken */
5383331766Skenstatic int32_t
5384331766Skenocs_hw_cb_temp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5385331766Sken{
5386331766Sken
5387331766Sken	sli4_cmd_dump4_t* mbox_rsp = (sli4_cmd_dump4_t*) mqe;
5388331766Sken	ocs_hw_temp_cb_arg_t *cb_arg = arg;
5389331766Sken	uint32_t curr_temp = mbox_rsp->resp_data[0]; /* word 5 */
5390331766Sken	uint32_t crit_temp_thrshld = mbox_rsp->resp_data[1]; /* word 6*/
5391331766Sken	uint32_t warn_temp_thrshld = mbox_rsp->resp_data[2]; /* word 7 */
5392331766Sken	uint32_t norm_temp_thrshld = mbox_rsp->resp_data[3]; /* word 8 */
5393331766Sken	uint32_t fan_off_thrshld = mbox_rsp->resp_data[4];   /* word 9 */
5394331766Sken	uint32_t fan_on_thrshld = mbox_rsp->resp_data[5];    /* word 10 */
5395331766Sken
5396331766Sken	if (cb_arg) {
5397331766Sken		if (cb_arg->cb) {
5398331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
5399331766Sken				status = mbox_rsp->hdr.status;
5400331766Sken			}
5401331766Sken			cb_arg->cb(status,
5402331766Sken				   curr_temp,
5403331766Sken				   crit_temp_thrshld,
5404331766Sken				   warn_temp_thrshld,
5405331766Sken				   norm_temp_thrshld,
5406331766Sken				   fan_off_thrshld,
5407331766Sken				   fan_on_thrshld,
5408331766Sken				   cb_arg->arg);
5409331766Sken		}
5410331766Sken
5411331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5412331766Sken	}
5413331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5414331766Sken
5415331766Sken	return 0;
5416331766Sken}
5417331766Sken
5418331766Sken/**
5419331766Sken * @brief Function to retrieve the link statistics.
5420331766Sken *
5421331766Sken * @param hw Hardware context.
5422331766Sken * @param req_ext_counters If TRUE, then the extended counters will be requested.
5423331766Sken * @param clear_overflow_flags If TRUE, then overflow flags will be cleared.
5424331766Sken * @param clear_all_counters If TRUE, the counters will be cleared.
5425331766Sken * @param cb Function call upon completion of sending the data (may be NULL).
5426331766Sken * @param arg Argument to pass to IO completion function.
5427331766Sken *
5428331766Sken * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5429331766Sken */
5430331766Skenocs_hw_rtn_e
5431331766Skenocs_hw_get_link_stats(ocs_hw_t *hw,
5432331766Sken			uint8_t req_ext_counters,
5433331766Sken			uint8_t clear_overflow_flags,
5434331766Sken			uint8_t clear_all_counters,
5435331766Sken			ocs_hw_link_stat_cb_t cb,
5436331766Sken			void *arg)
5437331766Sken{
5438331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5439331766Sken	ocs_hw_link_stat_cb_arg_t *cb_arg;
5440331766Sken	uint8_t *mbxdata;
5441331766Sken
5442331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5443331766Sken	if (mbxdata == NULL) {
5444331766Sken		ocs_log_err(hw->os, "failed to malloc mbox");
5445331766Sken		return OCS_HW_RTN_NO_MEMORY;
5446331766Sken	}
5447331766Sken
5448331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_link_stat_cb_arg_t), OCS_M_NOWAIT);
5449331766Sken	if (cb_arg == NULL) {
5450331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
5451331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5452331766Sken		return OCS_HW_RTN_NO_MEMORY;
5453331766Sken	}
5454331766Sken
5455331766Sken	cb_arg->cb = cb;
5456331766Sken	cb_arg->arg = arg;
5457331766Sken
5458331766Sken	if (sli_cmd_read_link_stats(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5459331766Sken				    req_ext_counters,
5460331766Sken				    clear_overflow_flags,
5461331766Sken				    clear_all_counters)) {
5462331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_link_stat, cb_arg);
5463331766Sken	}
5464331766Sken
5465331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5466331766Sken		ocs_log_test(hw->os, "READ_LINK_STATS failed\n");
5467331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5468331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5469331766Sken	}
5470331766Sken
5471331766Sken	return rc;
5472331766Sken}
5473331766Sken
5474331766Sken/**
5475331766Sken * @brief Called when the READ_LINK_STAT command completes.
5476331766Sken *
5477331766Sken * @par Description
5478331766Sken * Get the counters out of the response, free the mailbox that was malloc'd
5479331766Sken * by ocs_hw_get_link_stats(), then call the callback and pass the status and data.
5480331766Sken *
5481331766Sken * @param hw Hardware context.
5482331766Sken * @param status Status field from the mbox completion.
5483331766Sken * @param mqe Mailbox response structure.
5484331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
5485331766Sken * The callback function prototype is defined by ocs_hw_link_stat_cb_t.
5486331766Sken *
5487331766Sken * @return Returns 0.
5488331766Sken */
5489331766Skenstatic int32_t
5490331766Skenocs_hw_cb_link_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5491331766Sken{
5492331766Sken
5493331766Sken	sli4_cmd_read_link_stats_t* mbox_rsp = (sli4_cmd_read_link_stats_t*) mqe;
5494331766Sken	ocs_hw_link_stat_cb_arg_t *cb_arg = arg;
5495331766Sken	ocs_hw_link_stat_counts_t counts[OCS_HW_LINK_STAT_MAX];
5496331766Sken	uint32_t num_counters = (mbox_rsp->gec ? 20 : 13);
5497331766Sken
5498331766Sken	ocs_memset(counts, 0, sizeof(ocs_hw_link_stat_counts_t) *
5499331766Sken		   OCS_HW_LINK_STAT_MAX);
5500331766Sken
5501331766Sken	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].overflow = mbox_rsp->w02of;
5502331766Sken	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].overflow = mbox_rsp->w03of;
5503331766Sken	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].overflow = mbox_rsp->w04of;
5504331766Sken	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].overflow = mbox_rsp->w05of;
5505331766Sken	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].overflow = mbox_rsp->w06of;
5506331766Sken	counts[OCS_HW_LINK_STAT_CRC_COUNT].overflow = mbox_rsp->w07of;
5507331766Sken	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].overflow = mbox_rsp->w08of;
5508331766Sken	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].overflow = mbox_rsp->w09of;
5509331766Sken	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].overflow = mbox_rsp->w10of;
5510331766Sken	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].overflow = mbox_rsp->w11of;
5511331766Sken	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].overflow = mbox_rsp->w12of;
5512331766Sken	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].overflow = mbox_rsp->w13of;
5513331766Sken	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].overflow = mbox_rsp->w14of;
5514331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].overflow = mbox_rsp->w15of;
5515331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].overflow = mbox_rsp->w16of;
5516331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].overflow = mbox_rsp->w17of;
5517331766Sken	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].overflow = mbox_rsp->w18of;
5518331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].overflow = mbox_rsp->w19of;
5519331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].overflow = mbox_rsp->w20of;
5520331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].overflow = mbox_rsp->w21of;
5521331766Sken
5522331766Sken	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = mbox_rsp->link_failure_error_count;
5523331766Sken	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = mbox_rsp->loss_of_sync_error_count;
5524331766Sken	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = mbox_rsp->loss_of_signal_error_count;
5525331766Sken	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = mbox_rsp->primitive_sequence_error_count;
5526331766Sken	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = mbox_rsp->invalid_transmission_word_error_count;
5527331766Sken	counts[OCS_HW_LINK_STAT_CRC_COUNT].counter = mbox_rsp->crc_error_count;
5528331766Sken	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = mbox_rsp->primitive_sequence_event_timeout_count;
5529331766Sken	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = mbox_rsp->elastic_buffer_overrun_error_count;
5530331766Sken	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = mbox_rsp->arbitration_fc_al_timout_count;
5531331766Sken	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = mbox_rsp->advertised_receive_bufftor_to_buffer_credit;
5532331766Sken	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = mbox_rsp->current_receive_buffer_to_buffer_credit;
5533331766Sken	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = mbox_rsp->advertised_transmit_buffer_to_buffer_credit;
5534331766Sken	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = mbox_rsp->current_transmit_buffer_to_buffer_credit;
5535331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].counter = mbox_rsp->received_eofa_count;
5536331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = mbox_rsp->received_eofdti_count;
5537331766Sken	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = mbox_rsp->received_eofni_count;
5538331766Sken	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].counter = mbox_rsp->received_soff_count;
5539331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = mbox_rsp->received_dropped_no_aer_count;
5540331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = mbox_rsp->received_dropped_no_available_rpi_resources_count;
5541331766Sken	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = mbox_rsp->received_dropped_no_available_xri_resources_count;
5542331766Sken
5543331766Sken	if (cb_arg) {
5544331766Sken		if (cb_arg->cb) {
5545331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
5546331766Sken				status = mbox_rsp->hdr.status;
5547331766Sken			}
5548331766Sken			cb_arg->cb(status,
5549331766Sken				   num_counters,
5550331766Sken				   counts,
5551331766Sken				   cb_arg->arg);
5552331766Sken		}
5553331766Sken
5554331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5555331766Sken	}
5556331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5557331766Sken
5558331766Sken	return 0;
5559331766Sken}
5560331766Sken
5561331766Sken/**
5562331766Sken * @brief Function to retrieve the link and host statistics.
5563331766Sken *
5564331766Sken * @param hw Hardware context.
5565331766Sken * @param cc clear counters, if TRUE all counters will be cleared.
5566331766Sken * @param cb Function call upon completion of receiving the data.
5567331766Sken * @param arg Argument to pass to pointer fc hosts statistics structure.
5568331766Sken *
5569331766Sken * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5570331766Sken */
5571331766Skenocs_hw_rtn_e
5572331766Skenocs_hw_get_host_stats(ocs_hw_t *hw, uint8_t cc, ocs_hw_host_stat_cb_t cb, void *arg)
5573331766Sken{
5574331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5575331766Sken	ocs_hw_host_stat_cb_arg_t *cb_arg;
5576331766Sken	uint8_t *mbxdata;
5577331766Sken
5578331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO);
5579331766Sken	if (mbxdata == NULL) {
5580331766Sken		ocs_log_err(hw->os, "failed to malloc mbox");
5581331766Sken		return OCS_HW_RTN_NO_MEMORY;
5582331766Sken	}
5583331766Sken
5584331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_host_stat_cb_arg_t), 0);
5585331766Sken	if (cb_arg == NULL) {
5586331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
5587331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5588331766Sken		return OCS_HW_RTN_NO_MEMORY;
5589331766Sken	 }
5590331766Sken
5591331766Sken	 cb_arg->cb = cb;
5592331766Sken	 cb_arg->arg = arg;
5593331766Sken
5594331766Sken	 /* Send the HW command to get the host stats */
5595331766Sken	if (sli_cmd_read_status(&hw->sli, mbxdata, SLI4_BMBX_SIZE, cc)) {
5596331766Sken		 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_host_stat, cb_arg);
5597331766Sken	}
5598331766Sken
5599331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5600331766Sken		ocs_log_test(hw->os, "READ_HOST_STATS failed\n");
5601331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5602331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5603331766Sken	}
5604331766Sken
5605331766Sken	return rc;
5606331766Sken}
5607331766Sken
5608331766Sken
5609331766Sken/**
5610331766Sken * @brief Called when the READ_STATUS command completes.
5611331766Sken *
5612331766Sken * @par Description
5613331766Sken * Get the counters out of the response, free the mailbox that was malloc'd
5614331766Sken * by ocs_hw_get_host_stats(), then call the callback and pass
5615331766Sken * the status and data.
5616331766Sken *
5617331766Sken * @param hw Hardware context.
5618331766Sken * @param status Status field from the mbox completion.
5619331766Sken * @param mqe Mailbox response structure.
5620331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
5621331766Sken * The callback function prototype is defined by
5622331766Sken * ocs_hw_host_stat_cb_t.
5623331766Sken *
5624331766Sken * @return Returns 0.
5625331766Sken */
5626331766Skenstatic int32_t
5627331766Skenocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5628331766Sken{
5629331766Sken
5630331766Sken	sli4_cmd_read_status_t* mbox_rsp = (sli4_cmd_read_status_t*) mqe;
5631331766Sken	ocs_hw_host_stat_cb_arg_t *cb_arg = arg;
5632331766Sken	ocs_hw_host_stat_counts_t counts[OCS_HW_HOST_STAT_MAX];
5633331766Sken	uint32_t num_counters = OCS_HW_HOST_STAT_MAX;
5634331766Sken
5635331766Sken	ocs_memset(counts, 0, sizeof(ocs_hw_host_stat_counts_t) *
5636331766Sken		   OCS_HW_HOST_STAT_MAX);
5637331766Sken
5638331766Sken	counts[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter = mbox_rsp->transmit_kbyte_count;
5639331766Sken	counts[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter = mbox_rsp->receive_kbyte_count;
5640331766Sken	counts[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter = mbox_rsp->transmit_frame_count;
5641331766Sken	counts[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter = mbox_rsp->receive_frame_count;
5642331766Sken	counts[OCS_HW_HOST_STAT_TX_SEQ_COUNT].counter = mbox_rsp->transmit_sequence_count;
5643331766Sken	counts[OCS_HW_HOST_STAT_RX_SEQ_COUNT].counter = mbox_rsp->receive_sequence_count;
5644331766Sken	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = mbox_rsp->total_exchanges_originator;
5645331766Sken	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = mbox_rsp->total_exchanges_responder;
5646331766Sken	counts[OCS_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = mbox_rsp->receive_p_bsy_count;
5647331766Sken	counts[OCS_HW_HOST_STAT_RX_F_BSY_COUNT].counter = mbox_rsp->receive_f_bsy_count;
5648331766Sken	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_rq_buffer_count;
5649331766Sken	counts[OCS_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = mbox_rsp->empty_rq_timeout_count;
5650331766Sken	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_xri_count;
5651331766Sken	counts[OCS_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = mbox_rsp->empty_xri_pool_count;
5652331766Sken
5653331766Sken
5654331766Sken	if (cb_arg) {
5655331766Sken		if (cb_arg->cb) {
5656331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
5657331766Sken				status = mbox_rsp->hdr.status;
5658331766Sken			}
5659331766Sken			cb_arg->cb(status,
5660331766Sken				   num_counters,
5661331766Sken				   counts,
5662331766Sken				   cb_arg->arg);
5663331766Sken		}
5664331766Sken
5665331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5666331766Sken	}
5667331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5668331766Sken
5669331766Sken	return 0;
5670331766Sken}
5671331766Sken
5672331766Sken/**
5673331766Sken * @brief HW link configuration enum to the CLP string value mapping.
5674331766Sken *
5675331766Sken * This structure provides a mapping from the ocs_hw_linkcfg_e
5676331766Sken * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5677331766Sken * control) to the CLP string that is used
5678331766Sken * in the DMTF_CLP_CMD mailbox command.
5679331766Sken */
5680331766Skentypedef struct ocs_hw_linkcfg_map_s {
5681331766Sken	ocs_hw_linkcfg_e linkcfg;
5682331766Sken	const char *clp_str;
5683331766Sken} ocs_hw_linkcfg_map_t;
5684331766Sken
5685331766Sken/**
5686331766Sken * @brief Mapping from the HW linkcfg enum to the CLP command value
5687331766Sken * string.
5688331766Sken */
5689331766Skenstatic ocs_hw_linkcfg_map_t linkcfg_map[] = {
5690331766Sken	{OCS_HW_LINKCFG_4X10G, "ELX_4x10G"},
5691331766Sken	{OCS_HW_LINKCFG_1X40G, "ELX_1x40G"},
5692331766Sken	{OCS_HW_LINKCFG_2X16G, "ELX_2x16G"},
5693331766Sken	{OCS_HW_LINKCFG_4X8G, "ELX_4x8G"},
5694331766Sken	{OCS_HW_LINKCFG_4X1G, "ELX_4x1G"},
5695331766Sken	{OCS_HW_LINKCFG_2X10G, "ELX_2x10G"},
5696331766Sken	{OCS_HW_LINKCFG_2X10G_2X8G, "ELX_2x10G_2x8G"}};
5697331766Sken
5698331766Sken/**
5699331766Sken * @brief HW link configuration enum to Skyhawk link config ID mapping.
5700331766Sken *
5701331766Sken * This structure provides a mapping from the ocs_hw_linkcfg_e
5702331766Sken * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5703331766Sken * control) to the link config ID numbers used by Skyhawk
5704331766Sken */
5705331766Skentypedef struct ocs_hw_skyhawk_linkcfg_map_s {
5706331766Sken	ocs_hw_linkcfg_e linkcfg;
5707331766Sken	uint32_t	config_id;
5708331766Sken} ocs_hw_skyhawk_linkcfg_map_t;
5709331766Sken
5710331766Sken/**
5711331766Sken * @brief Mapping from the HW linkcfg enum to the Skyhawk link config IDs
5712331766Sken */
5713331766Skenstatic ocs_hw_skyhawk_linkcfg_map_t skyhawk_linkcfg_map[] = {
5714331766Sken	{OCS_HW_LINKCFG_4X10G, 0x0a},
5715331766Sken	{OCS_HW_LINKCFG_1X40G, 0x09},
5716331766Sken};
5717331766Sken
5718331766Sken/**
5719331766Sken * @brief Helper function for getting the HW linkcfg enum from the CLP
5720331766Sken * string value
5721331766Sken *
5722331766Sken * @param clp_str CLP string value from OEMELX_LinkConfig.
5723331766Sken *
5724331766Sken * @return Returns the HW linkcfg enum corresponding to clp_str.
5725331766Sken */
5726331766Skenstatic ocs_hw_linkcfg_e
5727331766Skenocs_hw_linkcfg_from_clp(const char *clp_str)
5728331766Sken{
5729331766Sken	uint32_t i;
5730331766Sken	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5731331766Sken		if (ocs_strncmp(linkcfg_map[i].clp_str, clp_str, ocs_strlen(clp_str)) == 0) {
5732331766Sken			return linkcfg_map[i].linkcfg;
5733331766Sken		}
5734331766Sken	}
5735331766Sken	return OCS_HW_LINKCFG_NA;
5736331766Sken}
5737331766Sken
5738331766Sken/**
5739331766Sken * @brief Helper function for getting the CLP string value from the HW
5740331766Sken * linkcfg enum.
5741331766Sken *
5742331766Sken * @param linkcfg HW linkcfg enum.
5743331766Sken *
5744331766Sken * @return Returns the OEMELX_LinkConfig CLP string value corresponding to
5745331766Sken * given linkcfg.
5746331766Sken */
5747331766Skenstatic const char *
5748331766Skenocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5749331766Sken{
5750331766Sken	uint32_t i;
5751331766Sken	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5752331766Sken		if (linkcfg_map[i].linkcfg == linkcfg) {
5753331766Sken			return linkcfg_map[i].clp_str;
5754331766Sken		}
5755331766Sken	}
5756331766Sken	return NULL;
5757331766Sken}
5758331766Sken
5759331766Sken/**
5760331766Sken * @brief Helper function for getting a Skyhawk link config ID from the HW
5761331766Sken * linkcfg enum.
5762331766Sken *
5763331766Sken * @param linkcfg HW linkcfg enum.
5764331766Sken *
5765331766Sken * @return Returns the Skyhawk link config ID corresponding to
5766331766Sken * given linkcfg.
5767331766Sken */
5768331766Skenstatic uint32_t
5769331766Skenocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5770331766Sken{
5771331766Sken	uint32_t i;
5772331766Sken	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5773331766Sken		if (skyhawk_linkcfg_map[i].linkcfg == linkcfg) {
5774331766Sken			return skyhawk_linkcfg_map[i].config_id;
5775331766Sken		}
5776331766Sken	}
5777331766Sken	return 0;
5778331766Sken}
5779331766Sken
5780331766Sken/**
5781331766Sken * @brief Helper function for getting the HW linkcfg enum from a
5782331766Sken * Skyhawk config ID.
5783331766Sken *
5784331766Sken * @param config_id Skyhawk link config ID.
5785331766Sken *
5786331766Sken * @return Returns the HW linkcfg enum corresponding to config_id.
5787331766Sken */
5788331766Skenstatic ocs_hw_linkcfg_e
5789331766Skenocs_hw_linkcfg_from_config_id(const uint32_t config_id)
5790331766Sken{
5791331766Sken	uint32_t i;
5792331766Sken	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5793331766Sken		if (skyhawk_linkcfg_map[i].config_id == config_id) {
5794331766Sken			return skyhawk_linkcfg_map[i].linkcfg;
5795331766Sken		}
5796331766Sken	}
5797331766Sken	return OCS_HW_LINKCFG_NA;
5798331766Sken}
5799331766Sken
5800331766Sken/**
5801331766Sken * @brief Link configuration callback argument.
5802331766Sken */
5803331766Skentypedef struct ocs_hw_linkcfg_cb_arg_s {
5804331766Sken	ocs_hw_port_control_cb_t cb;
5805331766Sken	void *arg;
5806331766Sken	uint32_t opts;
5807331766Sken	int32_t status;
5808331766Sken	ocs_dma_t dma_cmd;
5809331766Sken	ocs_dma_t dma_resp;
5810331766Sken	uint32_t result_len;
5811331766Sken} ocs_hw_linkcfg_cb_arg_t;
5812331766Sken
5813331766Sken/**
5814331766Sken * @brief Set link configuration.
5815331766Sken *
5816331766Sken * @param hw Hardware context.
5817331766Sken * @param value Link configuration enum to which the link configuration is
5818331766Sken * set.
5819331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5820331766Sken * @param cb Callback function to invoke following mbx command.
5821331766Sken * @param arg Callback argument.
5822331766Sken *
5823331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
5824331766Sken */
5825331766Skenstatic ocs_hw_rtn_e
5826331766Skenocs_hw_set_linkcfg(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5827331766Sken{
5828331766Sken	if (!sli_link_is_configurable(&hw->sli)) {
5829331766Sken		ocs_log_debug(hw->os, "Function not supported\n");
5830331766Sken		return OCS_HW_RTN_ERROR;
5831331766Sken	}
5832331766Sken
5833331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
5834331766Sken		return ocs_hw_set_linkcfg_lancer(hw, value, opts, cb, arg);
5835331766Sken	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
5836331766Sken		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
5837331766Sken		return ocs_hw_set_linkcfg_skyhawk(hw, value, opts, cb, arg);
5838331766Sken	} else {
5839331766Sken		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
5840331766Sken		return OCS_HW_RTN_ERROR;
5841331766Sken	}
5842331766Sken}
5843331766Sken
5844331766Sken/**
5845331766Sken * @brief Set link configuration for Lancer
5846331766Sken *
5847331766Sken * @param hw Hardware context.
5848331766Sken * @param value Link configuration enum to which the link configuration is
5849331766Sken * set.
5850331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5851331766Sken * @param cb Callback function to invoke following mbx command.
5852331766Sken * @param arg Callback argument.
5853331766Sken *
5854331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
5855331766Sken */
5856331766Skenstatic ocs_hw_rtn_e
5857331766Skenocs_hw_set_linkcfg_lancer(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5858331766Sken{
5859331766Sken	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
5860331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5861331766Sken	const char *value_str = NULL;
5862331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5863331766Sken
5864331766Sken	/* translate ocs_hw_linkcfg_e to CLP string */
5865331766Sken	value_str = ocs_hw_clp_from_linkcfg(value);
5866331766Sken
5867331766Sken	/* allocate memory for callback argument */
5868331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
5869331766Sken	if (cb_arg == NULL) {
5870331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
5871331766Sken		return OCS_HW_RTN_NO_MEMORY;
5872331766Sken	}
5873331766Sken
5874331766Sken	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_LinkConfig=%s", value_str);
5875331766Sken	/* allocate DMA for command  */
5876331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
5877331766Sken		ocs_log_err(hw->os, "malloc failed\n");
5878331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5879331766Sken		return OCS_HW_RTN_NO_MEMORY;
5880331766Sken	}
5881331766Sken	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
5882331766Sken	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
5883331766Sken
5884331766Sken	/* allocate DMA for response */
5885331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
5886331766Sken		ocs_log_err(hw->os, "malloc failed\n");
5887331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5888331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5889331766Sken		return OCS_HW_RTN_NO_MEMORY;
5890331766Sken	}
5891331766Sken	cb_arg->cb = cb;
5892331766Sken	cb_arg->arg = arg;
5893331766Sken	cb_arg->opts = opts;
5894331766Sken
5895331766Sken	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
5896331766Sken					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
5897331766Sken
5898331766Sken	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
5899331766Sken		/* if failed, or polling, free memory here; if success and not
5900331766Sken		 * polling, will free in callback function
5901331766Sken		 */
5902331766Sken		if (rc) {
5903331766Sken			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
5904331766Sken					(char *)cb_arg->dma_cmd.virt);
5905331766Sken		}
5906331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5907331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_resp);
5908331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5909331766Sken	}
5910331766Sken	return rc;
5911331766Sken}
5912331766Sken
5913331766Sken/**
5914331766Sken * @brief Callback for ocs_hw_set_linkcfg_skyhawk
5915331766Sken *
5916331766Sken * @param hw Hardware context.
5917331766Sken * @param status Status from the RECONFIG_GET_LINK_INFO command.
5918331766Sken * @param mqe Mailbox response structure.
5919331766Sken * @param arg Pointer to a callback argument.
5920331766Sken *
5921331766Sken * @return none
5922331766Sken */
5923331766Skenstatic void
5924331766Skenocs_hw_set_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5925331766Sken{
5926331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
5927331766Sken
5928331766Sken	if (status) {
5929331766Sken		ocs_log_test(hw->os, "SET_RECONFIG_LINK_ID failed, status=%d\n", status);
5930331766Sken	}
5931331766Sken
5932331766Sken	/* invoke callback */
5933331766Sken	if (cb_arg->cb) {
5934331766Sken		cb_arg->cb(status, 0, cb_arg->arg);
5935331766Sken	}
5936331766Sken
5937331766Sken	/* if polling, will free memory in calling function */
5938331766Sken	if (cb_arg->opts != OCS_CMD_POLL) {
5939331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5940331766Sken	}
5941331766Sken}
5942331766Sken
5943331766Sken/**
5944331766Sken * @brief Set link configuration for a Skyhawk
5945331766Sken *
5946331766Sken * @param hw Hardware context.
5947331766Sken * @param value Link configuration enum to which the link configuration is
5948331766Sken * set.
5949331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5950331766Sken * @param cb Callback function to invoke following mbx command.
5951331766Sken * @param arg Callback argument.
5952331766Sken *
5953331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
5954331766Sken */
5955331766Skenstatic ocs_hw_rtn_e
5956331766Skenocs_hw_set_linkcfg_skyhawk(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5957331766Sken{
5958331766Sken	uint8_t *mbxdata;
5959331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5960331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5961331766Sken	uint32_t config_id;
5962331766Sken
5963331766Sken	config_id = ocs_hw_config_id_from_linkcfg(value);
5964331766Sken
5965331766Sken	if (config_id == 0) {
5966331766Sken		ocs_log_test(hw->os, "Link config %d not supported by Skyhawk\n", value);
5967331766Sken		return OCS_HW_RTN_ERROR;
5968331766Sken	}
5969331766Sken
5970331766Sken	/* mbxdata holds the header of the command */
5971331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5972331766Sken	if (mbxdata == NULL) {
5973331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
5974331766Sken		return OCS_HW_RTN_NO_MEMORY;
5975331766Sken	}
5976331766Sken
5977331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
5978331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
5979331766Sken	if (cb_arg == NULL) {
5980331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5981331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5982331766Sken		return OCS_HW_RTN_NO_MEMORY;
5983331766Sken	}
5984331766Sken
5985331766Sken	cb_arg->cb = cb;
5986331766Sken	cb_arg->arg = arg;
5987331766Sken
5988331766Sken	if (sli_cmd_common_set_reconfig_link_id(&hw->sli, mbxdata, SLI4_BMBX_SIZE, NULL, 0, config_id)) {
5989331766Sken		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_set_active_link_config_cb, cb_arg);
5990331766Sken	}
5991331766Sken
5992331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
5993331766Sken		ocs_log_err(hw->os, "SET_RECONFIG_LINK_ID failed\n");
5994331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5995331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
5996331766Sken	} else if (opts == OCS_CMD_POLL) {
5997331766Sken		/* if we're polling we have to call the callback here. */
5998331766Sken		ocs_hw_set_active_link_config_cb(hw, 0, mbxdata, cb_arg);
5999331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6000331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6001331766Sken	} else {
6002331766Sken		/* We weren't poling, so the callback got called */
6003331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6004331766Sken	}
6005331766Sken
6006331766Sken	return rc;
6007331766Sken}
6008331766Sken
6009331766Sken/**
6010331766Sken * @brief Get link configuration.
6011331766Sken *
6012331766Sken * @param hw Hardware context.
6013331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6014331766Sken * @param cb Callback function to invoke following mbx command.
6015331766Sken * @param arg Callback argument.
6016331766Sken *
6017331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6018331766Sken */
6019331766Skenstatic ocs_hw_rtn_e
6020331766Skenocs_hw_get_linkcfg(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6021331766Sken{
6022331766Sken	if (!sli_link_is_configurable(&hw->sli)) {
6023331766Sken		ocs_log_debug(hw->os, "Function not supported\n");
6024331766Sken		return OCS_HW_RTN_ERROR;
6025331766Sken	}
6026331766Sken
6027331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
6028331766Sken		return ocs_hw_get_linkcfg_lancer(hw, opts, cb, arg);
6029331766Sken	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
6030331766Sken		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
6031331766Sken		return ocs_hw_get_linkcfg_skyhawk(hw, opts, cb, arg);
6032331766Sken	} else {
6033331766Sken		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
6034331766Sken		return OCS_HW_RTN_ERROR;
6035331766Sken	}
6036331766Sken}
6037331766Sken
6038331766Sken/**
6039331766Sken * @brief Get link configuration for a Lancer
6040331766Sken *
6041331766Sken * @param hw Hardware context.
6042331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6043331766Sken * @param cb Callback function to invoke following mbx command.
6044331766Sken * @param arg Callback argument.
6045331766Sken *
6046331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6047331766Sken */
6048331766Skenstatic ocs_hw_rtn_e
6049331766Skenocs_hw_get_linkcfg_lancer(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6050331766Sken{
6051331766Sken	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6052331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6053331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6054331766Sken
6055331766Sken	/* allocate memory for callback argument */
6056331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6057331766Sken	if (cb_arg == NULL) {
6058331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
6059331766Sken		return OCS_HW_RTN_NO_MEMORY;
6060331766Sken	}
6061331766Sken
6062331766Sken	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "show / OEMELX_LinkConfig");
6063331766Sken
6064331766Sken	/* allocate DMA for command  */
6065331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6066331766Sken		ocs_log_err(hw->os, "malloc failed\n");
6067331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6068331766Sken		return OCS_HW_RTN_NO_MEMORY;
6069331766Sken	}
6070331766Sken
6071331766Sken	/* copy CLP command to DMA command */
6072331766Sken	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6073331766Sken	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
6074331766Sken
6075331766Sken	/* allocate DMA for response */
6076331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6077331766Sken		ocs_log_err(hw->os, "malloc failed\n");
6078331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6079331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6080331766Sken		return OCS_HW_RTN_NO_MEMORY;
6081331766Sken	}
6082331766Sken	cb_arg->cb = cb;
6083331766Sken	cb_arg->arg = arg;
6084331766Sken	cb_arg->opts = opts;
6085331766Sken
6086331766Sken	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
6087331766Sken					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
6088331766Sken
6089331766Sken	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6090331766Sken		/* if failed or polling, free memory here; if not polling and success,
6091331766Sken		 * will free in callback function
6092331766Sken		 */
6093331766Sken		if (rc) {
6094331766Sken			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
6095331766Sken					(char *)cb_arg->dma_cmd.virt);
6096331766Sken		}
6097331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6098331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6099331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6100331766Sken	}
6101331766Sken	return rc;
6102331766Sken}
6103331766Sken
6104331766Sken
6105331766Sken/**
6106331766Sken * @brief Get the link configuration callback.
6107331766Sken *
6108331766Sken * @param hw Hardware context.
6109331766Sken * @param status Status from the RECONFIG_GET_LINK_INFO command.
6110331766Sken * @param mqe Mailbox response structure.
6111331766Sken * @param arg Pointer to a callback argument.
6112331766Sken *
6113331766Sken * @return none
6114331766Sken */
6115331766Skenstatic void
6116331766Skenocs_hw_get_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6117331766Sken{
6118331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6119331766Sken	sli4_res_common_get_reconfig_link_info_t *rsp = cb_arg->dma_cmd.virt;
6120331766Sken	ocs_hw_linkcfg_e value = OCS_HW_LINKCFG_NA;
6121331766Sken
6122331766Sken	if (status) {
6123331766Sken		ocs_log_test(hw->os, "GET_RECONFIG_LINK_INFO failed, status=%d\n", status);
6124331766Sken	} else {
6125331766Sken		/* Call was successful */
6126331766Sken		value = ocs_hw_linkcfg_from_config_id(rsp->active_link_config_id);
6127331766Sken	}
6128331766Sken
6129331766Sken	/* invoke callback */
6130331766Sken	if (cb_arg->cb) {
6131331766Sken		cb_arg->cb(status, value, cb_arg->arg);
6132331766Sken	}
6133331766Sken
6134331766Sken	/* if polling, will free memory in calling function */
6135331766Sken	if (cb_arg->opts != OCS_CMD_POLL) {
6136331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6137331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6138331766Sken	}
6139331766Sken}
6140331766Sken
6141331766Sken/**
6142331766Sken * @brief Get link configuration for a Skyhawk.
6143331766Sken *
6144331766Sken * @param hw Hardware context.
6145331766Sken * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6146331766Sken * @param cb Callback function to invoke following mbx command.
6147331766Sken * @param arg Callback argument.
6148331766Sken *
6149331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6150331766Sken */
6151331766Skenstatic ocs_hw_rtn_e
6152331766Skenocs_hw_get_linkcfg_skyhawk(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6153331766Sken{
6154331766Sken	uint8_t *mbxdata;
6155331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6156331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6157331766Sken
6158331766Sken	/* mbxdata holds the header of the command */
6159331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6160331766Sken	if (mbxdata == NULL) {
6161331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
6162331766Sken		return OCS_HW_RTN_NO_MEMORY;
6163331766Sken	}
6164331766Sken
6165331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
6166331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
6167331766Sken	if (cb_arg == NULL) {
6168331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
6169331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6170331766Sken		return OCS_HW_RTN_NO_MEMORY;
6171331766Sken	}
6172331766Sken
6173331766Sken	cb_arg->cb = cb;
6174331766Sken	cb_arg->arg = arg;
6175331766Sken	cb_arg->opts = opts;
6176331766Sken
6177331766Sken	/* dma_mem holds the non-embedded portion */
6178331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, sizeof(sli4_res_common_get_reconfig_link_info_t), 4)) {
6179331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
6180331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6181331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6182331766Sken		return OCS_HW_RTN_NO_MEMORY;
6183331766Sken	}
6184331766Sken
6185331766Sken	if (sli_cmd_common_get_reconfig_link_info(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->dma_cmd)) {
6186331766Sken		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_get_active_link_config_cb, cb_arg);
6187331766Sken	}
6188331766Sken
6189331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
6190331766Sken		ocs_log_err(hw->os, "GET_RECONFIG_LINK_INFO failed\n");
6191331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6192331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6193331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6194331766Sken	} else if (opts == OCS_CMD_POLL) {
6195331766Sken		/* if we're polling we have to call the callback here. */
6196331766Sken		ocs_hw_get_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6197331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6198331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6199331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6200331766Sken	} else {
6201331766Sken		/* We weren't poling, so the callback got called */
6202331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6203331766Sken	}
6204331766Sken
6205331766Sken	return rc;
6206331766Sken}
6207331766Sken
6208331766Sken/**
6209331766Sken * @brief Sets the DIF seed value.
6210331766Sken *
6211331766Sken * @param hw Hardware context.
6212331766Sken *
6213331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6214331766Sken */
6215331766Skenstatic ocs_hw_rtn_e
6216331766Skenocs_hw_set_dif_seed(ocs_hw_t *hw)
6217331766Sken{
6218331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6219331766Sken	uint8_t buf[SLI4_BMBX_SIZE];
6220331766Sken	sli4_req_common_set_features_dif_seed_t seed_param;
6221331766Sken
6222331766Sken	ocs_memset(&seed_param, 0, sizeof(seed_param));
6223331766Sken	seed_param.seed = hw->config.dif_seed;
6224331766Sken
6225331766Sken	/* send set_features command */
6226331766Sken	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6227331766Sken					SLI4_SET_FEATURES_DIF_SEED,
6228331766Sken					4,
6229331766Sken					(uint32_t*)&seed_param)) {
6230331766Sken		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6231331766Sken		if (rc) {
6232331766Sken			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6233331766Sken		} else {
6234331766Sken			ocs_log_debug(hw->os, "DIF seed set to 0x%x\n",
6235331766Sken					hw->config.dif_seed);
6236331766Sken		}
6237331766Sken	} else {
6238331766Sken		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6239331766Sken		rc = OCS_HW_RTN_ERROR;
6240331766Sken	}
6241331766Sken	return rc;
6242331766Sken}
6243331766Sken
6244331766Sken
6245331766Sken/**
6246331766Sken * @brief Sets the DIF mode value.
6247331766Sken *
6248331766Sken * @param hw Hardware context.
6249331766Sken *
6250331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6251331766Sken */
6252331766Skenstatic ocs_hw_rtn_e
6253331766Skenocs_hw_set_dif_mode(ocs_hw_t *hw)
6254331766Sken{
6255331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6256331766Sken	uint8_t buf[SLI4_BMBX_SIZE];
6257331766Sken	sli4_req_common_set_features_t10_pi_mem_model_t mode_param;
6258331766Sken
6259331766Sken	ocs_memset(&mode_param, 0, sizeof(mode_param));
6260331766Sken	mode_param.tmm = (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? 0 : 1);
6261331766Sken
6262331766Sken	/* send set_features command */
6263331766Sken	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6264331766Sken					SLI4_SET_FEATURES_DIF_MEMORY_MODE,
6265331766Sken					sizeof(mode_param),
6266331766Sken					(uint32_t*)&mode_param)) {
6267331766Sken		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6268331766Sken		if (rc) {
6269331766Sken			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6270331766Sken		} else {
6271331766Sken			ocs_log_test(hw->os, "DIF mode set to %s\n",
6272331766Sken				(hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? "inline" : "separate"));
6273331766Sken		}
6274331766Sken	} else {
6275331766Sken		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6276331766Sken		rc = OCS_HW_RTN_ERROR;
6277331766Sken	}
6278331766Sken	return rc;
6279331766Sken}
6280331766Sken
6281331766Skenstatic void
6282331766Skenocs_hw_watchdog_timer_cb(void *arg)
6283331766Sken{
6284331766Sken	ocs_hw_t *hw = (ocs_hw_t *)arg;
6285331766Sken
6286331766Sken	ocs_hw_config_watchdog_timer(hw);
6287331766Sken	return;
6288331766Sken}
6289331766Sken
6290331766Skenstatic void
6291331766Skenocs_hw_cb_cfg_watchdog(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6292331766Sken{
6293331766Sken	uint16_t timeout = hw->watchdog_timeout;
6294331766Sken
6295331766Sken	if (status != 0) {
6296331766Sken		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", status);
6297331766Sken	} else {
6298331766Sken		if(timeout != 0) {
6299331766Sken			/* keeping callback 500ms before timeout to keep heartbeat alive */
6300331766Sken			ocs_setup_timer(hw->os, &hw->watchdog_timer, ocs_hw_watchdog_timer_cb, hw, (timeout*1000 - 500) );
6301331766Sken		}else {
6302331766Sken			ocs_del_timer(&hw->watchdog_timer);
6303331766Sken		}
6304331766Sken	}
6305331766Sken
6306331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6307331766Sken	return;
6308331766Sken}
6309331766Sken
6310331766Sken/**
6311331766Sken * @brief Set configuration parameters for watchdog timer feature.
6312331766Sken *
6313331766Sken * @param hw Hardware context.
6314331766Sken * @param timeout Timeout for watchdog timer in seconds
6315331766Sken *
6316331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6317331766Sken */
6318331766Skenstatic ocs_hw_rtn_e
6319331766Skenocs_hw_config_watchdog_timer(ocs_hw_t *hw)
6320331766Sken{
6321331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6322331766Sken	uint8_t *buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
6323331766Sken
6324331766Sken	sli4_cmd_lowlevel_set_watchdog(&hw->sli, buf, SLI4_BMBX_SIZE, hw->watchdog_timeout);
6325331766Sken	rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_cfg_watchdog, NULL);
6326331766Sken	if (rc) {
6327331766Sken		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
6328331766Sken		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", rc);
6329331766Sken	}
6330331766Sken	return rc;
6331331766Sken}
6332331766Sken
6333331766Sken/**
6334331766Sken * @brief Set configuration parameters for auto-generate xfer_rdy T10 PI feature.
6335331766Sken *
6336331766Sken * @param hw Hardware context.
6337331766Sken * @param buf Pointer to a mailbox buffer area.
6338331766Sken *
6339331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6340331766Sken */
6341331766Skenstatic ocs_hw_rtn_e
6342331766Skenocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf)
6343331766Sken{
6344331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6345331766Sken	sli4_req_common_set_features_xfer_rdy_t10pi_t param;
6346331766Sken
6347331766Sken	ocs_memset(&param, 0, sizeof(param));
6348331766Sken	param.rtc = (hw->config.auto_xfer_rdy_ref_tag_is_lba ? 0 : 1);
6349331766Sken	param.atv = (hw->config.auto_xfer_rdy_app_tag_valid ? 1 : 0);
6350331766Sken	param.tmm = ((hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE) ? 0 : 1);
6351331766Sken	param.app_tag = hw->config.auto_xfer_rdy_app_tag_value;
6352331766Sken	param.blk_size = hw->config.auto_xfer_rdy_blk_size_chip;
6353331766Sken
6354331766Sken	switch (hw->config.auto_xfer_rdy_p_type) {
6355331766Sken	case 1:
6356331766Sken		param.p_type = 0;
6357331766Sken		break;
6358331766Sken	case 3:
6359331766Sken		param.p_type = 2;
6360331766Sken		break;
6361331766Sken	default:
6362331766Sken		ocs_log_err(hw->os, "unsupported p_type %d\n",
6363331766Sken			hw->config.auto_xfer_rdy_p_type);
6364331766Sken		return OCS_HW_RTN_ERROR;
6365331766Sken	}
6366331766Sken
6367331766Sken	/* build the set_features command */
6368331766Sken	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6369331766Sken				    SLI4_SET_FEATURES_SET_CONFIG_AUTO_XFER_RDY_T10PI,
6370331766Sken				    sizeof(param),
6371331766Sken				    &param);
6372331766Sken
6373331766Sken
6374331766Sken	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6375331766Sken	if (rc) {
6376331766Sken		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6377331766Sken	} else {
6378331766Sken		ocs_log_test(hw->os, "Auto XFER RDY T10 PI configured rtc:%d atv:%d p_type:%d app_tag:%x blk_size:%d\n",
6379331766Sken				param.rtc, param.atv, param.p_type,
6380331766Sken				param.app_tag, param.blk_size);
6381331766Sken	}
6382331766Sken
6383331766Sken	return rc;
6384331766Sken}
6385331766Sken
6386331766Sken
6387331766Sken/**
6388331766Sken * @brief enable sli port health check
6389331766Sken *
6390331766Sken * @param hw Hardware context.
6391331766Sken * @param buf Pointer to a mailbox buffer area.
6392331766Sken * @param query current status of the health check feature enabled/disabled
6393331766Sken * @param enable if 1: enable 0: disable
6394331766Sken * @param buf Pointer to a mailbox buffer area.
6395331766Sken *
6396331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6397331766Sken */
6398331766Skenstatic ocs_hw_rtn_e
6399331766Skenocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable)
6400331766Sken{
6401331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6402331766Sken	uint8_t buf[SLI4_BMBX_SIZE];
6403331766Sken	sli4_req_common_set_features_health_check_t param;
6404331766Sken
6405331766Sken	ocs_memset(&param, 0, sizeof(param));
6406331766Sken	param.hck = enable;
6407331766Sken	param.qry = query;
6408331766Sken
6409331766Sken	/* build the set_features command */
6410331766Sken	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6411331766Sken				    SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK,
6412331766Sken				    sizeof(param),
6413331766Sken				    &param);
6414331766Sken
6415331766Sken	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6416331766Sken	if (rc) {
6417331766Sken		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6418331766Sken	} else {
6419331766Sken		ocs_log_test(hw->os, "SLI Port Health Check is enabled \n");
6420331766Sken	}
6421331766Sken
6422331766Sken	return rc;
6423331766Sken}
6424331766Sken
6425331766Sken/**
6426331766Sken * @brief Set FTD transfer hint feature
6427331766Sken *
6428331766Sken * @param hw Hardware context.
6429331766Sken * @param fdt_xfer_hint size in bytes where read requests are segmented.
6430331766Sken *
6431331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6432331766Sken */
6433331766Skenstatic ocs_hw_rtn_e
6434331766Skenocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint)
6435331766Sken{
6436331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6437331766Sken	uint8_t buf[SLI4_BMBX_SIZE];
6438331766Sken	sli4_req_common_set_features_set_fdt_xfer_hint_t param;
6439331766Sken
6440331766Sken	ocs_memset(&param, 0, sizeof(param));
6441331766Sken	param.fdt_xfer_hint = fdt_xfer_hint;
6442331766Sken	/* build the set_features command */
6443331766Sken	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6444331766Sken				    SLI4_SET_FEATURES_SET_FTD_XFER_HINT,
6445331766Sken				    sizeof(param),
6446331766Sken				    &param);
6447331766Sken
6448331766Sken
6449331766Sken	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6450331766Sken	if (rc) {
6451331766Sken		ocs_log_warn(hw->os, "set FDT hint %d failed: %d\n", fdt_xfer_hint, rc);
6452331766Sken	} else {
6453331766Sken		ocs_log_debug(hw->os, "Set FTD transfer hint to %d\n", param.fdt_xfer_hint);
6454331766Sken	}
6455331766Sken
6456331766Sken	return rc;
6457331766Sken}
6458331766Sken
6459331766Sken/**
6460331766Sken * @brief Get the link configuration callback.
6461331766Sken *
6462331766Sken * @param hw Hardware context.
6463331766Sken * @param status Status from the DMTF CLP command.
6464331766Sken * @param result_len Length, in bytes, of the DMTF CLP result.
6465331766Sken * @param arg Pointer to a callback argument.
6466331766Sken *
6467331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6468331766Sken */
6469331766Skenstatic void
6470331766Skenocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg)
6471331766Sken{
6472331766Sken	int32_t rval;
6473331766Sken	char retdata_str[64];
6474331766Sken	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6475331766Sken	ocs_hw_linkcfg_e linkcfg = OCS_HW_LINKCFG_NA;
6476331766Sken
6477331766Sken	if (status) {
6478331766Sken		ocs_log_test(hw->os, "CLP cmd failed, status=%d\n", status);
6479331766Sken	} else {
6480331766Sken		/* parse CLP response to get return data */
6481331766Sken		rval = ocs_hw_clp_resp_get_value(hw, "retdata", retdata_str,
6482331766Sken						  sizeof(retdata_str),
6483331766Sken						  cb_arg->dma_resp.virt,
6484331766Sken						  result_len);
6485331766Sken
6486331766Sken		if (rval <= 0) {
6487331766Sken			ocs_log_err(hw->os, "failed to get retdata %d\n", result_len);
6488331766Sken		} else {
6489331766Sken			/* translate string into hw enum */
6490331766Sken			linkcfg = ocs_hw_linkcfg_from_clp(retdata_str);
6491331766Sken		}
6492331766Sken	}
6493331766Sken
6494331766Sken	/* invoke callback */
6495331766Sken	if (cb_arg->cb) {
6496331766Sken		cb_arg->cb(status, linkcfg, cb_arg->arg);
6497331766Sken	}
6498331766Sken
6499331766Sken	/* if polling, will free memory in calling function */
6500331766Sken	if (cb_arg->opts != OCS_CMD_POLL) {
6501331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6502331766Sken		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6503331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6504331766Sken	}
6505331766Sken}
6506331766Sken
6507331766Sken/**
6508331766Sken * @brief Set the Lancer dump location
6509331766Sken * @par Description
6510331766Sken * This function tells a Lancer chip to use a specific DMA
6511331766Sken * buffer as a dump location rather than the internal flash.
6512331766Sken *
6513331766Sken * @param hw Hardware context.
6514331766Sken * @param num_buffers The number of DMA buffers to hold the dump (1..n).
6515331766Sken * @param dump_buffers DMA buffers to hold the dump.
6516331766Sken *
6517331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6518331766Sken */
6519331766Skenocs_hw_rtn_e
6520331766Skenocs_hw_set_dump_location(ocs_hw_t *hw, uint32_t num_buffers, ocs_dma_t *dump_buffers, uint8_t fdb)
6521331766Sken{
6522331766Sken	uint8_t bus, dev, func;
6523331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6524331766Sken	uint8_t	buf[SLI4_BMBX_SIZE];
6525331766Sken
6526331766Sken	/*
6527331766Sken	 * Make sure the FW is new enough to support this command. If the FW
6528331766Sken	 * is too old, the FW will UE.
6529331766Sken	 */
6530331766Sken	if (hw->workaround.disable_dump_loc) {
6531331766Sken		ocs_log_test(hw->os, "FW version is too old for this feature\n");
6532331766Sken		return OCS_HW_RTN_ERROR;
6533331766Sken	}
6534331766Sken
6535331766Sken	/* This command is only valid for physical port 0 */
6536331766Sken	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
6537331766Sken	if (fdb == 0 && func != 0) {
6538331766Sken		ocs_log_test(hw->os, "function only valid for pci function 0, %d passed\n",
6539331766Sken			     func);
6540331766Sken		return OCS_HW_RTN_ERROR;
6541331766Sken	}
6542331766Sken
6543331766Sken	/*
6544331766Sken	 * If a single buffer is used, then it may be passed as is to the chip. For multiple buffers,
6545331766Sken	 * We must allocate a SGL list and then pass the address of the list to the chip.
6546331766Sken	 */
6547331766Sken	if (num_buffers > 1) {
6548331766Sken		uint32_t sge_size = num_buffers * sizeof(sli4_sge_t);
6549331766Sken		sli4_sge_t *sge;
6550331766Sken		uint32_t i;
6551331766Sken
6552331766Sken		if (hw->dump_sges.size < sge_size) {
6553331766Sken			ocs_dma_free(hw->os, &hw->dump_sges);
6554331766Sken			if (ocs_dma_alloc(hw->os, &hw->dump_sges, sge_size, OCS_MIN_DMA_ALIGNMENT)) {
6555331766Sken				ocs_log_err(hw->os, "SGE DMA allocation failed\n");
6556331766Sken				return OCS_HW_RTN_NO_MEMORY;
6557331766Sken			}
6558331766Sken		}
6559331766Sken		/* build the SGE list */
6560331766Sken		ocs_memset(hw->dump_sges.virt, 0, hw->dump_sges.size);
6561331766Sken		hw->dump_sges.len = sge_size;
6562331766Sken		sge = hw->dump_sges.virt;
6563331766Sken		for (i = 0; i < num_buffers; i++) {
6564331766Sken			sge[i].buffer_address_high = ocs_addr32_hi(dump_buffers[i].phys);
6565331766Sken			sge[i].buffer_address_low = ocs_addr32_lo(dump_buffers[i].phys);
6566331766Sken			sge[i].last = (i == num_buffers - 1 ? 1 : 0);
6567331766Sken			sge[i].buffer_length = dump_buffers[i].size;
6568331766Sken		}
6569331766Sken		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6570331766Sken						      SLI4_BMBX_SIZE, FALSE, TRUE,
6571331766Sken						      &hw->dump_sges, fdb);
6572331766Sken	} else {
6573331766Sken		dump_buffers->len = dump_buffers->size;
6574331766Sken		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6575331766Sken						      SLI4_BMBX_SIZE, FALSE, FALSE,
6576331766Sken						      dump_buffers, fdb);
6577331766Sken	}
6578331766Sken
6579331766Sken	if (rc) {
6580331766Sken		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL,
6581331766Sken				     NULL, NULL);
6582331766Sken		if (rc) {
6583331766Sken			ocs_log_err(hw->os, "ocs_hw_command returns %d\n",
6584331766Sken				rc);
6585331766Sken		}
6586331766Sken	} else {
6587331766Sken		ocs_log_err(hw->os,
6588331766Sken			"sli_cmd_common_set_dump_location failed\n");
6589331766Sken		rc = OCS_HW_RTN_ERROR;
6590331766Sken	}
6591331766Sken
6592331766Sken	return rc;
6593331766Sken}
6594331766Sken
6595331766Sken
6596331766Sken/**
6597331766Sken * @brief Set the Ethernet license.
6598331766Sken *
6599331766Sken * @par Description
6600331766Sken * This function sends the appropriate mailbox command (DMTF
6601331766Sken * CLP) to set the Ethernet license to the given license value.
6602331766Sken * Since it is used during the time of ocs_hw_init(), the mailbox
6603331766Sken * command is sent via polling (the BMBX route).
6604331766Sken *
6605331766Sken * @param hw Hardware context.
6606331766Sken * @param license 32-bit license value.
6607331766Sken *
6608331766Sken * @return Returns OCS_HW_RTN_SUCCESS on success.
6609331766Sken */
6610331766Skenstatic ocs_hw_rtn_e
6611331766Skenocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license)
6612331766Sken{
6613331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6614331766Sken	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6615331766Sken	ocs_dma_t dma_cmd;
6616331766Sken	ocs_dma_t dma_resp;
6617331766Sken
6618331766Sken	/* only for lancer right now */
6619331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6620331766Sken		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6621331766Sken		return OCS_HW_RTN_ERROR;
6622331766Sken	}
6623331766Sken
6624331766Sken	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_Ethernet_License=%X", license);
6625331766Sken	/* allocate DMA for command  */
6626331766Sken	if (ocs_dma_alloc(hw->os, &dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6627331766Sken		ocs_log_err(hw->os, "malloc failed\n");
6628331766Sken		return OCS_HW_RTN_NO_MEMORY;
6629331766Sken	}
6630331766Sken	ocs_memset(dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6631331766Sken	ocs_memcpy(dma_cmd.virt, cmd, ocs_strlen(cmd));
6632331766Sken
6633331766Sken	/* allocate DMA for response */
6634331766Sken	if (ocs_dma_alloc(hw->os, &dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6635331766Sken		ocs_log_err(hw->os, "malloc failed\n");
6636331766Sken		ocs_dma_free(hw->os, &dma_cmd);
6637331766Sken		return OCS_HW_RTN_NO_MEMORY;
6638331766Sken	}
6639331766Sken
6640331766Sken	/* send DMTF CLP command mbx and poll */
6641331766Sken	if (ocs_hw_exec_dmtf_clp_cmd(hw, &dma_cmd, &dma_resp, OCS_CMD_POLL, NULL, NULL)) {
6642331766Sken		ocs_log_err(hw->os, "CLP cmd=\"%s\" failed\n", (char *)dma_cmd.virt);
6643331766Sken		rc = OCS_HW_RTN_ERROR;
6644331766Sken	}
6645331766Sken
6646331766Sken	ocs_dma_free(hw->os, &dma_cmd);
6647331766Sken	ocs_dma_free(hw->os, &dma_resp);
6648331766Sken	return rc;
6649331766Sken}
6650331766Sken
6651331766Sken/**
6652331766Sken * @brief Callback argument structure for the DMTF CLP commands.
6653331766Sken */
6654331766Skentypedef struct ocs_hw_clp_cb_arg_s {
6655331766Sken	ocs_hw_dmtf_clp_cb_t cb;
6656331766Sken	ocs_dma_t *dma_resp;
6657331766Sken	int32_t status;
6658331766Sken	uint32_t opts;
6659331766Sken	void *arg;
6660331766Sken} ocs_hw_clp_cb_arg_t;
6661331766Sken
6662331766Sken/**
6663331766Sken * @brief Execute the DMTF CLP command.
6664331766Sken *
6665331766Sken * @param hw Hardware context.
6666331766Sken * @param dma_cmd DMA buffer containing the CLP command.
6667331766Sken * @param dma_resp DMA buffer that will contain the response (if successful).
6668331766Sken * @param opts Mailbox command options (such as OCS_CMD_NOWAIT and POLL).
6669331766Sken * @param cb Callback function.
6670331766Sken * @param arg Callback argument.
6671331766Sken *
6672331766Sken * @return Returns the number of bytes written to the response
6673331766Sken * buffer on success, or a negative value if failed.
6674331766Sken */
6675331766Skenstatic ocs_hw_rtn_e
6676331766Skenocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg)
6677331766Sken{
6678331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6679331766Sken	ocs_hw_clp_cb_arg_t *cb_arg;
6680331766Sken	uint8_t *mbxdata;
6681331766Sken
6682331766Sken	/* allocate DMA for mailbox */
6683331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6684331766Sken	if (mbxdata == NULL) {
6685331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
6686331766Sken		return OCS_HW_RTN_NO_MEMORY;
6687331766Sken	}
6688331766Sken
6689331766Sken	/* allocate memory for callback argument */
6690331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6691331766Sken	if (cb_arg == NULL) {
6692331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg");
6693331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6694331766Sken		return OCS_HW_RTN_NO_MEMORY;
6695331766Sken	}
6696331766Sken
6697331766Sken	cb_arg->cb = cb;
6698331766Sken	cb_arg->arg = arg;
6699331766Sken	cb_arg->dma_resp = dma_resp;
6700331766Sken	cb_arg->opts = opts;
6701331766Sken
6702331766Sken	/* Send the HW command */
6703331766Sken	if (sli_cmd_dmtf_exec_clp_cmd(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
6704331766Sken				      dma_cmd, dma_resp)) {
6705331766Sken		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_dmtf_clp_cb, cb_arg);
6706331766Sken
6707331766Sken		if (opts == OCS_CMD_POLL && rc == OCS_HW_RTN_SUCCESS) {
6708331766Sken			/* if we're polling, copy response and invoke callback to
6709331766Sken			 * parse result */
6710331766Sken			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
6711331766Sken			ocs_hw_dmtf_clp_cb(hw, 0, mbxdata, cb_arg);
6712331766Sken
6713331766Sken			/* set rc to resulting or "parsed" status */
6714331766Sken			rc = cb_arg->status;
6715331766Sken		}
6716331766Sken
6717331766Sken		/* if failed, or polling, free memory here */
6718331766Sken		if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6719331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
6720331766Sken				ocs_log_test(hw->os, "ocs_hw_command failed\n");
6721331766Sken			}
6722331766Sken			ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6723331766Sken			ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6724331766Sken		}
6725331766Sken	} else {
6726331766Sken		ocs_log_test(hw->os, "sli_cmd_dmtf_exec_clp_cmd failed\n");
6727331766Sken		rc = OCS_HW_RTN_ERROR;
6728331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6729331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6730331766Sken	}
6731331766Sken
6732331766Sken	return rc;
6733331766Sken}
6734331766Sken
6735331766Sken
6736331766Sken/**
6737331766Sken * @brief Called when the DMTF CLP command completes.
6738331766Sken *
6739331766Sken * @param hw Hardware context.
6740331766Sken * @param status Status field from the mbox completion.
6741331766Sken * @param mqe Mailbox response structure.
6742331766Sken * @param arg Pointer to a callback argument.
6743331766Sken *
6744331766Sken * @return None.
6745331766Sken *
6746331766Sken */
6747331766Skenstatic void
6748331766Skenocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6749331766Sken{
6750331766Sken	int32_t cb_status = 0;
6751331766Sken	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6752331766Sken	sli4_res_dmtf_exec_clp_cmd_t *clp_rsp = (sli4_res_dmtf_exec_clp_cmd_t *) mbox_rsp->payload.embed;
6753331766Sken	ocs_hw_clp_cb_arg_t *cb_arg = arg;
6754331766Sken	uint32_t result_len = 0;
6755331766Sken	int32_t stat_len;
6756331766Sken	char stat_str[8];
6757331766Sken
6758331766Sken	/* there are several status codes here, check them all and condense
6759331766Sken	 * into a single callback status
6760331766Sken	 */
6761331766Sken	if (status || mbox_rsp->hdr.status || clp_rsp->clp_status) {
6762331766Sken		ocs_log_debug(hw->os, "status=x%x/x%x/x%x  addl=x%x clp=x%x detail=x%x\n",
6763331766Sken			status,
6764331766Sken			mbox_rsp->hdr.status,
6765331766Sken			clp_rsp->hdr.status,
6766331766Sken			clp_rsp->hdr.additional_status,
6767331766Sken			clp_rsp->clp_status,
6768331766Sken			clp_rsp->clp_detailed_status);
6769331766Sken		if (status) {
6770331766Sken			cb_status = status;
6771331766Sken		} else if (mbox_rsp->hdr.status) {
6772331766Sken			cb_status = mbox_rsp->hdr.status;
6773331766Sken		} else {
6774331766Sken			cb_status = clp_rsp->clp_status;
6775331766Sken		}
6776331766Sken	} else {
6777331766Sken		result_len = clp_rsp->resp_length;
6778331766Sken	}
6779331766Sken
6780331766Sken	if (cb_status) {
6781331766Sken		goto ocs_hw_cb_dmtf_clp_done;
6782331766Sken	}
6783331766Sken
6784331766Sken	if ((result_len == 0) || (cb_arg->dma_resp->size < result_len)) {
6785331766Sken		ocs_log_test(hw->os, "Invalid response length: resp_len=%zu result len=%d\n",
6786331766Sken			     cb_arg->dma_resp->size, result_len);
6787331766Sken		cb_status = -1;
6788331766Sken		goto ocs_hw_cb_dmtf_clp_done;
6789331766Sken	}
6790331766Sken
6791331766Sken	/* parse CLP response to get status */
6792331766Sken	stat_len = ocs_hw_clp_resp_get_value(hw, "status", stat_str,
6793331766Sken					      sizeof(stat_str),
6794331766Sken					      cb_arg->dma_resp->virt,
6795331766Sken					      result_len);
6796331766Sken
6797331766Sken	if (stat_len <= 0) {
6798331766Sken		ocs_log_test(hw->os, "failed to get status %d\n", stat_len);
6799331766Sken		cb_status = -1;
6800331766Sken		goto ocs_hw_cb_dmtf_clp_done;
6801331766Sken	}
6802331766Sken
6803331766Sken	if (ocs_strcmp(stat_str, "0") != 0) {
6804331766Sken		ocs_log_test(hw->os, "CLP status indicates failure=%s\n", stat_str);
6805331766Sken		cb_status = -1;
6806331766Sken		goto ocs_hw_cb_dmtf_clp_done;
6807331766Sken	}
6808331766Sken
6809331766Skenocs_hw_cb_dmtf_clp_done:
6810331766Sken
6811331766Sken	/* save status in cb_arg for callers with NULL cb's + polling */
6812331766Sken	cb_arg->status = cb_status;
6813331766Sken	if (cb_arg->cb) {
6814331766Sken		cb_arg->cb(hw, cb_status, result_len, cb_arg->arg);
6815331766Sken	}
6816331766Sken	/* if polling, caller will free memory */
6817331766Sken	if (cb_arg->opts != OCS_CMD_POLL) {
6818331766Sken		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6819331766Sken		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6820331766Sken	}
6821331766Sken}
6822331766Sken
6823331766Sken/**
6824331766Sken * @brief Parse the CLP result and get the value corresponding to the given
6825331766Sken * keyword.
6826331766Sken *
6827331766Sken * @param hw Hardware context.
6828331766Sken * @param keyword CLP keyword for which the value is returned.
6829331766Sken * @param value Location to which the resulting value is copied.
6830331766Sken * @param value_len Length of the value parameter.
6831331766Sken * @param resp Pointer to the response buffer that is searched
6832331766Sken * for the keyword and value.
6833331766Sken * @param resp_len Length of response buffer passed in.
6834331766Sken *
6835331766Sken * @return Returns the number of bytes written to the value
6836331766Sken * buffer on success, or a negative vaue on failure.
6837331766Sken */
6838331766Skenstatic int32_t
6839331766Skenocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len)
6840331766Sken{
6841331766Sken	char *start = NULL;
6842331766Sken	char *end = NULL;
6843331766Sken
6844331766Sken	/* look for specified keyword in string */
6845331766Sken	start = ocs_strstr(resp, keyword);
6846331766Sken	if (start == NULL) {
6847331766Sken		ocs_log_test(hw->os, "could not find keyword=%s in CLP response\n",
6848331766Sken			     keyword);
6849331766Sken		return -1;
6850331766Sken	}
6851331766Sken
6852331766Sken	/* now look for '=' and go one past */
6853331766Sken	start = ocs_strchr(start, '=');
6854331766Sken	if (start == NULL) {
6855331766Sken		ocs_log_test(hw->os, "could not find \'=\' in CLP response for keyword=%s\n",
6856331766Sken			     keyword);
6857331766Sken		return -1;
6858331766Sken	}
6859331766Sken	start++;
6860331766Sken
6861331766Sken	/* \r\n terminates value */
6862331766Sken	end = ocs_strstr(start, "\r\n");
6863331766Sken	if (end == NULL) {
6864331766Sken		ocs_log_test(hw->os, "could not find \\r\\n for keyword=%s in CLP response\n",
6865331766Sken			     keyword);
6866331766Sken		return -1;
6867331766Sken	}
6868331766Sken
6869331766Sken	/* make sure given result array is big enough */
6870331766Sken	if ((end - start + 1) > value_len) {
6871331766Sken		ocs_log_test(hw->os, "value len=%d not large enough for actual=%ld\n",
6872331766Sken			     value_len, (end-start));
6873331766Sken		return -1;
6874331766Sken	}
6875331766Sken
6876331766Sken	ocs_strncpy(value, start, (end - start));
6877331766Sken	value[end-start] = '\0';
6878331766Sken	return (end-start+1);
6879331766Sken}
6880331766Sken
6881331766Sken/**
6882331766Sken * @brief Cause chip to enter an unrecoverable error state.
6883331766Sken *
6884331766Sken * @par Description
6885331766Sken * Cause chip to enter an unrecoverable error state. This is
6886331766Sken * used when detecting unexpected FW behavior so that the FW can be
6887331766Sken * hwted from the driver as soon as the error is detected.
6888331766Sken *
6889331766Sken * @param hw Hardware context.
6890331766Sken * @param dump Generate dump as part of reset.
6891331766Sken *
6892331766Sken * @return Returns 0 on success, or a non-zero value on failure.
6893331766Sken *
6894331766Sken */
6895331766Skenocs_hw_rtn_e
6896331766Skenocs_hw_raise_ue(ocs_hw_t *hw, uint8_t dump)
6897331766Sken{
6898331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6899331766Sken
6900331766Sken	if (sli_raise_ue(&hw->sli, dump) != 0) {
6901331766Sken		rc = OCS_HW_RTN_ERROR;
6902331766Sken	} else {
6903331766Sken		if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
6904331766Sken			hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
6905331766Sken		}
6906331766Sken	}
6907331766Sken
6908331766Sken	return rc;
6909331766Sken}
6910331766Sken
6911331766Sken/**
6912331766Sken * @brief Called when the OBJECT_GET command completes.
6913331766Sken *
6914331766Sken * @par Description
6915331766Sken * Get the number of bytes actually written out of the response, free the mailbox
6916331766Sken * that was malloc'd by ocs_hw_dump_get(), then call the callback
6917331766Sken * and pass the status and bytes read.
6918331766Sken *
6919331766Sken * @param hw Hardware context.
6920331766Sken * @param status Status field from the mbox completion.
6921331766Sken * @param mqe Mailbox response structure.
6922331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
6923331766Sken * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_read)</tt>.
6924331766Sken *
6925331766Sken * @return Returns 0.
6926331766Sken */
6927331766Skenstatic int32_t
6928331766Skenocs_hw_cb_dump_get(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6929331766Sken{
6930331766Sken	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6931331766Sken	sli4_res_common_read_object_t* rd_obj_rsp = (sli4_res_common_read_object_t*) mbox_rsp->payload.embed;
6932331766Sken	ocs_hw_dump_get_cb_arg_t *cb_arg = arg;
6933331766Sken	uint32_t bytes_read;
6934331766Sken	uint8_t eof;
6935331766Sken
6936331766Sken	bytes_read = rd_obj_rsp->actual_read_length;
6937331766Sken	eof = rd_obj_rsp->eof;
6938331766Sken
6939331766Sken	if (cb_arg) {
6940331766Sken		if (cb_arg->cb) {
6941331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
6942331766Sken				status = mbox_rsp->hdr.status;
6943331766Sken			}
6944331766Sken			cb_arg->cb(status, bytes_read, eof, cb_arg->arg);
6945331766Sken		}
6946331766Sken
6947331766Sken		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
6948331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
6949331766Sken	}
6950331766Sken
6951331766Sken	return 0;
6952331766Sken}
6953331766Sken
6954331766Sken
6955331766Sken/**
6956331766Sken * @brief Read a dump image to the host.
6957331766Sken *
6958331766Sken * @par Description
6959331766Sken * Creates a SLI_CONFIG mailbox command, fills in the correct values to read a
6960331766Sken * dump image chunk, then sends the command with the ocs_hw_command(). On completion,
6961331766Sken * the callback function ocs_hw_cb_dump_get() gets called to free the mailbox
6962331766Sken * and signal the caller that the read has completed.
6963331766Sken *
6964331766Sken * @param hw Hardware context.
6965331766Sken * @param dma DMA structure to transfer the dump chunk into.
6966331766Sken * @param size Size of the dump chunk.
6967331766Sken * @param offset Offset, in bytes, from the beginning of the dump.
6968331766Sken * @param cb Pointer to a callback function that is called when the command completes.
6969331766Sken * The callback function prototype is
6970331766Sken * <tt>void cb(int32_t status, uint32_t bytes_read, uint8_t eof, void *arg)</tt>.
6971331766Sken * @param arg Pointer to be passed to the callback function.
6972331766Sken *
6973331766Sken * @return Returns 0 on success, or a non-zero value on failure.
6974331766Sken */
6975331766Skenocs_hw_rtn_e
6976331766Skenocs_hw_dump_get(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, ocs_hw_dump_get_cb_t cb, void *arg)
6977331766Sken{
6978331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6979331766Sken	uint8_t *mbxdata;
6980331766Sken	ocs_hw_dump_get_cb_arg_t *cb_arg;
6981331766Sken	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
6982331766Sken
6983331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6984331766Sken		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6985331766Sken		return OCS_HW_RTN_ERROR;
6986331766Sken	}
6987331766Sken
6988331766Sken	if (1 != sli_dump_is_present(&hw->sli)) {
6989331766Sken		ocs_log_test(hw->os, "No dump is present\n");
6990331766Sken		return OCS_HW_RTN_ERROR;
6991331766Sken	}
6992331766Sken
6993331766Sken	if (1 == sli_reset_required(&hw->sli)) {
6994331766Sken		ocs_log_test(hw->os, "device reset required\n");
6995331766Sken		return OCS_HW_RTN_ERROR;
6996331766Sken	}
6997331766Sken
6998331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6999331766Sken	if (mbxdata == NULL) {
7000331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7001331766Sken		return OCS_HW_RTN_NO_MEMORY;
7002331766Sken	}
7003331766Sken
7004331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_get_cb_arg_t), OCS_M_NOWAIT);
7005331766Sken	if (cb_arg == NULL) {
7006331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7007331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7008331766Sken		return OCS_HW_RTN_NO_MEMORY;
7009331766Sken	}
7010331766Sken
7011331766Sken	cb_arg->cb = cb;
7012331766Sken	cb_arg->arg = arg;
7013331766Sken	cb_arg->mbox_cmd = mbxdata;
7014331766Sken
7015331766Sken	if (sli_cmd_common_read_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7016331766Sken			size, offset, "/dbg/dump.bin", dma)) {
7017331766Sken		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_get, cb_arg);
7018331766Sken		if (rc == 0 && opts == OCS_CMD_POLL) {
7019331766Sken			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7020331766Sken			rc = ocs_hw_cb_dump_get(hw, 0, mbxdata, cb_arg);
7021331766Sken		}
7022331766Sken	}
7023331766Sken
7024331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7025331766Sken		ocs_log_test(hw->os, "COMMON_READ_OBJECT failed\n");
7026331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7027331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
7028331766Sken	}
7029331766Sken
7030331766Sken	return rc;
7031331766Sken}
7032331766Sken
7033331766Sken/**
7034331766Sken * @brief Called when the OBJECT_DELETE command completes.
7035331766Sken *
7036331766Sken * @par Description
7037331766Sken * Free the mailbox that was malloc'd
7038331766Sken * by ocs_hw_dump_clear(), then call the callback and pass the status.
7039331766Sken *
7040331766Sken * @param hw Hardware context.
7041331766Sken * @param status Status field from the mbox completion.
7042331766Sken * @param mqe Mailbox response structure.
7043331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
7044331766Sken * The callback function prototype is <tt>void cb(int32_t status, void *arg)</tt>.
7045331766Sken *
7046331766Sken * @return Returns 0.
7047331766Sken */
7048331766Skenstatic int32_t
7049331766Skenocs_hw_cb_dump_clear(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
7050331766Sken{
7051331766Sken	ocs_hw_dump_clear_cb_arg_t *cb_arg = arg;
7052331766Sken	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7053331766Sken
7054331766Sken	if (cb_arg) {
7055331766Sken		if (cb_arg->cb) {
7056331766Sken			if ((status == 0) && mbox_rsp->hdr.status) {
7057331766Sken				status = mbox_rsp->hdr.status;
7058331766Sken			}
7059331766Sken			cb_arg->cb(status, cb_arg->arg);
7060331766Sken		}
7061331766Sken
7062331766Sken		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
7063331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7064331766Sken	}
7065331766Sken
7066331766Sken	return 0;
7067331766Sken}
7068331766Sken
7069331766Sken/**
7070331766Sken * @brief Clear a dump image from the device.
7071331766Sken *
7072331766Sken * @par Description
7073331766Sken * Creates a SLI_CONFIG mailbox command, fills it with the correct values to clear
7074331766Sken * the dump, then sends the command with ocs_hw_command(). On completion,
7075331766Sken * the callback function ocs_hw_cb_dump_clear() gets called to free the mailbox
7076331766Sken * and to signal the caller that the write has completed.
7077331766Sken *
7078331766Sken * @param hw Hardware context.
7079331766Sken * @param cb Pointer to a callback function that is called when the command completes.
7080331766Sken * The callback function prototype is
7081331766Sken * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
7082331766Sken * @param arg Pointer to be passed to the callback function.
7083331766Sken *
7084331766Sken * @return Returns 0 on success, or a non-zero value on failure.
7085331766Sken */
7086331766Skenocs_hw_rtn_e
7087331766Skenocs_hw_dump_clear(ocs_hw_t *hw, ocs_hw_dump_clear_cb_t cb, void *arg)
7088331766Sken{
7089331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7090331766Sken	uint8_t *mbxdata;
7091331766Sken	ocs_hw_dump_clear_cb_arg_t *cb_arg;
7092331766Sken	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
7093331766Sken
7094331766Sken	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
7095331766Sken		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
7096331766Sken		return OCS_HW_RTN_ERROR;
7097331766Sken	}
7098331766Sken
7099331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7100331766Sken	if (mbxdata == NULL) {
7101331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7102331766Sken		return OCS_HW_RTN_NO_MEMORY;
7103331766Sken	}
7104331766Sken
7105331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_clear_cb_arg_t), OCS_M_NOWAIT);
7106331766Sken	if (cb_arg == NULL) {
7107331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7108331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7109331766Sken		return OCS_HW_RTN_NO_MEMORY;
7110331766Sken	}
7111331766Sken
7112331766Sken	cb_arg->cb = cb;
7113331766Sken	cb_arg->arg = arg;
7114331766Sken	cb_arg->mbox_cmd = mbxdata;
7115331766Sken
7116331766Sken	if (sli_cmd_common_delete_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7117331766Sken			"/dbg/dump.bin")) {
7118331766Sken		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_clear, cb_arg);
7119331766Sken		if (rc == 0 && opts == OCS_CMD_POLL) {
7120331766Sken			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7121331766Sken			rc = ocs_hw_cb_dump_clear(hw, 0, mbxdata, cb_arg);
7122331766Sken		}
7123331766Sken	}
7124331766Sken
7125331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7126331766Sken		ocs_log_test(hw->os, "COMMON_DELETE_OBJECT failed\n");
7127331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7128331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7129331766Sken	}
7130331766Sken
7131331766Sken	return rc;
7132331766Sken}
7133331766Sken
7134331766Skentypedef struct ocs_hw_get_port_protocol_cb_arg_s {
7135331766Sken	ocs_get_port_protocol_cb_t cb;
7136331766Sken	void *arg;
7137331766Sken	uint32_t pci_func;
7138331766Sken	ocs_dma_t payload;
7139331766Sken} ocs_hw_get_port_protocol_cb_arg_t;
7140331766Sken
7141331766Sken/**
7142331766Sken * @brief Called for the completion of get_port_profile for a
7143331766Sken *        user request.
7144331766Sken *
7145331766Sken * @param hw Hardware context.
7146331766Sken * @param status The status from the MQE.
7147331766Sken * @param mqe Pointer to mailbox command buffer.
7148331766Sken * @param arg Pointer to a callback argument.
7149331766Sken *
7150331766Sken * @return Returns 0 on success, or a non-zero value on failure.
7151331766Sken */
7152331766Skenstatic int32_t
7153331766Skenocs_hw_get_port_protocol_cb(ocs_hw_t *hw, int32_t status,
7154331766Sken			    uint8_t *mqe, void *arg)
7155331766Sken{
7156331766Sken	ocs_hw_get_port_protocol_cb_arg_t *cb_arg = arg;
7157331766Sken	ocs_dma_t *payload = &(cb_arg->payload);
7158331766Sken	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7159331766Sken	ocs_hw_port_protocol_e port_protocol;
7160331766Sken	int num_descriptors;
7161331766Sken	sli4_resource_descriptor_v1_t *desc_p;
7162331766Sken	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7163331766Sken	int i;
7164331766Sken
7165331766Sken	port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7166331766Sken
7167331766Sken	num_descriptors = response->desc_count;
7168331766Sken	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7169331766Sken	for (i=0; i<num_descriptors; i++) {
7170331766Sken		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7171331766Sken			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7172331766Sken			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7173331766Sken				switch(pcie_desc_p->pf_type) {
7174331766Sken				case 0x02:
7175331766Sken					port_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
7176331766Sken					break;
7177331766Sken				case 0x04:
7178331766Sken					port_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
7179331766Sken					break;
7180331766Sken				case 0x10:
7181331766Sken					port_protocol = OCS_HW_PORT_PROTOCOL_FC;
7182331766Sken					break;
7183331766Sken				default:
7184331766Sken					port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7185331766Sken					break;
7186331766Sken				}
7187331766Sken			}
7188331766Sken		}
7189331766Sken
7190331766Sken		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7191331766Sken	}
7192331766Sken
7193331766Sken	if (cb_arg->cb) {
7194331766Sken		cb_arg->cb(status, port_protocol, cb_arg->arg);
7195331766Sken
7196331766Sken	}
7197331766Sken
7198331766Sken	ocs_dma_free(hw->os, &cb_arg->payload);
7199331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7200331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7201331766Sken
7202331766Sken	return 0;
7203331766Sken}
7204331766Sken
7205331766Sken/**
7206331766Sken * @ingroup io
7207331766Sken * @brief  Get the current port protocol.
7208331766Sken * @par Description
7209331766Sken * Issues a SLI4 COMMON_GET_PROFILE_CONFIG mailbox.  When the
7210331766Sken * command completes the provided mgmt callback function is
7211331766Sken * called.
7212331766Sken *
7213331766Sken * @param hw Hardware context.
7214331766Sken * @param pci_func PCI function to query for current protocol.
7215331766Sken * @param cb Callback function to be called when the command completes.
7216331766Sken * @param ul_arg An argument that is passed to the callback function.
7217331766Sken *
7218331766Sken * @return
7219331766Sken * - OCS_HW_RTN_SUCCESS on success.
7220331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7221331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7222331766Sken *   context.
7223331766Sken * - OCS_HW_RTN_ERROR on any other error.
7224331766Sken */
7225331766Skenocs_hw_rtn_e
7226331766Skenocs_hw_get_port_protocol(ocs_hw_t *hw, uint32_t pci_func,
7227331766Sken	ocs_get_port_protocol_cb_t cb, void* ul_arg)
7228331766Sken{
7229331766Sken	uint8_t *mbxdata;
7230331766Sken	ocs_hw_get_port_protocol_cb_arg_t *cb_arg;
7231331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7232331766Sken
7233331766Sken	/* Only supported on Skyhawk */
7234331766Sken	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7235331766Sken		return OCS_HW_RTN_ERROR;
7236331766Sken	}
7237331766Sken
7238331766Sken	/* mbxdata holds the header of the command */
7239331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7240331766Sken	if (mbxdata == NULL) {
7241331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7242331766Sken		return OCS_HW_RTN_NO_MEMORY;
7243331766Sken	}
7244331766Sken
7245331766Sken
7246331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7247331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7248331766Sken	if (cb_arg == NULL) {
7249331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7250331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7251331766Sken		return OCS_HW_RTN_NO_MEMORY;
7252331766Sken	}
7253331766Sken
7254331766Sken	cb_arg->cb = cb;
7255331766Sken	cb_arg->arg = ul_arg;
7256331766Sken	cb_arg->pci_func = pci_func;
7257331766Sken
7258331766Sken	/* dma_mem holds the non-embedded portion */
7259331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7260331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7261331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7262331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7263331766Sken		return OCS_HW_RTN_NO_MEMORY;
7264331766Sken	}
7265331766Sken
7266331766Sken	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7267331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_port_protocol_cb, cb_arg);
7268331766Sken	}
7269331766Sken
7270331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7271331766Sken		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7272331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7273331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7274331766Sken		ocs_dma_free(hw->os, &cb_arg->payload);
7275331766Sken	}
7276331766Sken
7277331766Sken	return rc;
7278331766Sken
7279331766Sken}
7280331766Sken
7281331766Skentypedef struct ocs_hw_set_port_protocol_cb_arg_s {
7282331766Sken	ocs_set_port_protocol_cb_t cb;
7283331766Sken	void *arg;
7284331766Sken	ocs_dma_t payload;
7285331766Sken	uint32_t new_protocol;
7286331766Sken	uint32_t pci_func;
7287331766Sken} ocs_hw_set_port_protocol_cb_arg_t;
7288331766Sken
7289331766Sken/**
7290331766Sken * @brief Called for the completion of set_port_profile for a
7291331766Sken *        user request.
7292331766Sken *
7293331766Sken * @par Description
7294331766Sken * This is the second of two callbacks for the set_port_protocol
7295331766Sken * function. The set operation is a read-modify-write. This
7296331766Sken * callback is called when the write (SET_PROFILE_CONFIG)
7297331766Sken * completes.
7298331766Sken *
7299331766Sken * @param hw Hardware context.
7300331766Sken * @param status The status from the MQE.
7301331766Sken * @param mqe Pointer to mailbox command buffer.
7302331766Sken * @param arg Pointer to a callback argument.
7303331766Sken *
7304331766Sken * @return 0 on success, non-zero otherwise
7305331766Sken */
7306331766Skenstatic int32_t
7307331766Skenocs_hw_set_port_protocol_cb2(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7308331766Sken{
7309331766Sken	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7310331766Sken
7311331766Sken	if (cb_arg->cb) {
7312331766Sken		cb_arg->cb( status, cb_arg->arg);
7313331766Sken	}
7314331766Sken
7315331766Sken	ocs_dma_free(hw->os, &(cb_arg->payload));
7316331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7317331766Sken	ocs_free(hw->os, arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7318331766Sken
7319331766Sken	return 0;
7320331766Sken}
7321331766Sken
7322331766Sken/**
7323331766Sken * @brief Called for the completion of set_port_profile for a
7324331766Sken *        user request.
7325331766Sken *
7326331766Sken * @par Description
7327331766Sken * This is the first of two callbacks for the set_port_protocol
7328331766Sken * function.  The set operation is a read-modify-write.  This
7329331766Sken * callback is called when the read completes
7330331766Sken * (GET_PROFILE_CONFG).  It will updated the resource
7331331766Sken * descriptors, then queue the write (SET_PROFILE_CONFIG).
7332331766Sken *
7333331766Sken * On entry there are three memory areas that were allocated by
7334331766Sken * ocs_hw_set_port_protocol.  If a failure is detected in this
7335331766Sken * function those need to be freed.  If this function succeeds
7336331766Sken * it allocates three more areas.
7337331766Sken *
7338331766Sken * @param hw Hardware context.
7339331766Sken * @param status The status from the MQE
7340331766Sken * @param mqe Pointer to mailbox command buffer.
7341331766Sken * @param arg Pointer to a callback argument.
7342331766Sken *
7343331766Sken * @return Returns 0 on success, or a non-zero value otherwise.
7344331766Sken */
7345331766Skenstatic int32_t
7346331766Skenocs_hw_set_port_protocol_cb1(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7347331766Sken{
7348331766Sken	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7349331766Sken	ocs_dma_t *payload = &(cb_arg->payload);
7350331766Sken	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7351331766Sken	int num_descriptors;
7352331766Sken	sli4_resource_descriptor_v1_t *desc_p;
7353331766Sken	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7354331766Sken	int i;
7355331766Sken	ocs_hw_set_port_protocol_cb_arg_t *new_cb_arg;
7356331766Sken	ocs_hw_port_protocol_e new_protocol;
7357331766Sken	uint8_t *dst;
7358331766Sken	sli4_isap_resouce_descriptor_v1_t *isap_desc_p;
7359331766Sken	uint8_t *mbxdata;
7360331766Sken	int pci_descriptor_count;
7361331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7362331766Sken	int num_fcoe_ports = 0;
7363331766Sken	int num_iscsi_ports = 0;
7364331766Sken
7365331766Sken	new_protocol = (ocs_hw_port_protocol_e)cb_arg->new_protocol;
7366331766Sken
7367331766Sken	num_descriptors = response->desc_count;
7368331766Sken
7369331766Sken	/* Count PCI descriptors */
7370331766Sken	pci_descriptor_count = 0;
7371331766Sken	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7372331766Sken	for (i=0; i<num_descriptors; i++) {
7373331766Sken		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7374331766Sken			++pci_descriptor_count;
7375331766Sken		}
7376331766Sken		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7377331766Sken	}
7378331766Sken
7379331766Sken	/* mbxdata holds the header of the command */
7380331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7381331766Sken	if (mbxdata == NULL) {
7382331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7383331766Sken		return OCS_HW_RTN_NO_MEMORY;
7384331766Sken	}
7385331766Sken
7386331766Sken
7387331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7388331766Sken	new_cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7389331766Sken	if (new_cb_arg == NULL) {
7390331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7391331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7392331766Sken		return OCS_HW_RTN_NO_MEMORY;
7393331766Sken	}
7394331766Sken
7395331766Sken	new_cb_arg->cb = cb_arg->cb;
7396331766Sken	new_cb_arg->arg = cb_arg->arg;
7397331766Sken
7398331766Sken	/* Allocate memory for the descriptors we're going to send.  This is
7399331766Sken	 * one for each PCI descriptor plus one ISAP descriptor. */
7400331766Sken	if (ocs_dma_alloc(hw->os, &new_cb_arg->payload, sizeof(sli4_req_common_set_profile_config_t) +
7401331766Sken			  (pci_descriptor_count * sizeof(sli4_pcie_resource_descriptor_v1_t)) +
7402331766Sken			  sizeof(sli4_isap_resouce_descriptor_v1_t), 4)) {
7403331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7404331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7405331766Sken		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7406331766Sken		return OCS_HW_RTN_NO_MEMORY;
7407331766Sken	}
7408331766Sken
7409331766Sken	sli_cmd_common_set_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7410331766Sken						   &new_cb_arg->payload,
7411331766Sken						   0, pci_descriptor_count+1, 1);
7412331766Sken
7413331766Sken	/* Point dst to the first descriptor entry in the SET_PROFILE_CONFIG command */
7414331766Sken	dst = (uint8_t *)&(((sli4_req_common_set_profile_config_t *) new_cb_arg->payload.virt)->desc);
7415331766Sken
7416331766Sken	/* Loop over all descriptors.  If the descriptor is a PCIe descriptor, copy it
7417331766Sken	 * to the SET_PROFILE_CONFIG command to be written back.  If it's the descriptor
7418331766Sken	 * that we're trying to change also set its pf_type.
7419331766Sken	 */
7420331766Sken	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7421331766Sken	for (i=0; i<num_descriptors; i++) {
7422331766Sken		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7423331766Sken			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7424331766Sken			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7425331766Sken				/* This is the PCIe descriptor for this OCS instance.
7426331766Sken				 * Update it with the new pf_type */
7427331766Sken				switch(new_protocol) {
7428331766Sken				case OCS_HW_PORT_PROTOCOL_FC:
7429331766Sken					pcie_desc_p->pf_type = SLI4_PROTOCOL_FC;
7430331766Sken					break;
7431331766Sken				case OCS_HW_PORT_PROTOCOL_FCOE:
7432331766Sken					pcie_desc_p->pf_type = SLI4_PROTOCOL_FCOE;
7433331766Sken					break;
7434331766Sken				case OCS_HW_PORT_PROTOCOL_ISCSI:
7435331766Sken					pcie_desc_p->pf_type = SLI4_PROTOCOL_ISCSI;
7436331766Sken					break;
7437331766Sken				default:
7438331766Sken					pcie_desc_p->pf_type = SLI4_PROTOCOL_DEFAULT;
7439331766Sken					break;
7440331766Sken				}
7441331766Sken
7442331766Sken			}
7443331766Sken
7444331766Sken			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_FCOE) {
7445331766Sken				++num_fcoe_ports;
7446331766Sken			}
7447331766Sken			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_ISCSI) {
7448331766Sken				++num_iscsi_ports;
7449331766Sken			}
7450331766Sken			ocs_memcpy(dst, pcie_desc_p, sizeof(sli4_pcie_resource_descriptor_v1_t));
7451331766Sken			dst += sizeof(sli4_pcie_resource_descriptor_v1_t);
7452331766Sken		}
7453331766Sken
7454331766Sken		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7455331766Sken	}
7456331766Sken
7457331766Sken	/* Create an ISAP resource descriptor */
7458331766Sken	isap_desc_p = (sli4_isap_resouce_descriptor_v1_t*)dst;
7459331766Sken	isap_desc_p->descriptor_type = SLI4_RESOURCE_DESCRIPTOR_TYPE_ISAP;
7460331766Sken	isap_desc_p->descriptor_length = sizeof(sli4_isap_resouce_descriptor_v1_t);
7461331766Sken	if (num_iscsi_ports > 0) {
7462331766Sken		isap_desc_p->iscsi_tgt = 1;
7463331766Sken		isap_desc_p->iscsi_ini = 1;
7464331766Sken		isap_desc_p->iscsi_dif = 1;
7465331766Sken	}
7466331766Sken	if (num_fcoe_ports > 0) {
7467331766Sken		isap_desc_p->fcoe_tgt = 1;
7468331766Sken		isap_desc_p->fcoe_ini = 1;
7469331766Sken		isap_desc_p->fcoe_dif = 1;
7470331766Sken	}
7471331766Sken
7472331766Sken	/* At this point we're done with the memory allocated by ocs_port_set_protocol */
7473331766Sken	ocs_dma_free(hw->os, &cb_arg->payload);
7474331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7475331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7476331766Sken
7477331766Sken
7478331766Sken	/* Send a SET_PROFILE_CONFIG mailbox command with the new descriptors */
7479331766Sken	rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb2, new_cb_arg);
7480331766Sken	if (rc) {
7481331766Sken		ocs_log_err(hw->os, "Error posting COMMON_SET_PROFILE_CONFIG\n");
7482331766Sken		/* Call the upper level callback to report a failure */
7483331766Sken		if (new_cb_arg->cb) {
7484331766Sken			new_cb_arg->cb( rc, new_cb_arg->arg);
7485331766Sken		}
7486331766Sken
7487331766Sken		/* Free the memory allocated by this function */
7488331766Sken		ocs_dma_free(hw->os, &new_cb_arg->payload);
7489331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7490331766Sken		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7491331766Sken	}
7492331766Sken
7493331766Sken
7494331766Sken	return rc;
7495331766Sken}
7496331766Sken
7497331766Sken/**
7498331766Sken * @ingroup io
7499331766Sken * @brief  Set the port protocol.
7500331766Sken * @par Description
7501331766Sken * Setting the port protocol is a read-modify-write operation.
7502331766Sken * This function submits a GET_PROFILE_CONFIG command to read
7503331766Sken * the current settings.  The callback function will modify the
7504331766Sken * settings and issue the write.
7505331766Sken *
7506331766Sken * On successful completion this function will have allocated
7507331766Sken * two regular memory areas and one dma area which will need to
7508331766Sken * get freed later in the callbacks.
7509331766Sken *
7510331766Sken * @param hw Hardware context.
7511331766Sken * @param new_protocol New protocol to use.
7512331766Sken * @param pci_func PCI function to configure.
7513331766Sken * @param cb Callback function to be called when the command completes.
7514331766Sken * @param ul_arg An argument that is passed to the callback function.
7515331766Sken *
7516331766Sken * @return
7517331766Sken * - OCS_HW_RTN_SUCCESS on success.
7518331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7519331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7520331766Sken *   context.
7521331766Sken * - OCS_HW_RTN_ERROR on any other error.
7522331766Sken */
7523331766Skenocs_hw_rtn_e
7524331766Skenocs_hw_set_port_protocol(ocs_hw_t *hw, ocs_hw_port_protocol_e new_protocol,
7525331766Sken		uint32_t pci_func, ocs_set_port_protocol_cb_t cb, void *ul_arg)
7526331766Sken{
7527331766Sken	uint8_t *mbxdata;
7528331766Sken	ocs_hw_set_port_protocol_cb_arg_t *cb_arg;
7529331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7530331766Sken
7531331766Sken	/* Only supported on Skyhawk */
7532331766Sken	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7533331766Sken		return OCS_HW_RTN_ERROR;
7534331766Sken	}
7535331766Sken
7536331766Sken	/* mbxdata holds the header of the command */
7537331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7538331766Sken	if (mbxdata == NULL) {
7539331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7540331766Sken		return OCS_HW_RTN_NO_MEMORY;
7541331766Sken	}
7542331766Sken
7543331766Sken
7544331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7545331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7546331766Sken	if (cb_arg == NULL) {
7547331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7548331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7549331766Sken		return OCS_HW_RTN_NO_MEMORY;
7550331766Sken	}
7551331766Sken
7552331766Sken	cb_arg->cb = cb;
7553331766Sken	cb_arg->arg = ul_arg;
7554331766Sken	cb_arg->new_protocol = new_protocol;
7555331766Sken	cb_arg->pci_func = pci_func;
7556331766Sken
7557331766Sken	/* dma_mem holds the non-embedded portion */
7558331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7559331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7560331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7561331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7562331766Sken		return OCS_HW_RTN_NO_MEMORY;
7563331766Sken	}
7564331766Sken
7565331766Sken	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7566331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb1, cb_arg);
7567331766Sken	}
7568331766Sken
7569331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7570331766Sken		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7571331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7572331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7573331766Sken		ocs_dma_free(hw->os, &cb_arg->payload);
7574331766Sken	}
7575331766Sken
7576331766Sken	return rc;
7577331766Sken}
7578331766Sken
7579331766Skentypedef struct ocs_hw_get_profile_list_cb_arg_s {
7580331766Sken	ocs_get_profile_list_cb_t cb;
7581331766Sken	void *arg;
7582331766Sken	ocs_dma_t payload;
7583331766Sken} ocs_hw_get_profile_list_cb_arg_t;
7584331766Sken
7585331766Sken/**
7586331766Sken * @brief Called for the completion of get_profile_list for a
7587331766Sken *        user request.
7588331766Sken * @par Description
7589331766Sken * This function is called when the COMMMON_GET_PROFILE_LIST
7590331766Sken * mailbox completes.  The response will be in
7591331766Sken * ctx->non_embedded_mem.virt.  This function parses the
7592331766Sken * response and creates a ocs_hw_profile_list, then calls the
7593331766Sken * mgmt_cb callback function and passes that list to it.
7594331766Sken *
7595331766Sken * @param hw Hardware context.
7596331766Sken * @param status The status from the MQE
7597331766Sken * @param mqe Pointer to mailbox command buffer.
7598331766Sken * @param arg Pointer to a callback argument.
7599331766Sken *
7600331766Sken * @return Returns 0 on success, or a non-zero value on failure.
7601331766Sken */
7602331766Skenstatic int32_t
7603331766Skenocs_hw_get_profile_list_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7604331766Sken{
7605331766Sken	ocs_hw_profile_list_t *list;
7606331766Sken	ocs_hw_get_profile_list_cb_arg_t *cb_arg = arg;
7607331766Sken	ocs_dma_t *payload = &(cb_arg->payload);
7608331766Sken	sli4_res_common_get_profile_list_t *response = (sli4_res_common_get_profile_list_t *)payload->virt;
7609331766Sken	int i;
7610331766Sken	int num_descriptors;
7611331766Sken
7612331766Sken	list = ocs_malloc(hw->os, sizeof(ocs_hw_profile_list_t), OCS_M_ZERO);
7613331766Sken	list->num_descriptors = response->profile_descriptor_count;
7614331766Sken
7615331766Sken	num_descriptors = list->num_descriptors;
7616331766Sken	if (num_descriptors > OCS_HW_MAX_PROFILES) {
7617331766Sken		num_descriptors = OCS_HW_MAX_PROFILES;
7618331766Sken	}
7619331766Sken
7620331766Sken	for (i=0; i<num_descriptors; i++) {
7621331766Sken		list->descriptors[i].profile_id = response->profile_descriptor[i].profile_id;
7622331766Sken		list->descriptors[i].profile_index = response->profile_descriptor[i].profile_index;
7623331766Sken		ocs_strcpy(list->descriptors[i].profile_description, (char *)response->profile_descriptor[i].profile_description);
7624331766Sken	}
7625331766Sken
7626331766Sken	if (cb_arg->cb) {
7627331766Sken		cb_arg->cb(status, list, cb_arg->arg);
7628331766Sken	} else {
7629331766Sken		ocs_free(hw->os, list, sizeof(*list));
7630331766Sken	}
7631331766Sken
7632331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7633331766Sken	ocs_dma_free(hw->os, &cb_arg->payload);
7634331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7635331766Sken
7636331766Sken	return 0;
7637331766Sken}
7638331766Sken
7639331766Sken/**
7640331766Sken * @ingroup io
7641331766Sken * @brief  Get a list of available profiles.
7642331766Sken * @par Description
7643331766Sken * Issues a SLI-4 COMMON_GET_PROFILE_LIST mailbox.  When the
7644331766Sken * command completes the provided mgmt callback function is
7645331766Sken * called.
7646331766Sken *
7647331766Sken * @param hw Hardware context.
7648331766Sken * @param cb Callback function to be called when the
7649331766Sken *      	  command completes.
7650331766Sken * @param ul_arg An argument that is passed to the callback
7651331766Sken *      	 function.
7652331766Sken *
7653331766Sken * @return
7654331766Sken * - OCS_HW_RTN_SUCCESS on success.
7655331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7656331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7657331766Sken *   context.
7658331766Sken * - OCS_HW_RTN_ERROR on any other error.
7659331766Sken */
7660331766Skenocs_hw_rtn_e
7661331766Skenocs_hw_get_profile_list(ocs_hw_t *hw, ocs_get_profile_list_cb_t cb, void* ul_arg)
7662331766Sken{
7663331766Sken	uint8_t *mbxdata;
7664331766Sken	ocs_hw_get_profile_list_cb_arg_t *cb_arg;
7665331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7666331766Sken
7667331766Sken	/* Only supported on Skyhawk */
7668331766Sken	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7669331766Sken		return OCS_HW_RTN_ERROR;
7670331766Sken	}
7671331766Sken
7672331766Sken	/* mbxdata holds the header of the command */
7673331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7674331766Sken	if (mbxdata == NULL) {
7675331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7676331766Sken		return OCS_HW_RTN_NO_MEMORY;
7677331766Sken	}
7678331766Sken
7679331766Sken
7680331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7681331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_profile_list_cb_arg_t), OCS_M_NOWAIT);
7682331766Sken	if (cb_arg == NULL) {
7683331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7684331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7685331766Sken		return OCS_HW_RTN_NO_MEMORY;
7686331766Sken	}
7687331766Sken
7688331766Sken	cb_arg->cb = cb;
7689331766Sken	cb_arg->arg = ul_arg;
7690331766Sken
7691331766Sken	/* dma_mem holds the non-embedded portion */
7692331766Sken	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_get_profile_list_t), 4)) {
7693331766Sken		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7694331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7695331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7696331766Sken		return OCS_HW_RTN_NO_MEMORY;
7697331766Sken	}
7698331766Sken
7699331766Sken	if (sli_cmd_common_get_profile_list(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, &cb_arg->payload)) {
7700331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_profile_list_cb, cb_arg);
7701331766Sken	}
7702331766Sken
7703331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7704331766Sken		ocs_log_test(hw->os, "GET_PROFILE_LIST failed\n");
7705331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7706331766Sken		ocs_dma_free(hw->os, &cb_arg->payload);
7707331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7708331766Sken	}
7709331766Sken
7710331766Sken	return rc;
7711331766Sken}
7712331766Sken
7713331766Skentypedef struct ocs_hw_get_active_profile_cb_arg_s {
7714331766Sken	ocs_get_active_profile_cb_t cb;
7715331766Sken	void *arg;
7716331766Sken} ocs_hw_get_active_profile_cb_arg_t;
7717331766Sken
7718331766Sken/**
7719331766Sken * @brief Called for the completion of get_active_profile for a
7720331766Sken *        user request.
7721331766Sken *
7722331766Sken * @param hw Hardware context.
7723331766Sken * @param status The status from the MQE
7724331766Sken * @param mqe Pointer to mailbox command buffer.
7725331766Sken * @param arg Pointer to a callback argument.
7726331766Sken *
7727331766Sken * @return Returns 0 on success, or a non-zero value on failure.
7728331766Sken */
7729331766Skenstatic int32_t
7730331766Skenocs_hw_get_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7731331766Sken{
7732331766Sken	ocs_hw_get_active_profile_cb_arg_t *cb_arg = arg;
7733331766Sken	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7734331766Sken	sli4_res_common_get_active_profile_t* response = (sli4_res_common_get_active_profile_t*) mbox_rsp->payload.embed;
7735331766Sken	uint32_t active_profile;
7736331766Sken
7737331766Sken	active_profile = response->active_profile_id;
7738331766Sken
7739331766Sken	if (cb_arg->cb) {
7740331766Sken		cb_arg->cb(status, active_profile, cb_arg->arg);
7741331766Sken	}
7742331766Sken
7743331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7744331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7745331766Sken
7746331766Sken	return 0;
7747331766Sken}
7748331766Sken
7749331766Sken/**
7750331766Sken * @ingroup io
7751331766Sken * @brief  Get the currently active profile.
7752331766Sken * @par Description
7753331766Sken * Issues a SLI-4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
7754331766Sken * command completes the provided mgmt callback function is
7755331766Sken * called.
7756331766Sken *
7757331766Sken * @param hw Hardware context.
7758331766Sken * @param cb Callback function to be called when the
7759331766Sken *	     command completes.
7760331766Sken * @param ul_arg An argument that is passed to the callback
7761331766Sken *      	 function.
7762331766Sken *
7763331766Sken * @return
7764331766Sken * - OCS_HW_RTN_SUCCESS on success.
7765331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7766331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7767331766Sken *   context.
7768331766Sken * - OCS_HW_RTN_ERROR on any other error.
7769331766Sken */
7770331766Skenint32_t
7771331766Skenocs_hw_get_active_profile(ocs_hw_t *hw, ocs_get_active_profile_cb_t cb, void* ul_arg)
7772331766Sken{
7773331766Sken	uint8_t *mbxdata;
7774331766Sken	ocs_hw_get_active_profile_cb_arg_t *cb_arg;
7775331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7776331766Sken
7777331766Sken	/* Only supported on Skyhawk */
7778331766Sken	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7779331766Sken		return OCS_HW_RTN_ERROR;
7780331766Sken	}
7781331766Sken
7782331766Sken	/* mbxdata holds the header of the command */
7783331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7784331766Sken	if (mbxdata == NULL) {
7785331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7786331766Sken		return OCS_HW_RTN_NO_MEMORY;
7787331766Sken	}
7788331766Sken
7789331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7790331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_active_profile_cb_arg_t), OCS_M_NOWAIT);
7791331766Sken	if (cb_arg == NULL) {
7792331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7793331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7794331766Sken		return OCS_HW_RTN_NO_MEMORY;
7795331766Sken	}
7796331766Sken
7797331766Sken	cb_arg->cb = cb;
7798331766Sken	cb_arg->arg = ul_arg;
7799331766Sken
7800331766Sken	if (sli_cmd_common_get_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7801331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_active_profile_cb, cb_arg);
7802331766Sken	}
7803331766Sken
7804331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7805331766Sken		ocs_log_test(hw->os, "GET_ACTIVE_PROFILE failed\n");
7806331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7807331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7808331766Sken	}
7809331766Sken
7810331766Sken	return rc;
7811331766Sken}
7812331766Sken
7813331766Skentypedef struct ocs_hw_get_nvparms_cb_arg_s {
7814331766Sken	ocs_get_nvparms_cb_t cb;
7815331766Sken	void *arg;
7816331766Sken} ocs_hw_get_nvparms_cb_arg_t;
7817331766Sken
7818331766Sken/**
7819331766Sken * @brief Called for the completion of get_nvparms for a
7820331766Sken *        user request.
7821331766Sken *
7822331766Sken * @param hw Hardware context.
7823331766Sken * @param status The status from the MQE.
7824331766Sken * @param mqe Pointer to mailbox command buffer.
7825331766Sken * @param arg Pointer to a callback argument.
7826331766Sken *
7827331766Sken * @return 0 on success, non-zero otherwise
7828331766Sken */
7829331766Skenstatic int32_t
7830331766Skenocs_hw_get_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7831331766Sken{
7832331766Sken	ocs_hw_get_nvparms_cb_arg_t *cb_arg = arg;
7833331766Sken	sli4_cmd_read_nvparms_t* mbox_rsp = (sli4_cmd_read_nvparms_t*) mqe;
7834331766Sken
7835331766Sken	if (cb_arg->cb) {
7836331766Sken		cb_arg->cb(status, mbox_rsp->wwpn, mbox_rsp->wwnn, mbox_rsp->hard_alpa,
7837331766Sken				mbox_rsp->preferred_d_id, cb_arg->arg);
7838331766Sken	}
7839331766Sken
7840331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7841331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7842331766Sken
7843331766Sken	return 0;
7844331766Sken}
7845331766Sken
7846331766Sken/**
7847331766Sken * @ingroup io
7848331766Sken * @brief  Read non-volatile parms.
7849331766Sken * @par Description
7850331766Sken * Issues a SLI-4 READ_NVPARMS mailbox. When the
7851331766Sken * command completes the provided mgmt callback function is
7852331766Sken * called.
7853331766Sken *
7854331766Sken * @param hw Hardware context.
7855331766Sken * @param cb Callback function to be called when the
7856331766Sken *	  command completes.
7857331766Sken * @param ul_arg An argument that is passed to the callback
7858331766Sken *	  function.
7859331766Sken *
7860331766Sken * @return
7861331766Sken * - OCS_HW_RTN_SUCCESS on success.
7862331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7863331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7864331766Sken *   context.
7865331766Sken * - OCS_HW_RTN_ERROR on any other error.
7866331766Sken */
7867331766Skenint32_t
7868331766Skenocs_hw_get_nvparms(ocs_hw_t *hw, ocs_get_nvparms_cb_t cb, void* ul_arg)
7869331766Sken{
7870331766Sken	uint8_t *mbxdata;
7871331766Sken	ocs_hw_get_nvparms_cb_arg_t *cb_arg;
7872331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7873331766Sken
7874331766Sken	/* mbxdata holds the header of the command */
7875331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7876331766Sken	if (mbxdata == NULL) {
7877331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7878331766Sken		return OCS_HW_RTN_NO_MEMORY;
7879331766Sken	}
7880331766Sken
7881331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7882331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_nvparms_cb_arg_t), OCS_M_NOWAIT);
7883331766Sken	if (cb_arg == NULL) {
7884331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7885331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7886331766Sken		return OCS_HW_RTN_NO_MEMORY;
7887331766Sken	}
7888331766Sken
7889331766Sken	cb_arg->cb = cb;
7890331766Sken	cb_arg->arg = ul_arg;
7891331766Sken
7892331766Sken	if (sli_cmd_read_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7893331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_nvparms_cb, cb_arg);
7894331766Sken	}
7895331766Sken
7896331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7897331766Sken		ocs_log_test(hw->os, "READ_NVPARMS failed\n");
7898331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7899331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7900331766Sken	}
7901331766Sken
7902331766Sken	return rc;
7903331766Sken}
7904331766Sken
7905331766Skentypedef struct ocs_hw_set_nvparms_cb_arg_s {
7906331766Sken	ocs_set_nvparms_cb_t cb;
7907331766Sken	void *arg;
7908331766Sken} ocs_hw_set_nvparms_cb_arg_t;
7909331766Sken
7910331766Sken/**
7911331766Sken * @brief Called for the completion of set_nvparms for a
7912331766Sken *        user request.
7913331766Sken *
7914331766Sken * @param hw Hardware context.
7915331766Sken * @param status The status from the MQE.
7916331766Sken * @param mqe Pointer to mailbox command buffer.
7917331766Sken * @param arg Pointer to a callback argument.
7918331766Sken *
7919331766Sken * @return Returns 0 on success, or a non-zero value on failure.
7920331766Sken */
7921331766Skenstatic int32_t
7922331766Skenocs_hw_set_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7923331766Sken{
7924331766Sken	ocs_hw_set_nvparms_cb_arg_t *cb_arg = arg;
7925331766Sken
7926331766Sken	if (cb_arg->cb) {
7927331766Sken		cb_arg->cb(status, cb_arg->arg);
7928331766Sken	}
7929331766Sken
7930331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7931331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7932331766Sken
7933331766Sken	return 0;
7934331766Sken}
7935331766Sken
7936331766Sken/**
7937331766Sken * @ingroup io
7938331766Sken * @brief  Write non-volatile parms.
7939331766Sken * @par Description
7940331766Sken * Issues a SLI-4 WRITE_NVPARMS mailbox. When the
7941331766Sken * command completes the provided mgmt callback function is
7942331766Sken * called.
7943331766Sken *
7944331766Sken * @param hw Hardware context.
7945331766Sken * @param cb Callback function to be called when the
7946331766Sken *	  command completes.
7947331766Sken * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
7948331766Sken * @param wwnn Port's WWNN in big-endian order, or NULL to use default.
7949331766Sken * @param hard_alpa A hard AL_PA address setting used during loop
7950331766Sken * initialization. If no hard AL_PA is required, set to 0.
7951331766Sken * @param preferred_d_id A preferred D_ID address setting
7952331766Sken * that may be overridden with the CONFIG_LINK mailbox command.
7953331766Sken * If there is no preference, set to 0.
7954331766Sken * @param ul_arg An argument that is passed to the callback
7955331766Sken *	  function.
7956331766Sken *
7957331766Sken * @return
7958331766Sken * - OCS_HW_RTN_SUCCESS on success.
7959331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7960331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7961331766Sken *   context.
7962331766Sken * - OCS_HW_RTN_ERROR on any other error.
7963331766Sken */
7964331766Skenint32_t
7965331766Skenocs_hw_set_nvparms(ocs_hw_t *hw, ocs_set_nvparms_cb_t cb, uint8_t *wwpn,
7966331766Sken		uint8_t *wwnn, uint8_t hard_alpa, uint32_t preferred_d_id, void* ul_arg)
7967331766Sken{
7968331766Sken	uint8_t *mbxdata;
7969331766Sken	ocs_hw_set_nvparms_cb_arg_t *cb_arg;
7970331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7971331766Sken
7972331766Sken	/* mbxdata holds the header of the command */
7973331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7974331766Sken	if (mbxdata == NULL) {
7975331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
7976331766Sken		return OCS_HW_RTN_NO_MEMORY;
7977331766Sken	}
7978331766Sken
7979331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
7980331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_nvparms_cb_arg_t), OCS_M_NOWAIT);
7981331766Sken	if (cb_arg == NULL) {
7982331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7983331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7984331766Sken		return OCS_HW_RTN_NO_MEMORY;
7985331766Sken	}
7986331766Sken
7987331766Sken	cb_arg->cb = cb;
7988331766Sken	cb_arg->arg = ul_arg;
7989331766Sken
7990331766Sken	if (sli_cmd_write_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE, wwpn, wwnn, hard_alpa, preferred_d_id)) {
7991331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_nvparms_cb, cb_arg);
7992331766Sken	}
7993331766Sken
7994331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
7995331766Sken		ocs_log_test(hw->os, "SET_NVPARMS failed\n");
7996331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7997331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7998331766Sken	}
7999331766Sken
8000331766Sken	return rc;
8001331766Sken}
8002331766Sken
8003331766Sken
8004331766Sken
8005331766Sken/**
8006331766Sken * @brief Called to obtain the count for the specified type.
8007331766Sken *
8008331766Sken * @param hw Hardware context.
8009331766Sken * @param io_count_type IO count type (inuse, free, wait_free).
8010331766Sken *
8011331766Sken * @return Returns the number of IOs on the specified list type.
8012331766Sken */
8013331766Skenuint32_t
8014331766Skenocs_hw_io_get_count(ocs_hw_t *hw, ocs_hw_io_count_type_e io_count_type)
8015331766Sken{
8016331766Sken	ocs_hw_io_t *io = NULL;
8017331766Sken	uint32_t count = 0;
8018331766Sken
8019331766Sken	ocs_lock(&hw->io_lock);
8020331766Sken
8021331766Sken	switch (io_count_type) {
8022331766Sken	case OCS_HW_IO_INUSE_COUNT :
8023331766Sken		ocs_list_foreach(&hw->io_inuse, io) {
8024331766Sken			count++;
8025331766Sken		}
8026331766Sken		break;
8027331766Sken	case OCS_HW_IO_FREE_COUNT :
8028331766Sken		 ocs_list_foreach(&hw->io_free, io) {
8029331766Sken			 count++;
8030331766Sken		 }
8031331766Sken		 break;
8032331766Sken	case OCS_HW_IO_WAIT_FREE_COUNT :
8033331766Sken		 ocs_list_foreach(&hw->io_wait_free, io) {
8034331766Sken			 count++;
8035331766Sken		 }
8036331766Sken		 break;
8037331766Sken	case OCS_HW_IO_PORT_OWNED_COUNT:
8038331766Sken		 ocs_list_foreach(&hw->io_port_owned, io) {
8039331766Sken			 count++;
8040331766Sken		 }
8041331766Sken		 break;
8042331766Sken	case OCS_HW_IO_N_TOTAL_IO_COUNT :
8043331766Sken		count = hw->config.n_io;
8044331766Sken		break;
8045331766Sken	}
8046331766Sken
8047331766Sken	ocs_unlock(&hw->io_lock);
8048331766Sken
8049331766Sken	return count;
8050331766Sken}
8051331766Sken
8052331766Sken/**
8053331766Sken * @brief Called to obtain the count of produced RQs.
8054331766Sken *
8055331766Sken * @param hw Hardware context.
8056331766Sken *
8057331766Sken * @return Returns the number of RQs produced.
8058331766Sken */
8059331766Skenuint32_t
8060331766Skenocs_hw_get_rqes_produced_count(ocs_hw_t *hw)
8061331766Sken{
8062331766Sken	uint32_t count = 0;
8063331766Sken	uint32_t i;
8064331766Sken	uint32_t j;
8065331766Sken
8066331766Sken	for (i = 0; i < hw->hw_rq_count; i++) {
8067331766Sken		hw_rq_t *rq = hw->hw_rq[i];
8068331766Sken		if (rq->rq_tracker != NULL) {
8069331766Sken			for (j = 0; j < rq->entry_count; j++) {
8070331766Sken				if (rq->rq_tracker[j] != NULL) {
8071331766Sken					count++;
8072331766Sken				}
8073331766Sken			}
8074331766Sken		}
8075331766Sken	}
8076331766Sken
8077331766Sken	return count;
8078331766Sken}
8079331766Sken
8080331766Skentypedef struct ocs_hw_set_active_profile_cb_arg_s {
8081331766Sken	ocs_set_active_profile_cb_t cb;
8082331766Sken	void *arg;
8083331766Sken} ocs_hw_set_active_profile_cb_arg_t;
8084331766Sken
8085331766Sken/**
8086331766Sken * @brief Called for the completion of set_active_profile for a
8087331766Sken *        user request.
8088331766Sken *
8089331766Sken * @param hw Hardware context.
8090331766Sken * @param status The status from the MQE
8091331766Sken * @param mqe Pointer to mailbox command buffer.
8092331766Sken * @param arg Pointer to a callback argument.
8093331766Sken *
8094331766Sken * @return Returns 0 on success, or a non-zero value on failure.
8095331766Sken */
8096331766Skenstatic int32_t
8097331766Skenocs_hw_set_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
8098331766Sken{
8099331766Sken	ocs_hw_set_active_profile_cb_arg_t *cb_arg = arg;
8100331766Sken
8101331766Sken	if (cb_arg->cb) {
8102331766Sken		cb_arg->cb(status, cb_arg->arg);
8103331766Sken	}
8104331766Sken
8105331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
8106331766Sken	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
8107331766Sken
8108331766Sken	return 0;
8109331766Sken}
8110331766Sken
8111331766Sken/**
8112331766Sken * @ingroup io
8113331766Sken * @brief  Set the currently active profile.
8114331766Sken * @par Description
8115331766Sken * Issues a SLI4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
8116331766Sken * command completes the provided mgmt callback function is
8117331766Sken * called.
8118331766Sken *
8119331766Sken * @param hw Hardware context.
8120331766Sken * @param profile_id Profile ID to activate.
8121331766Sken * @param cb Callback function to be called when the command completes.
8122331766Sken * @param ul_arg An argument that is passed to the callback function.
8123331766Sken *
8124331766Sken * @return
8125331766Sken * - OCS_HW_RTN_SUCCESS on success.
8126331766Sken * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
8127331766Sken * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
8128331766Sken *   context.
8129331766Sken * - OCS_HW_RTN_ERROR on any other error.
8130331766Sken */
8131331766Skenint32_t
8132331766Skenocs_hw_set_active_profile(ocs_hw_t *hw, ocs_set_active_profile_cb_t cb, uint32_t profile_id, void* ul_arg)
8133331766Sken{
8134331766Sken	uint8_t *mbxdata;
8135331766Sken	ocs_hw_set_active_profile_cb_arg_t *cb_arg;
8136331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
8137331766Sken
8138331766Sken	/* Only supported on Skyhawk */
8139331766Sken	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
8140331766Sken		return OCS_HW_RTN_ERROR;
8141331766Sken	}
8142331766Sken
8143331766Sken	/* mbxdata holds the header of the command */
8144331766Sken	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
8145331766Sken	if (mbxdata == NULL) {
8146331766Sken		ocs_log_err(hw->os, "failed to malloc mbox\n");
8147331766Sken		return OCS_HW_RTN_NO_MEMORY;
8148331766Sken	}
8149331766Sken
8150331766Sken
8151331766Sken	/* cb_arg holds the data that will be passed to the callback on completion */
8152331766Sken	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_active_profile_cb_arg_t), OCS_M_NOWAIT);
8153331766Sken	if (cb_arg == NULL) {
8154331766Sken		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
8155331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8156331766Sken		return OCS_HW_RTN_NO_MEMORY;
8157331766Sken	}
8158331766Sken
8159331766Sken	cb_arg->cb = cb;
8160331766Sken	cb_arg->arg = ul_arg;
8161331766Sken
8162331766Sken	if (sli_cmd_common_set_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, profile_id)) {
8163331766Sken		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_active_profile_cb, cb_arg);
8164331766Sken	}
8165331766Sken
8166331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
8167331766Sken		ocs_log_test(hw->os, "SET_ACTIVE_PROFILE failed\n");
8168331766Sken		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8169331766Sken		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_active_profile_cb_arg_t));
8170331766Sken	}
8171331766Sken
8172331766Sken	return rc;
8173331766Sken}
8174331766Sken
8175331766Sken
8176331766Sken
8177331766Sken/*
8178331766Sken * Private functions
8179331766Sken */
8180331766Sken
8181331766Sken/**
8182331766Sken * @brief Update the queue hash with the ID and index.
8183331766Sken *
8184331766Sken * @param hash Pointer to hash table.
8185331766Sken * @param id ID that was created.
8186331766Sken * @param index The index into the hash object.
8187331766Sken */
8188331766Skenstatic void
8189331766Skenocs_hw_queue_hash_add(ocs_queue_hash_t *hash, uint16_t id, uint16_t index)
8190331766Sken{
8191331766Sken	uint32_t	hash_index = id & (OCS_HW_Q_HASH_SIZE - 1);
8192331766Sken
8193331766Sken	/*
8194331766Sken	 * Since the hash is always bigger than the number of queues, then we
8195331766Sken	 * never have to worry about an infinite loop.
8196331766Sken	 */
8197331766Sken	while(hash[hash_index].in_use) {
8198331766Sken		hash_index = (hash_index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8199331766Sken	}
8200331766Sken
8201331766Sken	/* not used, claim the entry */
8202331766Sken	hash[hash_index].id = id;
8203331766Sken	hash[hash_index].in_use = 1;
8204331766Sken	hash[hash_index].index = index;
8205331766Sken}
8206331766Sken
8207331766Sken/**
8208331766Sken * @brief Find index given queue ID.
8209331766Sken *
8210331766Sken * @param hash Pointer to hash table.
8211331766Sken * @param id ID to find.
8212331766Sken *
8213331766Sken * @return Returns the index into the HW cq array or -1 if not found.
8214331766Sken */
8215331766Skenint32_t
8216331766Skenocs_hw_queue_hash_find(ocs_queue_hash_t *hash, uint16_t id)
8217331766Sken{
8218331766Sken	int32_t	rc = -1;
8219331766Sken	int32_t	index = id & (OCS_HW_Q_HASH_SIZE - 1);
8220331766Sken
8221331766Sken	/*
8222331766Sken	 * Since the hash is always bigger than the maximum number of Qs, then we
8223331766Sken	 * never have to worry about an infinite loop. We will always find an
8224331766Sken	 * unused entry.
8225331766Sken	 */
8226331766Sken	do {
8227331766Sken		if (hash[index].in_use &&
8228331766Sken		    hash[index].id == id) {
8229331766Sken			rc = hash[index].index;
8230331766Sken		} else {
8231331766Sken			index = (index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8232331766Sken		}
8233331766Sken	} while(rc == -1 && hash[index].in_use);
8234331766Sken
8235331766Sken	return rc;
8236331766Sken}
8237331766Sken
8238331766Skenstatic int32_t
8239331766Skenocs_hw_domain_add(ocs_hw_t *hw, ocs_domain_t *domain)
8240331766Sken{
8241331766Sken	int32_t		rc = OCS_HW_RTN_ERROR;
8242331766Sken	uint16_t	fcfi = UINT16_MAX;
8243331766Sken
8244331766Sken	if ((hw == NULL) || (domain == NULL)) {
8245331766Sken		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8246331766Sken				hw, domain);
8247331766Sken		return OCS_HW_RTN_ERROR;
8248331766Sken	}
8249331766Sken
8250331766Sken	fcfi = domain->fcf_indicator;
8251331766Sken
8252331766Sken	if (fcfi < SLI4_MAX_FCFI) {
8253331766Sken		uint16_t	fcf_index = UINT16_MAX;
8254331766Sken
8255331766Sken		ocs_log_debug(hw->os, "adding domain %p @ %#x\n",
8256331766Sken				domain, fcfi);
8257331766Sken		hw->domains[fcfi] = domain;
8258331766Sken
8259331766Sken		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8260331766Sken		if (hw->workaround.override_fcfi) {
8261331766Sken			if (hw->first_domain_idx < 0) {
8262331766Sken				hw->first_domain_idx = fcfi;
8263331766Sken			}
8264331766Sken		}
8265331766Sken
8266331766Sken		fcf_index = domain->fcf;
8267331766Sken
8268331766Sken		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8269331766Sken			ocs_log_debug(hw->os, "adding map of FCF index %d to FCFI %d\n",
8270331766Sken				      fcf_index, fcfi);
8271331766Sken			hw->fcf_index_fcfi[fcf_index] = fcfi;
8272331766Sken			rc = OCS_HW_RTN_SUCCESS;
8273331766Sken		} else {
8274331766Sken			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8275331766Sken				     fcf_index, SLI4_MAX_FCF_INDEX);
8276331766Sken			hw->domains[fcfi] = NULL;
8277331766Sken		}
8278331766Sken	} else {
8279331766Sken		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8280331766Sken				fcfi, SLI4_MAX_FCFI);
8281331766Sken	}
8282331766Sken
8283331766Sken	return rc;
8284331766Sken}
8285331766Sken
8286331766Skenstatic int32_t
8287331766Skenocs_hw_domain_del(ocs_hw_t *hw, ocs_domain_t *domain)
8288331766Sken{
8289331766Sken	int32_t		rc = OCS_HW_RTN_ERROR;
8290331766Sken	uint16_t	fcfi = UINT16_MAX;
8291331766Sken
8292331766Sken	if ((hw == NULL) || (domain == NULL)) {
8293331766Sken		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8294331766Sken				hw, domain);
8295331766Sken		return OCS_HW_RTN_ERROR;
8296331766Sken	}
8297331766Sken
8298331766Sken	fcfi = domain->fcf_indicator;
8299331766Sken
8300331766Sken	if (fcfi < SLI4_MAX_FCFI) {
8301331766Sken		uint16_t	fcf_index = UINT16_MAX;
8302331766Sken
8303331766Sken		ocs_log_debug(hw->os, "deleting domain %p @ %#x\n",
8304331766Sken				domain, fcfi);
8305331766Sken
8306331766Sken		if (domain != hw->domains[fcfi]) {
8307331766Sken			ocs_log_test(hw->os, "provided domain %p does not match stored domain %p\n",
8308331766Sken				     domain, hw->domains[fcfi]);
8309331766Sken			return OCS_HW_RTN_ERROR;
8310331766Sken		}
8311331766Sken
8312331766Sken		hw->domains[fcfi] = NULL;
8313331766Sken
8314331766Sken		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8315331766Sken		if (hw->workaround.override_fcfi) {
8316331766Sken			if (hw->first_domain_idx == fcfi) {
8317331766Sken				hw->first_domain_idx = -1;
8318331766Sken			}
8319331766Sken		}
8320331766Sken
8321331766Sken		fcf_index = domain->fcf;
8322331766Sken
8323331766Sken		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8324331766Sken			if (hw->fcf_index_fcfi[fcf_index] == fcfi) {
8325331766Sken				hw->fcf_index_fcfi[fcf_index] = 0;
8326331766Sken				rc = OCS_HW_RTN_SUCCESS;
8327331766Sken			} else {
8328331766Sken				ocs_log_test(hw->os, "indexed FCFI %#x doesn't match provided %#x @ %d\n",
8329331766Sken					     hw->fcf_index_fcfi[fcf_index], fcfi, fcf_index);
8330331766Sken			}
8331331766Sken		} else {
8332331766Sken			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8333331766Sken				     fcf_index, SLI4_MAX_FCF_INDEX);
8334331766Sken		}
8335331766Sken	} else {
8336331766Sken		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8337331766Sken				fcfi, SLI4_MAX_FCFI);
8338331766Sken	}
8339331766Sken
8340331766Sken	return rc;
8341331766Sken}
8342331766Sken
8343331766Skenocs_domain_t *
8344331766Skenocs_hw_domain_get(ocs_hw_t *hw, uint16_t fcfi)
8345331766Sken{
8346331766Sken
8347331766Sken	if (hw == NULL) {
8348331766Sken		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8349331766Sken		return NULL;
8350331766Sken	}
8351331766Sken
8352331766Sken	if (fcfi < SLI4_MAX_FCFI) {
8353331766Sken		return hw->domains[fcfi];
8354331766Sken	} else {
8355331766Sken		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8356331766Sken				fcfi, SLI4_MAX_FCFI);
8357331766Sken		return NULL;
8358331766Sken	}
8359331766Sken}
8360331766Sken
8361331766Skenstatic ocs_domain_t *
8362331766Skenocs_hw_domain_get_indexed(ocs_hw_t *hw, uint16_t fcf_index)
8363331766Sken{
8364331766Sken
8365331766Sken	if (hw == NULL) {
8366331766Sken		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8367331766Sken		return NULL;
8368331766Sken	}
8369331766Sken
8370331766Sken	if (fcf_index < SLI4_MAX_FCF_INDEX) {
8371331766Sken		return ocs_hw_domain_get(hw, hw->fcf_index_fcfi[fcf_index]);
8372331766Sken	} else {
8373331766Sken		ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8374331766Sken			     fcf_index, SLI4_MAX_FCF_INDEX);
8375331766Sken		return NULL;
8376331766Sken	}
8377331766Sken}
8378331766Sken
8379331766Sken/**
8380331766Sken * @brief Quaratine an IO by taking a reference count and adding it to the
8381331766Sken *        quarantine list. When the IO is popped from the list then the
8382331766Sken *        count is released and the IO MAY be freed depending on whether
8383331766Sken *        it is still referenced by the IO.
8384331766Sken *
8385331766Sken *        @n @b Note: BZ 160124 - If this is a target write or an initiator read using
8386331766Sken *        DIF, then we must add the XRI to a quarantine list until we receive
8387331766Sken *        4 more completions of this same type.
8388331766Sken *
8389331766Sken * @param hw Hardware context.
8390331766Sken * @param wq Pointer to the WQ associated with the IO object to quarantine.
8391331766Sken * @param io Pointer to the io object to quarantine.
8392331766Sken */
8393331766Skenstatic void
8394331766Skenocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io)
8395331766Sken{
8396331766Sken	ocs_quarantine_info_t *q_info = &wq->quarantine_info;
8397331766Sken	uint32_t	index;
8398331766Sken	ocs_hw_io_t	*free_io = NULL;
8399331766Sken
8400331766Sken	/* return if the QX bit was clear */
8401331766Sken	if (!io->quarantine) {
8402331766Sken		return;
8403331766Sken	}
8404331766Sken
8405331766Sken	/* increment the IO refcount to prevent it from being freed before the quarantine is over */
8406331766Sken	if (ocs_ref_get_unless_zero(&io->ref) == 0) {
8407331766Sken		/* command no longer active */
8408331766Sken		ocs_log_debug(hw ? hw->os : NULL,
8409331766Sken			      "io not active xri=0x%x tag=0x%x\n",
8410331766Sken			      io->indicator, io->reqtag);
8411331766Sken		return;
8412331766Sken	}
8413331766Sken
8414331766Sken	sli_queue_lock(wq->queue);
8415331766Sken		index = q_info->quarantine_index;
8416331766Sken		free_io = q_info->quarantine_ios[index];
8417331766Sken		q_info->quarantine_ios[index] = io;
8418331766Sken		q_info->quarantine_index = (index + 1) % OCS_HW_QUARANTINE_QUEUE_DEPTH;
8419331766Sken	sli_queue_unlock(wq->queue);
8420331766Sken
8421331766Sken	if (free_io != NULL) {
8422331766Sken		ocs_ref_put(&free_io->ref); /* ocs_ref_get(): same function */
8423331766Sken	}
8424331766Sken}
8425331766Sken
8426331766Sken/**
8427331766Sken * @brief Process entries on the given completion queue.
8428331766Sken *
8429331766Sken * @param hw Hardware context.
8430331766Sken * @param cq Pointer to the HW completion queue object.
8431331766Sken *
8432331766Sken * @return None.
8433331766Sken */
8434331766Skenvoid
8435331766Skenocs_hw_cq_process(ocs_hw_t *hw, hw_cq_t *cq)
8436331766Sken{
8437331766Sken	uint8_t		cqe[sizeof(sli4_mcqe_t)];
8438331766Sken	uint16_t	rid = UINT16_MAX;
8439331766Sken	sli4_qentry_e	ctype;		/* completion type */
8440331766Sken	int32_t		status;
8441331766Sken	uint32_t	n_processed = 0;
8442331766Sken	time_t		tstart;
8443331766Sken	time_t		telapsed;
8444331766Sken
8445331766Sken	tstart = ocs_msectime();
8446331766Sken
8447331766Sken	while (!sli_queue_read(&hw->sli, cq->queue, cqe)) {
8448331766Sken		status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
8449331766Sken		/*
8450331766Sken		 * The sign of status is significant. If status is:
8451331766Sken		 * == 0 : call completed correctly and the CQE indicated success
8452331766Sken		 *  > 0 : call completed correctly and the CQE indicated an error
8453331766Sken		 *  < 0 : call failed and no information is available about the CQE
8454331766Sken		 */
8455331766Sken		if (status < 0) {
8456331766Sken			if (status == -2) {
8457331766Sken				/* Notification that an entry was consumed, but not completed */
8458331766Sken				continue;
8459331766Sken			}
8460331766Sken
8461331766Sken			break;
8462331766Sken		}
8463331766Sken
8464331766Sken		switch (ctype) {
8465331766Sken		case SLI_QENTRY_ASYNC:
8466331766Sken			CPUTRACE("async");
8467331766Sken			sli_cqe_async(&hw->sli, cqe);
8468331766Sken			break;
8469331766Sken		case SLI_QENTRY_MQ:
8470331766Sken			/*
8471331766Sken			 * Process MQ entry. Note there is no way to determine
8472331766Sken			 * the MQ_ID from the completion entry.
8473331766Sken			 */
8474331766Sken			CPUTRACE("mq");
8475331766Sken			ocs_hw_mq_process(hw, status, hw->mq);
8476331766Sken			break;
8477331766Sken		case SLI_QENTRY_OPT_WRITE_CMD:
8478331766Sken			ocs_hw_rqpair_process_auto_xfr_rdy_cmd(hw, cq, cqe);
8479331766Sken			break;
8480331766Sken		case SLI_QENTRY_OPT_WRITE_DATA:
8481331766Sken			ocs_hw_rqpair_process_auto_xfr_rdy_data(hw, cq, cqe);
8482331766Sken			break;
8483331766Sken		case SLI_QENTRY_WQ:
8484331766Sken			CPUTRACE("wq");
8485331766Sken			ocs_hw_wq_process(hw, cq, cqe, status, rid);
8486331766Sken			break;
8487331766Sken		case SLI_QENTRY_WQ_RELEASE: {
8488331766Sken			uint32_t wq_id = rid;
8489331766Sken			uint32_t index = ocs_hw_queue_hash_find(hw->wq_hash, wq_id);
8490331766Sken			hw_wq_t *wq = hw->hw_wq[index];
8491331766Sken
8492331766Sken			/* Submit any HW IOs that are on the WQ pending list */
8493331766Sken			hw_wq_submit_pending(wq, wq->wqec_set_count);
8494331766Sken
8495331766Sken			break;
8496331766Sken		}
8497331766Sken
8498331766Sken		case SLI_QENTRY_RQ:
8499331766Sken			CPUTRACE("rq");
8500331766Sken			ocs_hw_rqpair_process_rq(hw, cq, cqe);
8501331766Sken			break;
8502331766Sken		case SLI_QENTRY_XABT: {
8503331766Sken			CPUTRACE("xabt");
8504331766Sken			ocs_hw_xabt_process(hw, cq, cqe, rid);
8505331766Sken			break;
8506331766Sken
8507331766Sken		}
8508331766Sken		default:
8509331766Sken			ocs_log_test(hw->os, "unhandled ctype=%#x rid=%#x\n", ctype, rid);
8510331766Sken			break;
8511331766Sken		}
8512331766Sken
8513331766Sken		n_processed++;
8514331766Sken		if (n_processed == cq->queue->proc_limit) {
8515331766Sken			break;
8516331766Sken		}
8517331766Sken
8518331766Sken		if (cq->queue->n_posted >= (cq->queue->posted_limit)) {
8519331766Sken			sli_queue_arm(&hw->sli, cq->queue, FALSE);
8520331766Sken		}
8521331766Sken	}
8522331766Sken
8523331766Sken	sli_queue_arm(&hw->sli, cq->queue, TRUE);
8524331766Sken
8525331766Sken	if (n_processed > cq->queue->max_num_processed) {
8526331766Sken		cq->queue->max_num_processed = n_processed;
8527331766Sken	}
8528331766Sken	telapsed = ocs_msectime() - tstart;
8529331766Sken	if (telapsed > cq->queue->max_process_time) {
8530331766Sken		cq->queue->max_process_time = telapsed;
8531331766Sken	}
8532331766Sken}
8533331766Sken
8534331766Sken/**
8535331766Sken * @brief Process WQ completion queue entries.
8536331766Sken *
8537331766Sken * @param hw Hardware context.
8538331766Sken * @param cq Pointer to the HW completion queue object.
8539331766Sken * @param cqe Pointer to WQ completion queue.
8540331766Sken * @param status Completion status.
8541331766Sken * @param rid Resource ID (IO tag).
8542331766Sken *
8543331766Sken * @return none
8544331766Sken */
8545331766Skenvoid
8546331766Skenocs_hw_wq_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, int32_t status, uint16_t rid)
8547331766Sken{
8548331766Sken	hw_wq_callback_t *wqcb;
8549331766Sken
8550331766Sken	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_WQ, (void *)cqe, ((sli4_fc_wcqe_t *)cqe)->status, cq->queue->id,
8551331766Sken			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8552331766Sken
8553331766Sken	if(rid == OCS_HW_REQUE_XRI_REGTAG) {
8554331766Sken		if(status) {
8555331766Sken			ocs_log_err(hw->os, "reque xri failed, status = %d \n", status);
8556331766Sken		}
8557331766Sken		return;
8558331766Sken	}
8559331766Sken
8560331766Sken	wqcb = ocs_hw_reqtag_get_instance(hw, rid);
8561331766Sken	if (wqcb == NULL) {
8562331766Sken		ocs_log_err(hw->os, "invalid request tag: x%x\n", rid);
8563331766Sken		return;
8564331766Sken	}
8565331766Sken
8566331766Sken	if (wqcb->callback == NULL) {
8567331766Sken		ocs_log_err(hw->os, "wqcb callback is NULL\n");
8568331766Sken		return;
8569331766Sken	}
8570331766Sken
8571331766Sken	(*wqcb->callback)(wqcb->arg, cqe, status);
8572331766Sken}
8573331766Sken
8574331766Sken/**
8575331766Sken * @brief Process WQ completions for IO requests
8576331766Sken *
8577331766Sken * @param arg Generic callback argument
8578331766Sken * @param cqe Pointer to completion queue entry
8579331766Sken * @param status Completion status
8580331766Sken *
8581331766Sken * @par Description
8582331766Sken * @n @b Note:  Regarding io->reqtag, the reqtag is assigned once when HW IOs are initialized
8583331766Sken * in ocs_hw_setup_io(), and don't need to be returned to the hw->wq_reqtag_pool.
8584331766Sken *
8585331766Sken * @return None.
8586331766Sken */
8587331766Skenstatic void
8588331766Skenocs_hw_wq_process_io(void *arg, uint8_t *cqe, int32_t status)
8589331766Sken{
8590331766Sken	ocs_hw_io_t *io = arg;
8591331766Sken	ocs_hw_t *hw = io->hw;
8592331766Sken	sli4_fc_wcqe_t *wcqe = (void *)cqe;
8593331766Sken	uint32_t	len = 0;
8594331766Sken	uint32_t ext = 0;
8595331766Sken	uint8_t out_of_order_axr_cmd = 0;
8596331766Sken	uint8_t out_of_order_axr_data = 0;
8597331766Sken	uint8_t lock_taken = 0;
8598331766Sken#if defined(OCS_DISC_SPIN_DELAY)
8599331766Sken	uint32_t delay = 0;
8600331766Sken	char prop_buf[32];
8601331766Sken#endif
8602331766Sken
8603331766Sken	/*
8604331766Sken	 * For the primary IO, this will also be used for the
8605331766Sken	 * response. So it is important to only set/clear this
8606331766Sken	 * flag on the first data phase of the IO because
8607331766Sken	 * subsequent phases will be done on the secondary XRI.
8608331766Sken	 */
8609331766Sken	if (io->quarantine && io->quarantine_first_phase) {
8610331766Sken		io->quarantine = (wcqe->qx == 1);
8611331766Sken		ocs_hw_io_quarantine(hw, io->wq, io);
8612331766Sken	}
8613331766Sken	io->quarantine_first_phase = FALSE;
8614331766Sken
8615331766Sken	/* BZ 161832 - free secondary HW IO */
8616331766Sken	if (io->sec_hio != NULL &&
8617331766Sken	    io->sec_hio->quarantine) {
8618331766Sken		/*
8619331766Sken		 * If the quarantine flag is set on the
8620331766Sken		 * IO, then set it on the secondary IO
8621331766Sken		 * based on the quarantine XRI (QX) bit
8622331766Sken		 * sent by the FW.
8623331766Sken		 */
8624331766Sken		io->sec_hio->quarantine = (wcqe->qx == 1);
8625331766Sken		/* use the primary io->wq because it is not set on the secondary IO. */
8626331766Sken		ocs_hw_io_quarantine(hw, io->wq, io->sec_hio);
8627331766Sken	}
8628331766Sken
8629331766Sken	ocs_hw_remove_io_timed_wqe(hw, io);
8630331766Sken
8631331766Sken	/* clear xbusy flag if WCQE[XB] is clear */
8632331766Sken	if (io->xbusy && wcqe->xb == 0) {
8633331766Sken		io->xbusy = FALSE;
8634331766Sken	}
8635331766Sken
8636331766Sken	/* get extended CQE status */
8637331766Sken	switch (io->type) {
8638331766Sken	case OCS_HW_BLS_ACC:
8639331766Sken	case OCS_HW_BLS_ACC_SID:
8640331766Sken		break;
8641331766Sken	case OCS_HW_ELS_REQ:
8642331766Sken		sli_fc_els_did(&hw->sli, cqe, &ext);
8643331766Sken		len = sli_fc_response_length(&hw->sli, cqe);
8644331766Sken		break;
8645331766Sken	case OCS_HW_ELS_RSP:
8646331766Sken	case OCS_HW_ELS_RSP_SID:
8647331766Sken	case OCS_HW_FC_CT_RSP:
8648331766Sken		break;
8649331766Sken	case OCS_HW_FC_CT:
8650331766Sken		len = sli_fc_response_length(&hw->sli, cqe);
8651331766Sken		break;
8652331766Sken	case OCS_HW_IO_TARGET_WRITE:
8653331766Sken		len = sli_fc_io_length(&hw->sli, cqe);
8654331766Sken#if defined(OCS_DISC_SPIN_DELAY)
8655331766Sken		if (ocs_get_property("disk_spin_delay", prop_buf, sizeof(prop_buf)) == 0) {
8656331766Sken			delay = ocs_strtoul(prop_buf, 0, 0);
8657331766Sken			ocs_udelay(delay);
8658331766Sken		}
8659331766Sken#endif
8660331766Sken		break;
8661331766Sken	case OCS_HW_IO_TARGET_READ:
8662331766Sken		len = sli_fc_io_length(&hw->sli, cqe);
8663331766Sken		/*
8664331766Sken		 * if_type == 2 seems to return 0 "total length placed" on
8665331766Sken		 * FCP_TSEND64_WQE completions. If this appears to happen,
8666331766Sken		 * use the CTIO data transfer length instead.
8667331766Sken		 */
8668331766Sken		if (hw->workaround.retain_tsend_io_length && !len && !status) {
8669331766Sken			len = io->length;
8670331766Sken		}
8671331766Sken
8672331766Sken		break;
8673331766Sken	case OCS_HW_IO_TARGET_RSP:
8674331766Sken		if(io->is_port_owned) {
8675331766Sken			ocs_lock(&io->axr_lock);
8676331766Sken			lock_taken = 1;
8677331766Sken			if(io->axr_buf->call_axr_cmd) {
8678331766Sken				out_of_order_axr_cmd = 1;
8679331766Sken			}
8680331766Sken			if(io->axr_buf->call_axr_data) {
8681331766Sken				out_of_order_axr_data = 1;
8682331766Sken			}
8683331766Sken		}
8684331766Sken		break;
8685331766Sken	case OCS_HW_IO_INITIATOR_READ:
8686331766Sken		len = sli_fc_io_length(&hw->sli, cqe);
8687331766Sken		break;
8688331766Sken	case OCS_HW_IO_INITIATOR_WRITE:
8689331766Sken		len = sli_fc_io_length(&hw->sli, cqe);
8690331766Sken		break;
8691331766Sken	case OCS_HW_IO_INITIATOR_NODATA:
8692331766Sken		break;
8693331766Sken	case OCS_HW_IO_DNRX_REQUEUE:
8694331766Sken		/* release the count for re-posting the buffer */
8695331766Sken		//ocs_hw_io_free(hw, io);
8696331766Sken		break;
8697331766Sken	default:
8698331766Sken		ocs_log_test(hw->os, "XXX unhandled io type %#x for XRI 0x%x\n",
8699331766Sken			     io->type, io->indicator);
8700331766Sken		break;
8701331766Sken	}
8702331766Sken	if (status) {
8703331766Sken		ext = sli_fc_ext_status(&hw->sli, cqe);
8704331766Sken		/* Emulate IAAB=0 for initiator WQEs only; i.e. automatically
8705331766Sken		 * abort exchange if an error occurred and exchange is still busy.
8706331766Sken		 */
8707331766Sken		if (hw->config.i_only_aab &&
8708331766Sken		    (ocs_hw_iotype_is_originator(io->type)) &&
8709331766Sken		    (ocs_hw_wcqe_abort_needed(status, ext, wcqe->xb))) {
8710331766Sken			ocs_hw_rtn_e rc;
8711331766Sken
8712331766Sken			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
8713331766Sken				      io->indicator, io->reqtag);
8714331766Sken			/*
8715331766Sken			 * Because the initiator will not issue another IO phase, then it is OK to to issue the
8716331766Sken			 * callback on the abort completion, but for consistency with the target, wait for the
8717331766Sken			 * XRI_ABORTED CQE to issue the IO callback.
8718331766Sken			 */
8719331766Sken			rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
8720331766Sken
8721331766Sken			if (rc == OCS_HW_RTN_SUCCESS) {
8722331766Sken				/* latch status to return after abort is complete */
8723331766Sken				io->status_saved = 1;
8724331766Sken				io->saved_status = status;
8725331766Sken				io->saved_ext = ext;
8726331766Sken				io->saved_len = len;
8727331766Sken				goto exit_ocs_hw_wq_process_io;
8728331766Sken			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8729331766Sken				/*
8730331766Sken				 * Already being aborted by someone else (ABTS
8731331766Sken				 * perhaps). Just fall through and return original
8732331766Sken				 * error.
8733331766Sken				 */
8734331766Sken				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8735331766Sken					      io->indicator, io->reqtag);
8736331766Sken
8737331766Sken			} else {
8738331766Sken				/* Failed to abort for some other reason, log error */
8739331766Sken				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8740331766Sken					     io->indicator, io->reqtag, rc);
8741331766Sken			}
8742331766Sken		}
8743331766Sken
8744331766Sken		/*
8745331766Sken		 * If we're not an originator IO, and XB is set, then issue abort for the IO from within the HW
8746331766Sken		 */
8747331766Sken		if ( (! ocs_hw_iotype_is_originator(io->type)) && wcqe->xb) {
8748331766Sken			ocs_hw_rtn_e rc;
8749331766Sken
8750331766Sken			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", io->indicator, io->reqtag);
8751331766Sken
8752331766Sken			/*
8753331766Sken			 * Because targets may send a response when the IO completes using the same XRI, we must
8754331766Sken			 * wait for the XRI_ABORTED CQE to issue the IO callback
8755331766Sken			 */
8756331766Sken			rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
8757331766Sken			if (rc == OCS_HW_RTN_SUCCESS) {
8758331766Sken				/* latch status to return after abort is complete */
8759331766Sken				io->status_saved = 1;
8760331766Sken				io->saved_status = status;
8761331766Sken				io->saved_ext = ext;
8762331766Sken				io->saved_len = len;
8763331766Sken				goto exit_ocs_hw_wq_process_io;
8764331766Sken			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8765331766Sken				/*
8766331766Sken				 * Already being aborted by someone else (ABTS
8767331766Sken				 * perhaps). Just fall through and return original
8768331766Sken				 * error.
8769331766Sken				 */
8770331766Sken				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8771331766Sken					      io->indicator, io->reqtag);
8772331766Sken
8773331766Sken			} else {
8774331766Sken				/* Failed to abort for some other reason, log error */
8775331766Sken				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8776331766Sken					     io->indicator, io->reqtag, rc);
8777331766Sken			}
8778331766Sken		}
8779331766Sken	}
8780331766Sken	/* BZ 161832 - free secondary HW IO */
8781331766Sken	if (io->sec_hio != NULL) {
8782331766Sken		ocs_hw_io_free(hw, io->sec_hio);
8783331766Sken		io->sec_hio = NULL;
8784331766Sken	}
8785331766Sken
8786331766Sken	if (io->done != NULL) {
8787331766Sken		ocs_hw_done_t  done = io->done;
8788331766Sken		void		*arg = io->arg;
8789331766Sken
8790331766Sken		io->done = NULL;
8791331766Sken
8792331766Sken		if (io->status_saved) {
8793331766Sken			/* use latched status if exists */
8794331766Sken			status = io->saved_status;
8795331766Sken			len = io->saved_len;
8796331766Sken			ext = io->saved_ext;
8797331766Sken			io->status_saved = 0;
8798331766Sken		}
8799331766Sken
8800331766Sken		/* Restore default SGL */
8801331766Sken		ocs_hw_io_restore_sgl(hw, io);
8802331766Sken		done(io, io->rnode, len, status, ext, arg);
8803331766Sken	}
8804331766Sken
8805331766Sken	if(out_of_order_axr_cmd) {
8806331766Sken		/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8807331766Sken		if (hw->config.bounce) {
8808331766Sken			fc_header_t *hdr = io->axr_buf->cmd_seq->header->dma.virt;
8809331766Sken			uint32_t s_id = fc_be24toh(hdr->s_id);
8810331766Sken			uint32_t d_id = fc_be24toh(hdr->d_id);
8811331766Sken			uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8812331766Sken			if (hw->callback.bounce != NULL) {
8813331766Sken				(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, io->axr_buf->cmd_seq, s_id, d_id, ox_id);
8814331766Sken			}
8815331766Sken		}else {
8816331766Sken			hw->callback.unsolicited(hw->args.unsolicited, io->axr_buf->cmd_seq);
8817331766Sken		}
8818331766Sken
8819331766Sken		if(out_of_order_axr_data) {
8820331766Sken			/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8821331766Sken			if (hw->config.bounce) {
8822331766Sken				fc_header_t *hdr = io->axr_buf->seq.header->dma.virt;
8823331766Sken				uint32_t s_id = fc_be24toh(hdr->s_id);
8824331766Sken				uint32_t d_id = fc_be24toh(hdr->d_id);
8825331766Sken				uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8826331766Sken				if (hw->callback.bounce != NULL) {
8827331766Sken					(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, &io->axr_buf->seq, s_id, d_id, ox_id);
8828331766Sken				}
8829331766Sken			}else {
8830331766Sken				hw->callback.unsolicited(hw->args.unsolicited, &io->axr_buf->seq);
8831331766Sken			}
8832331766Sken		}
8833331766Sken	}
8834331766Sken
8835331766Skenexit_ocs_hw_wq_process_io:
8836331766Sken	if(lock_taken) {
8837331766Sken		ocs_unlock(&io->axr_lock);
8838331766Sken	}
8839331766Sken}
8840331766Sken
8841331766Sken/**
8842331766Sken * @brief Process WQ completions for abort requests.
8843331766Sken *
8844331766Sken * @param arg Generic callback argument.
8845331766Sken * @param cqe Pointer to completion queue entry.
8846331766Sken * @param status Completion status.
8847331766Sken *
8848331766Sken * @return None.
8849331766Sken */
8850331766Skenstatic void
8851331766Skenocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status)
8852331766Sken{
8853331766Sken	ocs_hw_io_t *io = arg;
8854331766Sken	ocs_hw_t *hw = io->hw;
8855331766Sken	uint32_t ext = 0;
8856331766Sken	uint32_t len = 0;
8857331766Sken	hw_wq_callback_t *wqcb;
8858331766Sken
8859331766Sken	/*
8860331766Sken	 * For IOs that were aborted internally, we may need to issue the callback here depending
8861331766Sken	 * on whether a XRI_ABORTED CQE is expected ot not. If the status is Local Reject/No XRI, then
8862331766Sken	 * issue the callback now.
8863331766Sken	*/
8864331766Sken	ext = sli_fc_ext_status(&hw->sli, cqe);
8865331766Sken	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
8866331766Sken	    ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
8867331766Sken		io->done != NULL) {
8868331766Sken		ocs_hw_done_t  done = io->done;
8869331766Sken		void		*arg = io->arg;
8870331766Sken
8871331766Sken		io->done = NULL;
8872331766Sken
8873331766Sken		/*
8874331766Sken		 * Use latched status as this is always saved for an internal abort
8875331766Sken		 *
8876331766Sken		 * Note: We wont have both a done and abort_done function, so don't worry about
8877331766Sken		 *       clobbering the len, status and ext fields.
8878331766Sken		 */
8879331766Sken		status = io->saved_status;
8880331766Sken		len = io->saved_len;
8881331766Sken		ext = io->saved_ext;
8882331766Sken		io->status_saved = 0;
8883331766Sken		done(io, io->rnode, len, status, ext, arg);
8884331766Sken	}
8885331766Sken
8886331766Sken	if (io->abort_done != NULL) {
8887331766Sken		ocs_hw_done_t  done = io->abort_done;
8888331766Sken		void		*arg = io->abort_arg;
8889331766Sken
8890331766Sken		io->abort_done = NULL;
8891331766Sken
8892331766Sken		done(io, io->rnode, len, status, ext, arg);
8893331766Sken	}
8894331766Sken	ocs_lock(&hw->io_abort_lock);
8895331766Sken		/* clear abort bit to indicate abort is complete */
8896331766Sken		io->abort_in_progress = 0;
8897331766Sken	ocs_unlock(&hw->io_abort_lock);
8898331766Sken
8899331766Sken	/* Free the WQ callback */
8900331766Sken	ocs_hw_assert(io->abort_reqtag != UINT32_MAX);
8901331766Sken	wqcb = ocs_hw_reqtag_get_instance(hw, io->abort_reqtag);
8902331766Sken	ocs_hw_reqtag_free(hw, wqcb);
8903331766Sken
8904331766Sken	/*
8905331766Sken	 * Call ocs_hw_io_free() because this releases the WQ reservation as
8906331766Sken	 * well as doing the refcount put. Don't duplicate the code here.
8907331766Sken	 */
8908331766Sken	(void)ocs_hw_io_free(hw, io);
8909331766Sken}
8910331766Sken
8911331766Sken/**
8912331766Sken * @brief Process XABT completions
8913331766Sken *
8914331766Sken * @param hw Hardware context.
8915331766Sken * @param cq Pointer to the HW completion queue object.
8916331766Sken * @param cqe Pointer to WQ completion queue.
8917331766Sken * @param rid Resource ID (IO tag).
8918331766Sken *
8919331766Sken *
8920331766Sken * @return None.
8921331766Sken */
8922331766Skenvoid
8923331766Skenocs_hw_xabt_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, uint16_t rid)
8924331766Sken{
8925331766Sken	/* search IOs wait free list */
8926331766Sken	ocs_hw_io_t *io = NULL;
8927331766Sken
8928331766Sken	io = ocs_hw_io_lookup(hw, rid);
8929331766Sken
8930331766Sken	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_XABT, (void *)cqe, 0, cq->queue->id,
8931331766Sken			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8932331766Sken	if (io == NULL) {
8933331766Sken		/* IO lookup failure should never happen */
8934331766Sken		ocs_log_err(hw->os, "Error: xabt io lookup failed rid=%#x\n", rid);
8935331766Sken		return;
8936331766Sken	}
8937331766Sken
8938331766Sken	if (!io->xbusy) {
8939331766Sken		ocs_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
8940331766Sken	} else {
8941331766Sken		/* mark IO as no longer busy */
8942331766Sken		io->xbusy = FALSE;
8943331766Sken	}
8944331766Sken
8945331766Sken       if (io->is_port_owned) {
8946331766Sken               ocs_lock(&hw->io_lock);
8947331766Sken               /* Take reference so that below callback will not free io before reque */
8948331766Sken               ocs_ref_get(&io->ref);
8949331766Sken               ocs_unlock(&hw->io_lock);
8950331766Sken       }
8951331766Sken
8952331766Sken
8953331766Sken
8954331766Sken	/* For IOs that were aborted internally, we need to issue any pending callback here. */
8955331766Sken	if (io->done != NULL) {
8956331766Sken		ocs_hw_done_t  done = io->done;
8957331766Sken		void		*arg = io->arg;
8958331766Sken
8959331766Sken		/* Use latched status as this is always saved for an internal abort */
8960331766Sken		int32_t status = io->saved_status;
8961331766Sken		uint32_t len = io->saved_len;
8962331766Sken		uint32_t ext = io->saved_ext;
8963331766Sken
8964331766Sken		io->done = NULL;
8965331766Sken		io->status_saved = 0;
8966331766Sken
8967331766Sken		done(io, io->rnode, len, status, ext, arg);
8968331766Sken	}
8969331766Sken
8970331766Sken	/* Check to see if this is a port owned XRI */
8971331766Sken	if (io->is_port_owned) {
8972331766Sken		ocs_lock(&hw->io_lock);
8973331766Sken		ocs_hw_reque_xri(hw, io);
8974331766Sken		ocs_unlock(&hw->io_lock);
8975331766Sken		/* Not hanlding reque xri completion, free io */
8976331766Sken		ocs_hw_io_free(hw, io);
8977331766Sken		return;
8978331766Sken	}
8979331766Sken
8980331766Sken	ocs_lock(&hw->io_lock);
8981331766Sken		if ((io->state == OCS_HW_IO_STATE_INUSE) || (io->state == OCS_HW_IO_STATE_WAIT_FREE)) {
8982331766Sken			/* if on wait_free list, caller has already freed IO;
8983331766Sken			 * remove from wait_free list and add to free list.
8984331766Sken			 * if on in-use list, already marked as no longer busy;
8985331766Sken			 * just leave there and wait for caller to free.
8986331766Sken			 */
8987331766Sken			if (io->state == OCS_HW_IO_STATE_WAIT_FREE) {
8988331766Sken				io->state = OCS_HW_IO_STATE_FREE;
8989331766Sken				ocs_list_remove(&hw->io_wait_free, io);
8990331766Sken				ocs_hw_io_free_move_correct_list(hw, io);
8991331766Sken			}
8992331766Sken		}
8993331766Sken	ocs_unlock(&hw->io_lock);
8994331766Sken}
8995331766Sken
8996331766Sken/**
8997331766Sken * @brief Adjust the number of WQs and CQs within the HW.
8998331766Sken *
8999331766Sken * @par Description
9000331766Sken * Calculates the number of WQs and associated CQs needed in the HW based on
9001331766Sken * the number of IOs. Calculates the starting CQ index for each WQ, RQ and
9002331766Sken * MQ.
9003331766Sken *
9004331766Sken * @param hw Hardware context allocated by the caller.
9005331766Sken */
9006331766Skenstatic void
9007331766Skenocs_hw_adjust_wqs(ocs_hw_t *hw)
9008331766Sken{
9009331766Sken	uint32_t max_wq_num = sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ);
9010331766Sken	uint32_t max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ];
9011331766Sken	uint32_t max_cq_entries = hw->num_qentries[SLI_QTYPE_CQ];
9012331766Sken
9013331766Sken	/*
9014331766Sken	 * possibly adjust the the size of the WQs so that the CQ is twice as
9015331766Sken	 * big as the WQ to allow for 2 completions per IO. This allows us to
9016331766Sken	 * handle multi-phase as well as aborts.
9017331766Sken	 */
9018331766Sken	if (max_cq_entries < max_wq_entries * 2) {
9019331766Sken		max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ] = max_cq_entries / 2;
9020331766Sken	}
9021331766Sken
9022331766Sken	/*
9023331766Sken	 * Calculate the number of WQs to use base on the number of IOs.
9024331766Sken	 *
9025331766Sken	 * Note: We need to reserve room for aborts which must be sent down
9026331766Sken	 *       the same WQ as the IO. So we allocate enough WQ space to
9027331766Sken	 *       handle 2 times the number of IOs. Half of the space will be
9028331766Sken	 *       used for normal IOs and the other hwf is reserved for aborts.
9029331766Sken	 */
9030331766Sken	hw->config.n_wq = ((hw->config.n_io * 2) + (max_wq_entries - 1)) / max_wq_entries;
9031331766Sken
9032331766Sken	/*
9033331766Sken	 * For performance reasons, it is best to use use a minimum of 4 WQs
9034331766Sken	 * for BE3 and Skyhawk.
9035331766Sken	 */
9036331766Sken	if (hw->config.n_wq < 4 &&
9037331766Sken	    SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
9038331766Sken		hw->config.n_wq = 4;
9039331766Sken	}
9040331766Sken
9041331766Sken	/*
9042331766Sken	 * For dual-chute support, we need to have at least one WQ per chute.
9043331766Sken	 */
9044331766Sken	if (hw->config.n_wq < 2 &&
9045331766Sken	    ocs_hw_get_num_chutes(hw) > 1) {
9046331766Sken		hw->config.n_wq = 2;
9047331766Sken	}
9048331766Sken
9049331766Sken	/* make sure we haven't exceeded the max supported in the HW */
9050331766Sken	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
9051331766Sken		hw->config.n_wq = OCS_HW_MAX_NUM_WQ;
9052331766Sken	}
9053331766Sken
9054331766Sken	/* make sure we haven't exceeded the chip maximum */
9055331766Sken	if (hw->config.n_wq > max_wq_num) {
9056331766Sken		hw->config.n_wq = max_wq_num;
9057331766Sken	}
9058331766Sken
9059331766Sken	/*
9060331766Sken	 * Using Queue Topology string, we divide by number of chutes
9061331766Sken	 */
9062331766Sken	hw->config.n_wq /= ocs_hw_get_num_chutes(hw);
9063331766Sken}
9064331766Sken
9065331766Skenstatic int32_t
9066331766Skenocs_hw_command_process(ocs_hw_t *hw, int32_t status, uint8_t *mqe, size_t size)
9067331766Sken{
9068331766Sken	ocs_command_ctx_t *ctx = NULL;
9069331766Sken
9070331766Sken	ocs_lock(&hw->cmd_lock);
9071331766Sken		if (NULL == (ctx = ocs_list_remove_head(&hw->cmd_head))) {
9072331766Sken			ocs_log_err(hw->os, "XXX no command context?!?\n");
9073331766Sken			ocs_unlock(&hw->cmd_lock);
9074331766Sken			return -1;
9075331766Sken		}
9076331766Sken
9077331766Sken		hw->cmd_head_count--;
9078331766Sken
9079331766Sken		/* Post any pending requests */
9080331766Sken		ocs_hw_cmd_submit_pending(hw);
9081331766Sken
9082331766Sken	ocs_unlock(&hw->cmd_lock);
9083331766Sken
9084331766Sken	if (ctx->cb) {
9085331766Sken		if (ctx->buf) {
9086331766Sken			ocs_memcpy(ctx->buf, mqe, size);
9087331766Sken		}
9088331766Sken		ctx->cb(hw, status, ctx->buf, ctx->arg);
9089331766Sken	}
9090331766Sken
9091331766Sken	ocs_memset(ctx, 0, sizeof(ocs_command_ctx_t));
9092331766Sken	ocs_free(hw->os, ctx, sizeof(ocs_command_ctx_t));
9093331766Sken
9094331766Sken	return 0;
9095331766Sken}
9096331766Sken
9097331766Sken
9098331766Sken
9099331766Sken
9100331766Sken/**
9101331766Sken * @brief Process entries on the given mailbox queue.
9102331766Sken *
9103331766Sken * @param hw Hardware context.
9104331766Sken * @param status CQE status.
9105331766Sken * @param mq Pointer to the mailbox queue object.
9106331766Sken *
9107331766Sken * @return Returns 0 on success, or a non-zero value on failure.
9108331766Sken */
9109331766Skenstatic int32_t
9110331766Skenocs_hw_mq_process(ocs_hw_t *hw, int32_t status, sli4_queue_t *mq)
9111331766Sken{
9112331766Sken	uint8_t		mqe[SLI4_BMBX_SIZE];
9113331766Sken
9114331766Sken	if (!sli_queue_read(&hw->sli, mq, mqe)) {
9115331766Sken		ocs_hw_command_process(hw, status, mqe, mq->size);
9116331766Sken	}
9117331766Sken
9118331766Sken	return 0;
9119331766Sken}
9120331766Sken
9121331766Sken/**
9122331766Sken * @brief Read a FCF table entry.
9123331766Sken *
9124331766Sken * @param hw Hardware context.
9125331766Sken * @param index Table index to read. Use SLI4_FCOE_FCF_TABLE_FIRST for the first
9126331766Sken * read and the next_index field from the FCOE_READ_FCF_TABLE command
9127331766Sken * for subsequent reads.
9128331766Sken *
9129331766Sken * @return Returns 0 on success, or a non-zero value on failure.
9130331766Sken */
9131331766Skenstatic ocs_hw_rtn_e
9132331766Skenocs_hw_read_fcf(ocs_hw_t *hw, uint32_t index)
9133331766Sken{
9134331766Sken	uint8_t		*buf = NULL;
9135331766Sken	int32_t		rc = OCS_HW_RTN_ERROR;
9136331766Sken
9137331766Sken	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9138331766Sken	if (!buf) {
9139331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
9140331766Sken		return OCS_HW_RTN_NO_MEMORY;
9141331766Sken	}
9142331766Sken
9143331766Sken	if (sli_cmd_fcoe_read_fcf_table(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->fcf_dmem,
9144331766Sken			index)) {
9145331766Sken		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_read_fcf, &hw->fcf_dmem);
9146331766Sken	}
9147331766Sken
9148331766Sken	if (rc != OCS_HW_RTN_SUCCESS) {
9149331766Sken		ocs_log_test(hw->os, "FCOE_READ_FCF_TABLE failed\n");
9150331766Sken		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9151331766Sken	}
9152331766Sken
9153331766Sken	return rc;
9154331766Sken}
9155331766Sken
9156331766Sken/**
9157331766Sken * @brief Callback function for the FCOE_READ_FCF_TABLE command.
9158331766Sken *
9159331766Sken * @par Description
9160331766Sken * Note that the caller has allocated:
9161331766Sken *  - DMA memory to hold the table contents
9162331766Sken *  - DMA memory structure
9163331766Sken *  - Command/results buffer
9164331766Sken *  .
9165331766Sken * Each of these must be freed here.
9166331766Sken *
9167331766Sken * @param hw Hardware context.
9168331766Sken * @param status Hardware status.
9169331766Sken * @param mqe Pointer to the mailbox command/results buffer.
9170331766Sken * @param arg Pointer to the DMA memory structure.
9171331766Sken *
9172331766Sken * @return Returns 0 on success, or a non-zero value on failure.
9173331766Sken */
9174331766Skenstatic int32_t
9175331766Skenocs_hw_cb_read_fcf(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9176331766Sken{
9177331766Sken	ocs_dma_t	*dma = arg;
9178331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9179331766Sken
9180331766Sken	if (status || hdr->status) {
9181331766Sken		ocs_log_test(hw->os, "bad status cqe=%#x mqe=%#x\n",
9182331766Sken				status, hdr->status);
9183331766Sken	} else if (dma->virt) {
9184331766Sken		sli4_res_fcoe_read_fcf_table_t *read_fcf = dma->virt;
9185331766Sken
9186331766Sken		/* if FC or FCOE and FCF entry valid, process it */
9187331766Sken		if (read_fcf->fcf_entry.fc ||
9188331766Sken				(read_fcf->fcf_entry.val && !read_fcf->fcf_entry.sol)) {
9189331766Sken			if (hw->callback.domain != NULL) {
9190331766Sken				ocs_domain_record_t drec = {0};
9191331766Sken
9192331766Sken				if (read_fcf->fcf_entry.fc) {
9193331766Sken					/*
9194331766Sken					 * This is a pseudo FCF entry. Create a domain
9195331766Sken					 * record based on the read topology information
9196331766Sken					 */
9197331766Sken					drec.speed = hw->link.speed;
9198331766Sken					drec.fc_id = hw->link.fc_id;
9199331766Sken					drec.is_fc = TRUE;
9200331766Sken					if (SLI_LINK_TOPO_LOOP == hw->link.topology) {
9201331766Sken						drec.is_loop = TRUE;
9202331766Sken						ocs_memcpy(drec.map.loop, hw->link.loop_map,
9203331766Sken							   sizeof(drec.map.loop));
9204331766Sken					} else if (SLI_LINK_TOPO_NPORT == hw->link.topology) {
9205331766Sken						drec.is_nport = TRUE;
9206331766Sken					}
9207331766Sken				} else {
9208331766Sken					drec.index = read_fcf->fcf_entry.fcf_index;
9209331766Sken					drec.priority = read_fcf->fcf_entry.fip_priority;
9210331766Sken
9211331766Sken					/* copy address, wwn and vlan_bitmap */
9212331766Sken					ocs_memcpy(drec.address, read_fcf->fcf_entry.fcf_mac_address,
9213331766Sken						   sizeof(drec.address));
9214331766Sken					ocs_memcpy(drec.wwn, read_fcf->fcf_entry.fabric_name_id,
9215331766Sken						   sizeof(drec.wwn));
9216331766Sken					ocs_memcpy(drec.map.vlan, read_fcf->fcf_entry.vlan_bitmap,
9217331766Sken						   sizeof(drec.map.vlan));
9218331766Sken
9219331766Sken					drec.is_ethernet = TRUE;
9220331766Sken					drec.is_nport = TRUE;
9221331766Sken				}
9222331766Sken
9223331766Sken				hw->callback.domain(hw->args.domain,
9224331766Sken						OCS_HW_DOMAIN_FOUND,
9225331766Sken						&drec);
9226331766Sken			}
9227331766Sken		} else {
9228331766Sken			/* if FCOE and FCF is not valid, ignore it */
9229331766Sken			ocs_log_test(hw->os, "ignore invalid FCF entry\n");
9230331766Sken		}
9231331766Sken
9232331766Sken		if (SLI4_FCOE_FCF_TABLE_LAST != read_fcf->next_index) {
9233331766Sken			ocs_hw_read_fcf(hw, read_fcf->next_index);
9234331766Sken		}
9235331766Sken	}
9236331766Sken
9237331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9238331766Sken	//ocs_dma_free(hw->os, dma);
9239331766Sken	//ocs_free(hw->os, dma, sizeof(ocs_dma_t));
9240331766Sken
9241331766Sken	return 0;
9242331766Sken}
9243331766Sken
9244331766Sken/**
9245331766Sken * @brief Callback function for the SLI link events.
9246331766Sken *
9247331766Sken * @par Description
9248331766Sken * This function allocates memory which must be freed in its callback.
9249331766Sken *
9250331766Sken * @param ctx Hardware context pointer (that is, ocs_hw_t *).
9251331766Sken * @param e Event structure pointer (that is, sli4_link_event_t *).
9252331766Sken *
9253331766Sken * @return Returns 0 on success, or a non-zero value on failure.
9254331766Sken */
9255331766Skenstatic int32_t
9256331766Skenocs_hw_cb_link(void *ctx, void *e)
9257331766Sken{
9258331766Sken	ocs_hw_t	*hw = ctx;
9259331766Sken	sli4_link_event_t *event = e;
9260331766Sken	ocs_domain_t	*d = NULL;
9261331766Sken	uint32_t	i = 0;
9262331766Sken	int32_t		rc = OCS_HW_RTN_ERROR;
9263331766Sken	ocs_t 		*ocs = hw->os;
9264331766Sken
9265331766Sken	ocs_hw_link_event_init(hw);
9266331766Sken
9267331766Sken	switch (event->status) {
9268331766Sken	case SLI_LINK_STATUS_UP:
9269331766Sken
9270331766Sken		hw->link = *event;
9271331766Sken
9272331766Sken		if (SLI_LINK_TOPO_NPORT == event->topology) {
9273331766Sken			device_printf(ocs->dev, "Link Up, NPORT, speed is %d\n", event->speed);
9274331766Sken			ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9275331766Sken		} else if (SLI_LINK_TOPO_LOOP == event->topology) {
9276331766Sken			uint8_t	*buf = NULL;
9277331766Sken			device_printf(ocs->dev, "Link Up, LOOP, speed is %d\n", event->speed);
9278331766Sken
9279331766Sken			buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9280331766Sken			if (!buf) {
9281331766Sken				ocs_log_err(hw->os, "no buffer for command\n");
9282331766Sken				break;
9283331766Sken			}
9284331766Sken
9285331766Sken			if (sli_cmd_read_topology(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->loop_map)) {
9286331766Sken				rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, __ocs_read_topology_cb, NULL);
9287331766Sken			}
9288331766Sken
9289331766Sken			if (rc != OCS_HW_RTN_SUCCESS) {
9290331766Sken				ocs_log_test(hw->os, "READ_TOPOLOGY failed\n");
9291331766Sken				ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9292331766Sken			}
9293331766Sken		} else {
9294331766Sken			device_printf(ocs->dev, "Link Up, unsupported topology (%#x), speed is %d\n",
9295331766Sken					event->topology, event->speed);
9296331766Sken		}
9297331766Sken		break;
9298331766Sken	case SLI_LINK_STATUS_DOWN:
9299331766Sken		device_printf(ocs->dev, "Link Down\n");
9300331766Sken
9301331766Sken		hw->link.status = event->status;
9302331766Sken
9303331766Sken		for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9304331766Sken			if (d != NULL &&
9305331766Sken			    hw->callback.domain != NULL) {
9306331766Sken				hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, d);
9307331766Sken			}
9308331766Sken		}
9309331766Sken		break;
9310331766Sken	default:
9311331766Sken		ocs_log_test(hw->os, "unhandled link status %#x\n", event->status);
9312331766Sken		break;
9313331766Sken	}
9314331766Sken
9315331766Sken	return 0;
9316331766Sken}
9317331766Sken
9318331766Skenstatic int32_t
9319331766Skenocs_hw_cb_fip(void *ctx, void *e)
9320331766Sken{
9321331766Sken	ocs_hw_t	*hw = ctx;
9322331766Sken	ocs_domain_t	*domain = NULL;
9323331766Sken	sli4_fip_event_t *event = e;
9324331766Sken
9325331766Sken	/* Find the associated domain object */
9326331766Sken	if (event->type == SLI4_FCOE_FIP_FCF_CLEAR_VLINK) {
9327331766Sken		ocs_domain_t *d = NULL;
9328331766Sken		uint32_t	i = 0;
9329331766Sken
9330331766Sken		/* Clear VLINK is different from the other FIP events as it passes back
9331331766Sken		 * a VPI instead of a FCF index. Check all attached SLI ports for a
9332331766Sken		 * matching VPI */
9333331766Sken		for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9334331766Sken			if (d != NULL) {
9335331766Sken				ocs_sport_t	*sport = NULL;
9336331766Sken
9337331766Sken				ocs_list_foreach(&d->sport_list, sport) {
9338331766Sken					if (sport->indicator == event->index) {
9339331766Sken						domain = d;
9340331766Sken						break;
9341331766Sken					}
9342331766Sken				}
9343331766Sken
9344331766Sken				if (domain != NULL) {
9345331766Sken					break;
9346331766Sken				}
9347331766Sken			}
9348331766Sken		}
9349331766Sken	} else {
9350331766Sken		domain = ocs_hw_domain_get_indexed(hw, event->index);
9351331766Sken	}
9352331766Sken
9353331766Sken	switch (event->type) {
9354331766Sken	case SLI4_FCOE_FIP_FCF_DISCOVERED:
9355331766Sken		ocs_hw_read_fcf(hw, event->index);
9356331766Sken		break;
9357331766Sken	case SLI4_FCOE_FIP_FCF_DEAD:
9358331766Sken		if (domain != NULL &&
9359331766Sken		    hw->callback.domain != NULL) {
9360331766Sken			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9361331766Sken		}
9362331766Sken		break;
9363331766Sken	case SLI4_FCOE_FIP_FCF_CLEAR_VLINK:
9364331766Sken		if (domain != NULL &&
9365331766Sken		    hw->callback.domain != NULL) {
9366331766Sken			/*
9367331766Sken			 * We will want to issue rediscover FCF when this domain is free'd  in order
9368331766Sken			 * to invalidate the FCF table
9369331766Sken			 */
9370331766Sken			domain->req_rediscover_fcf = TRUE;
9371331766Sken			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9372331766Sken		}
9373331766Sken		break;
9374331766Sken	case SLI4_FCOE_FIP_FCF_MODIFIED:
9375331766Sken		if (domain != NULL &&
9376331766Sken		    hw->callback.domain != NULL) {
9377331766Sken			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9378331766Sken		}
9379331766Sken
9380331766Sken		ocs_hw_read_fcf(hw, event->index);
9381331766Sken		break;
9382331766Sken	default:
9383331766Sken		ocs_log_test(hw->os, "unsupported event %#x\n", event->type);
9384331766Sken	}
9385331766Sken
9386331766Sken	return 0;
9387331766Sken}
9388331766Sken
9389331766Skenstatic int32_t
9390331766Skenocs_hw_cb_node_attach(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9391331766Sken{
9392331766Sken	ocs_remote_node_t *rnode = arg;
9393331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9394331766Sken	ocs_hw_remote_node_event_e	evt = 0;
9395331766Sken
9396331766Sken	if (status || hdr->status) {
9397331766Sken		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9398331766Sken				hdr->status);
9399331766Sken		ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
9400331766Sken		rnode->attached = FALSE;
9401331766Sken		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9402331766Sken		evt = OCS_HW_NODE_ATTACH_FAIL;
9403331766Sken	} else {
9404331766Sken		rnode->attached = TRUE;
9405331766Sken		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 1);
9406331766Sken		evt = OCS_HW_NODE_ATTACH_OK;
9407331766Sken	}
9408331766Sken
9409331766Sken	if (hw->callback.rnode != NULL) {
9410331766Sken		hw->callback.rnode(hw->args.rnode, evt, rnode);
9411331766Sken	}
9412331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9413331766Sken
9414331766Sken	return 0;
9415331766Sken}
9416331766Sken
9417331766Skenstatic int32_t
9418331766Skenocs_hw_cb_node_free(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9419331766Sken{
9420331766Sken	ocs_remote_node_t *rnode = arg;
9421331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9422331766Sken	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9423331766Sken	int32_t		rc = 0;
9424331766Sken
9425331766Sken	if (status || hdr->status) {
9426331766Sken		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9427331766Sken				hdr->status);
9428331766Sken
9429331766Sken		/*
9430331766Sken		 * In certain cases, a non-zero MQE status is OK (all must be true):
9431331766Sken		 *   - node is attached
9432331766Sken		 *   - if High Login Mode is enabled, node is part of a node group
9433331766Sken		 *   - status is 0x1400
9434331766Sken		 */
9435331766Sken		if (!rnode->attached || ((sli_get_hlm(&hw->sli) == TRUE) && !rnode->node_group) ||
9436331766Sken				(hdr->status != SLI4_MBOX_STATUS_RPI_NOT_REG)) {
9437331766Sken			rc = -1;
9438331766Sken		}
9439331766Sken	}
9440331766Sken
9441331766Sken	if (rc == 0) {
9442331766Sken		rnode->node_group = FALSE;
9443331766Sken		rnode->attached = FALSE;
9444331766Sken
9445331766Sken		if (ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_count) == 0) {
9446331766Sken			ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9447331766Sken		}
9448331766Sken
9449331766Sken		evt = OCS_HW_NODE_FREE_OK;
9450331766Sken	}
9451331766Sken
9452331766Sken	if (hw->callback.rnode != NULL) {
9453331766Sken		hw->callback.rnode(hw->args.rnode, evt, rnode);
9454331766Sken	}
9455331766Sken
9456331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9457331766Sken
9458331766Sken	return rc;
9459331766Sken}
9460331766Sken
9461331766Skenstatic int32_t
9462331766Skenocs_hw_cb_node_free_all(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9463331766Sken{
9464331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9465331766Sken	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9466331766Sken	int32_t		rc = 0;
9467331766Sken	uint32_t	i;
9468331766Sken
9469331766Sken	if (status || hdr->status) {
9470331766Sken		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9471331766Sken				hdr->status);
9472331766Sken	} else {
9473331766Sken		evt = OCS_HW_NODE_FREE_ALL_OK;
9474331766Sken	}
9475331766Sken
9476331766Sken	if (evt == OCS_HW_NODE_FREE_ALL_OK) {
9477331766Sken		for (i = 0; i < sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI); i++) {
9478331766Sken			ocs_atomic_set(&hw->rpi_ref[i].rpi_count, 0);
9479331766Sken		}
9480331766Sken
9481331766Sken		if (sli_resource_reset(&hw->sli, SLI_RSRC_FCOE_RPI)) {
9482331766Sken			ocs_log_test(hw->os, "FCOE_RPI free all failure\n");
9483331766Sken			rc = -1;
9484331766Sken		}
9485331766Sken	}
9486331766Sken
9487331766Sken	if (hw->callback.rnode != NULL) {
9488331766Sken		hw->callback.rnode(hw->args.rnode, evt, NULL);
9489331766Sken	}
9490331766Sken
9491331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9492331766Sken
9493331766Sken	return rc;
9494331766Sken}
9495331766Sken
9496331766Sken/**
9497331766Sken * @brief Initialize the pool of HW IO objects.
9498331766Sken *
9499331766Sken * @param hw Hardware context.
9500331766Sken *
9501331766Sken * @return Returns 0 on success, or a non-zero value on failure.
9502331766Sken */
9503331766Skenstatic ocs_hw_rtn_e
9504331766Skenocs_hw_setup_io(ocs_hw_t *hw)
9505331766Sken{
9506331766Sken	uint32_t	i = 0;
9507331766Sken	ocs_hw_io_t	*io = NULL;
9508331766Sken	uintptr_t	xfer_virt = 0;
9509331766Sken	uintptr_t	xfer_phys = 0;
9510331766Sken	uint32_t	index;
9511331766Sken	uint8_t		new_alloc = TRUE;
9512331766Sken
9513331766Sken	if (NULL == hw->io) {
9514331766Sken		hw->io = ocs_malloc(hw->os, hw->config.n_io * sizeof(ocs_hw_io_t *), OCS_M_ZERO | OCS_M_NOWAIT);
9515331766Sken
9516331766Sken		if (NULL == hw->io) {
9517331766Sken			ocs_log_err(hw->os, "IO pointer memory allocation failed, %d Ios at size %zu\n",
9518331766Sken				    hw->config.n_io,
9519331766Sken				    sizeof(ocs_hw_io_t *));
9520331766Sken			return OCS_HW_RTN_NO_MEMORY;
9521331766Sken		}
9522331766Sken		for (i = 0; i < hw->config.n_io; i++) {
9523331766Sken			hw->io[i] = ocs_malloc(hw->os, sizeof(ocs_hw_io_t),
9524331766Sken						OCS_M_ZERO | OCS_M_NOWAIT);
9525331766Sken			if (hw->io[i] == NULL) {
9526331766Sken				ocs_log_err(hw->os, "IO(%d) memory allocation failed\n", i);
9527331766Sken				goto error;
9528331766Sken			}
9529331766Sken		}
9530331766Sken
9531331766Sken		/* Create WQE buffs for IO */
9532331766Sken		hw->wqe_buffs = ocs_malloc(hw->os, hw->config.n_io * hw->sli.config.wqe_size,
9533331766Sken				OCS_M_ZERO | OCS_M_NOWAIT);
9534331766Sken		if (NULL == hw->wqe_buffs) {
9535331766Sken			ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t));
9536331766Sken			ocs_log_err(hw->os, "%s: IO WQE buff allocation failed, %d Ios at size %zu\n",
9537331766Sken					__func__, hw->config.n_io, hw->sli.config.wqe_size);
9538331766Sken			return OCS_HW_RTN_NO_MEMORY;
9539331766Sken		}
9540331766Sken
9541331766Sken	} else {
9542331766Sken		/* re-use existing IOs, including SGLs */
9543331766Sken		new_alloc = FALSE;
9544331766Sken	}
9545331766Sken
9546331766Sken	if (new_alloc) {
9547331766Sken		if (ocs_dma_alloc(hw->os, &hw->xfer_rdy,
9548331766Sken					sizeof(fcp_xfer_rdy_iu_t) * hw->config.n_io,
9549331766Sken					4/*XXX what does this need to be? */)) {
9550331766Sken			ocs_log_err(hw->os, "XFER_RDY buffer allocation failed\n");
9551331766Sken			return OCS_HW_RTN_NO_MEMORY;
9552331766Sken		}
9553331766Sken	}
9554331766Sken	xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
9555331766Sken	xfer_phys = hw->xfer_rdy.phys;
9556331766Sken
9557331766Sken	for (i = 0; i < hw->config.n_io; i++) {
9558331766Sken		hw_wq_callback_t *wqcb;
9559331766Sken
9560331766Sken		io = hw->io[i];
9561331766Sken
9562331766Sken		/* initialize IO fields */
9563331766Sken		io->hw = hw;
9564331766Sken
9565331766Sken		/* Assign a WQE buff */
9566331766Sken		io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.config.wqe_size];
9567331766Sken
9568331766Sken		/* Allocate the request tag for this IO */
9569331766Sken		wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_io, io);
9570331766Sken		if (wqcb == NULL) {
9571331766Sken			ocs_log_err(hw->os, "can't allocate request tag\n");
9572331766Sken			return OCS_HW_RTN_NO_RESOURCES;
9573331766Sken		}
9574331766Sken		io->reqtag = wqcb->instance_index;
9575331766Sken
9576331766Sken		/* Now for the fields that are initialized on each free */
9577331766Sken		ocs_hw_init_free_io(io);
9578331766Sken
9579331766Sken		/* The XB flag isn't cleared on IO free, so initialize it to zero here */
9580331766Sken		io->xbusy = 0;
9581331766Sken
9582331766Sken		if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_XRI, &io->indicator, &index)) {
9583331766Sken			ocs_log_err(hw->os, "sli_resource_alloc failed @ %d\n", i);
9584331766Sken			return OCS_HW_RTN_NO_MEMORY;
9585331766Sken		}
9586331766Sken
9587331766Sken		if (new_alloc && ocs_dma_alloc(hw->os, &io->def_sgl, hw->config.n_sgl * sizeof(sli4_sge_t), 64)) {
9588331766Sken			ocs_log_err(hw->os, "ocs_dma_alloc failed @ %d\n", i);
9589331766Sken			ocs_memset(&io->def_sgl, 0, sizeof(ocs_dma_t));
9590331766Sken			return OCS_HW_RTN_NO_MEMORY;
9591331766Sken		}
9592331766Sken		io->def_sgl_count = hw->config.n_sgl;
9593331766Sken		io->sgl = &io->def_sgl;
9594331766Sken		io->sgl_count = io->def_sgl_count;
9595331766Sken
9596331766Sken		if (hw->xfer_rdy.size) {
9597331766Sken			io->xfer_rdy.virt = (void *)xfer_virt;
9598331766Sken			io->xfer_rdy.phys = xfer_phys;
9599331766Sken			io->xfer_rdy.size = sizeof(fcp_xfer_rdy_iu_t);
9600331766Sken
9601331766Sken			xfer_virt += sizeof(fcp_xfer_rdy_iu_t);
9602331766Sken			xfer_phys += sizeof(fcp_xfer_rdy_iu_t);
9603331766Sken		}
9604331766Sken	}
9605331766Sken
9606331766Sken	return OCS_HW_RTN_SUCCESS;
9607331766Skenerror:
9608331766Sken	for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
9609331766Sken		ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
9610331766Sken		hw->io[i] = NULL;
9611331766Sken	}
9612331766Sken
9613331766Sken	return OCS_HW_RTN_NO_MEMORY;
9614331766Sken}
9615331766Sken
9616331766Skenstatic ocs_hw_rtn_e
9617331766Skenocs_hw_init_io(ocs_hw_t *hw)
9618331766Sken{
9619331766Sken	uint32_t        i = 0, io_index = 0;
9620331766Sken	uint32_t        prereg = 0;
9621331766Sken	ocs_hw_io_t	*io = NULL;
9622331766Sken	uint8_t		cmd[SLI4_BMBX_SIZE];
9623331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
9624331766Sken	uint32_t	nremaining;
9625331766Sken	uint32_t	n = 0;
9626331766Sken	uint32_t	sgls_per_request = 256;
9627331766Sken	ocs_dma_t	**sgls = NULL;
9628331766Sken	ocs_dma_t	reqbuf = { 0 };
9629331766Sken
9630331766Sken	prereg = sli_get_sgl_preregister(&hw->sli);
9631331766Sken
9632331766Sken	if (prereg) {
9633331766Sken		sgls = ocs_malloc(hw->os, sizeof(*sgls) * sgls_per_request, OCS_M_NOWAIT);
9634331766Sken		if (sgls == NULL) {
9635331766Sken			ocs_log_err(hw->os, "ocs_malloc sgls failed\n");
9636331766Sken			return OCS_HW_RTN_NO_MEMORY;
9637331766Sken		}
9638331766Sken
9639331766Sken		rc = ocs_dma_alloc(hw->os, &reqbuf, 32 + sgls_per_request*16, OCS_MIN_DMA_ALIGNMENT);
9640331766Sken		if (rc) {
9641331766Sken			ocs_log_err(hw->os, "ocs_dma_alloc reqbuf failed\n");
9642331766Sken			ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9643331766Sken			return OCS_HW_RTN_NO_MEMORY;
9644331766Sken		}
9645331766Sken	}
9646331766Sken
9647331766Sken	io = hw->io[io_index];
9648331766Sken	for (nremaining = hw->config.n_io; nremaining; nremaining -= n) {
9649331766Sken		if (prereg) {
9650331766Sken			/* Copy address of SGL's into local sgls[] array, break out if the xri
9651331766Sken			 * is not contiguous.
9652331766Sken			 */
9653331766Sken			for (n = 0; n < MIN(sgls_per_request, nremaining); n++) {
9654331766Sken				/* Check that we have contiguous xri values */
9655331766Sken				if (n > 0) {
9656331766Sken					if (hw->io[io_index + n]->indicator != (hw->io[io_index + n-1]->indicator+1)) {
9657331766Sken						break;
9658331766Sken					}
9659331766Sken				}
9660331766Sken				sgls[n] = hw->io[io_index + n]->sgl;
9661331766Sken			}
9662331766Sken
9663331766Sken			if (sli_cmd_fcoe_post_sgl_pages(&hw->sli, cmd, sizeof(cmd),
9664331766Sken						io->indicator, n, sgls, NULL, &reqbuf)) {
9665331766Sken				if (ocs_hw_command(hw, cmd, OCS_CMD_POLL, NULL, NULL)) {
9666331766Sken					rc = OCS_HW_RTN_ERROR;
9667331766Sken					ocs_log_err(hw->os, "SGL post failed\n");
9668331766Sken					break;
9669331766Sken				}
9670331766Sken			}
9671331766Sken		} else {
9672331766Sken			n = nremaining;
9673331766Sken		}
9674331766Sken
9675331766Sken		/* Add to tail if successful */
9676331766Sken		for (i = 0; i < n; i ++) {
9677331766Sken			io->is_port_owned = 0;
9678331766Sken			io->state = OCS_HW_IO_STATE_FREE;
9679331766Sken			ocs_list_add_tail(&hw->io_free, io);
9680331766Sken			io = hw->io[io_index+1];
9681331766Sken			io_index++;
9682331766Sken		}
9683331766Sken	}
9684331766Sken
9685331766Sken	if (prereg) {
9686331766Sken		ocs_dma_free(hw->os, &reqbuf);
9687331766Sken		ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9688331766Sken	}
9689331766Sken
9690331766Sken	return rc;
9691331766Sken}
9692331766Sken
9693331766Skenstatic int32_t
9694331766Skenocs_hw_flush(ocs_hw_t *hw)
9695331766Sken{
9696331766Sken	uint32_t	i = 0;
9697331766Sken
9698331766Sken	/* Process any remaining completions */
9699331766Sken	for (i = 0; i < hw->eq_count; i++) {
9700331766Sken		ocs_hw_process(hw, i, ~0);
9701331766Sken	}
9702331766Sken
9703331766Sken	return 0;
9704331766Sken}
9705331766Sken
9706331766Skenstatic int32_t
9707331766Skenocs_hw_command_cancel(ocs_hw_t *hw)
9708331766Sken{
9709331766Sken
9710331766Sken	ocs_lock(&hw->cmd_lock);
9711331766Sken
9712331766Sken	/*
9713331766Sken	 * Manually clean up remaining commands. Note: since this calls
9714331766Sken	 * ocs_hw_command_process(), we'll also process the cmd_pending
9715331766Sken	 * list, so no need to manually clean that out.
9716331766Sken	 */
9717331766Sken	while (!ocs_list_empty(&hw->cmd_head)) {
9718331766Sken		uint8_t		mqe[SLI4_BMBX_SIZE] = { 0 };
9719331766Sken		ocs_command_ctx_t *ctx = ocs_list_get_head(&hw->cmd_head);
9720331766Sken
9721331766Sken		ocs_log_test(hw->os, "hung command %08x\n",
9722331766Sken				NULL == ctx ? UINT32_MAX :
9723331766Sken				(NULL == ctx->buf ? UINT32_MAX : *((uint32_t *)ctx->buf)));
9724331766Sken		ocs_unlock(&hw->cmd_lock);
9725331766Sken		ocs_hw_command_process(hw, -1/*Bad status*/, mqe, SLI4_BMBX_SIZE);
9726331766Sken		ocs_lock(&hw->cmd_lock);
9727331766Sken	}
9728331766Sken
9729331766Sken	ocs_unlock(&hw->cmd_lock);
9730331766Sken
9731331766Sken	return 0;
9732331766Sken}
9733331766Sken
9734331766Sken/**
9735331766Sken * @brief Find IO given indicator (xri).
9736331766Sken *
9737331766Sken * @param hw Hal context.
9738331766Sken * @param indicator Indicator (xri) to look for.
9739331766Sken *
9740331766Sken * @return Returns io if found, NULL otherwise.
9741331766Sken */
9742331766Skenocs_hw_io_t *
9743331766Skenocs_hw_io_lookup(ocs_hw_t *hw, uint32_t xri)
9744331766Sken{
9745331766Sken	uint32_t ioindex;
9746331766Sken	ioindex = xri - hw->sli.config.extent[SLI_RSRC_FCOE_XRI].base[0];
9747331766Sken	return hw->io[ioindex];
9748331766Sken}
9749331766Sken
9750331766Sken/**
9751331766Sken * @brief Issue any pending callbacks for an IO and remove off the timer and pending lists.
9752331766Sken *
9753331766Sken * @param hw Hal context.
9754331766Sken * @param io Pointer to the IO to cleanup.
9755331766Sken */
9756331766Skenstatic void
9757331766Skenocs_hw_io_cancel_cleanup(ocs_hw_t *hw, ocs_hw_io_t *io)
9758331766Sken{
9759331766Sken	ocs_hw_done_t  done = io->done;
9760331766Sken	ocs_hw_done_t  abort_done = io->abort_done;
9761331766Sken
9762331766Sken	/* first check active_wqe list and remove if there */
9763331766Sken	if (ocs_list_on_list(&io->wqe_link)) {
9764331766Sken		ocs_list_remove(&hw->io_timed_wqe, io);
9765331766Sken	}
9766331766Sken
9767331766Sken	/* Remove from WQ pending list */
9768331766Sken	if ((io->wq != NULL) && ocs_list_on_list(&io->wq->pending_list)) {
9769331766Sken		ocs_list_remove(&io->wq->pending_list, io);
9770331766Sken	}
9771331766Sken
9772331766Sken	if (io->done) {
9773331766Sken		void		*arg = io->arg;
9774331766Sken
9775331766Sken		io->done = NULL;
9776331766Sken		ocs_unlock(&hw->io_lock);
9777331766Sken		done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, arg);
9778331766Sken		ocs_lock(&hw->io_lock);
9779331766Sken	}
9780331766Sken
9781331766Sken	if (io->abort_done != NULL) {
9782331766Sken		void		*abort_arg = io->abort_arg;
9783331766Sken
9784331766Sken		io->abort_done = NULL;
9785331766Sken		ocs_unlock(&hw->io_lock);
9786331766Sken		abort_done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, abort_arg);
9787331766Sken		ocs_lock(&hw->io_lock);
9788331766Sken	}
9789331766Sken}
9790331766Sken
9791331766Skenstatic int32_t
9792331766Skenocs_hw_io_cancel(ocs_hw_t *hw)
9793331766Sken{
9794331766Sken	ocs_hw_io_t	*io = NULL;
9795331766Sken	ocs_hw_io_t	*tmp_io = NULL;
9796331766Sken	uint32_t	iters = 100; /* One second limit */
9797331766Sken
9798331766Sken	/*
9799331766Sken	 * Manually clean up outstanding IO.
9800331766Sken	 * Only walk through list once: the backend will cleanup any IOs when done/abort_done is called.
9801331766Sken	 */
9802331766Sken	ocs_lock(&hw->io_lock);
9803331766Sken	ocs_list_foreach_safe(&hw->io_inuse, io, tmp_io) {
9804331766Sken		ocs_hw_done_t  done = io->done;
9805331766Sken		ocs_hw_done_t  abort_done = io->abort_done;
9806331766Sken
9807331766Sken		ocs_hw_io_cancel_cleanup(hw, io);
9808331766Sken
9809331766Sken		/*
9810331766Sken		 * Since this is called in a reset/shutdown
9811331766Sken		 * case, If there is no callback, then just
9812331766Sken		 * free the IO.
9813331766Sken		 *
9814331766Sken		 * Note: A port owned XRI cannot be on
9815331766Sken		 *       the in use list. We cannot call
9816331766Sken		 *       ocs_hw_io_free() because we already
9817331766Sken		 *       hold the io_lock.
9818331766Sken		 */
9819331766Sken		if (done == NULL &&
9820331766Sken		    abort_done == NULL) {
9821331766Sken			/*
9822331766Sken			 * Since this is called in a reset/shutdown
9823331766Sken			 * case, If there is no callback, then just
9824331766Sken			 * free the IO.
9825331766Sken			 */
9826331766Sken			ocs_hw_io_free_common(hw, io);
9827331766Sken			ocs_list_remove(&hw->io_inuse, io);
9828331766Sken			ocs_hw_io_free_move_correct_list(hw, io);
9829331766Sken		}
9830331766Sken	}
9831331766Sken
9832331766Sken	/*
9833331766Sken	 * For port owned XRIs, they are not on the in use list, so
9834331766Sken	 * walk though XRIs and issue any callbacks.
9835331766Sken	 */
9836331766Sken	ocs_list_foreach_safe(&hw->io_port_owned, io, tmp_io) {
9837331766Sken		/* check  list and remove if there */
9838331766Sken		if (ocs_list_on_list(&io->dnrx_link)) {
9839331766Sken			ocs_list_remove(&hw->io_port_dnrx, io);
9840331766Sken			ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
9841331766Sken		}
9842331766Sken		ocs_hw_io_cancel_cleanup(hw, io);
9843331766Sken		ocs_list_remove(&hw->io_port_owned, io);
9844331766Sken		ocs_hw_io_free_common(hw, io);
9845331766Sken	}
9846331766Sken	ocs_unlock(&hw->io_lock);
9847331766Sken
9848331766Sken	/* Give time for the callbacks to complete */
9849331766Sken	do {
9850331766Sken		ocs_udelay(10000);
9851331766Sken		iters--;
9852331766Sken	} while (!ocs_list_empty(&hw->io_inuse) && iters);
9853331766Sken
9854331766Sken	/* Leave a breadcrumb that cleanup is not yet complete. */
9855331766Sken	if (!ocs_list_empty(&hw->io_inuse)) {
9856331766Sken		ocs_log_test(hw->os, "io_inuse list is not empty\n");
9857331766Sken	}
9858331766Sken
9859331766Sken	return 0;
9860331766Sken}
9861331766Sken
9862331766Skenstatic int32_t
9863331766Skenocs_hw_io_ini_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *cmnd, uint32_t cmnd_size,
9864331766Sken		ocs_dma_t *rsp)
9865331766Sken{
9866331766Sken	sli4_sge_t	*data = NULL;
9867331766Sken
9868331766Sken	if (!hw || !io) {
9869331766Sken		ocs_log_err(NULL, "bad parm hw=%p io=%p\n", hw, io);
9870331766Sken		return OCS_HW_RTN_ERROR;
9871331766Sken	}
9872331766Sken
9873331766Sken	data = io->def_sgl.virt;
9874331766Sken
9875331766Sken	/* setup command pointer */
9876331766Sken	data->buffer_address_high = ocs_addr32_hi(cmnd->phys);
9877331766Sken	data->buffer_address_low  = ocs_addr32_lo(cmnd->phys);
9878331766Sken	data->buffer_length = cmnd_size;
9879331766Sken	data++;
9880331766Sken
9881331766Sken	/* setup response pointer */
9882331766Sken	data->buffer_address_high = ocs_addr32_hi(rsp->phys);
9883331766Sken	data->buffer_address_low  = ocs_addr32_lo(rsp->phys);
9884331766Sken	data->buffer_length = rsp->size;
9885331766Sken
9886331766Sken	return 0;
9887331766Sken}
9888331766Sken
9889331766Skenstatic int32_t
9890331766Sken__ocs_read_topology_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9891331766Sken{
9892331766Sken	sli4_cmd_read_topology_t *read_topo = (sli4_cmd_read_topology_t *)mqe;
9893331766Sken
9894331766Sken	if (status || read_topo->hdr.status) {
9895331766Sken		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n",
9896331766Sken				status, read_topo->hdr.status);
9897331766Sken		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9898331766Sken		return -1;
9899331766Sken	}
9900331766Sken
9901331766Sken	switch (read_topo->attention_type) {
9902331766Sken	case SLI4_READ_TOPOLOGY_LINK_UP:
9903331766Sken		hw->link.status = SLI_LINK_STATUS_UP;
9904331766Sken		break;
9905331766Sken	case SLI4_READ_TOPOLOGY_LINK_DOWN:
9906331766Sken		hw->link.status = SLI_LINK_STATUS_DOWN;
9907331766Sken		break;
9908331766Sken	case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
9909331766Sken		hw->link.status = SLI_LINK_STATUS_NO_ALPA;
9910331766Sken		break;
9911331766Sken	default:
9912331766Sken		hw->link.status = SLI_LINK_STATUS_MAX;
9913331766Sken		break;
9914331766Sken	}
9915331766Sken
9916331766Sken	switch (read_topo->topology) {
9917331766Sken	case SLI4_READ_TOPOLOGY_NPORT:
9918331766Sken		hw->link.topology = SLI_LINK_TOPO_NPORT;
9919331766Sken		break;
9920331766Sken	case SLI4_READ_TOPOLOGY_FC_AL:
9921331766Sken		hw->link.topology = SLI_LINK_TOPO_LOOP;
9922331766Sken		if (SLI_LINK_STATUS_UP == hw->link.status) {
9923331766Sken			hw->link.loop_map = hw->loop_map.virt;
9924331766Sken		}
9925331766Sken		hw->link.fc_id = read_topo->acquired_al_pa;
9926331766Sken		break;
9927331766Sken	default:
9928331766Sken		hw->link.topology = SLI_LINK_TOPO_MAX;
9929331766Sken		break;
9930331766Sken	}
9931331766Sken
9932331766Sken	hw->link.medium = SLI_LINK_MEDIUM_FC;
9933331766Sken
9934331766Sken	switch (read_topo->link_current.link_speed) {
9935331766Sken	case SLI4_READ_TOPOLOGY_SPEED_1G:
9936331766Sken		hw->link.speed =  1 * 1000;
9937331766Sken		break;
9938331766Sken	case SLI4_READ_TOPOLOGY_SPEED_2G:
9939331766Sken		hw->link.speed =  2 * 1000;
9940331766Sken		break;
9941331766Sken	case SLI4_READ_TOPOLOGY_SPEED_4G:
9942331766Sken		hw->link.speed =  4 * 1000;
9943331766Sken		break;
9944331766Sken	case SLI4_READ_TOPOLOGY_SPEED_8G:
9945331766Sken		hw->link.speed =  8 * 1000;
9946331766Sken		break;
9947331766Sken	case SLI4_READ_TOPOLOGY_SPEED_16G:
9948331766Sken		hw->link.speed = 16 * 1000;
9949331766Sken		hw->link.loop_map = NULL;
9950331766Sken		break;
9951331766Sken	case SLI4_READ_TOPOLOGY_SPEED_32G:
9952331766Sken		hw->link.speed = 32 * 1000;
9953331766Sken		hw->link.loop_map = NULL;
9954331766Sken		break;
9955331766Sken	}
9956331766Sken
9957331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9958331766Sken
9959331766Sken	ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9960331766Sken
9961331766Sken	return 0;
9962331766Sken}
9963331766Sken
9964331766Skenstatic int32_t
9965331766Sken__ocs_hw_port_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9966331766Sken{
9967331766Sken	ocs_sli_port_t	*sport = ctx->app;
9968331766Sken	ocs_hw_t	*hw = sport->hw;
9969331766Sken
9970331766Sken	smtrace("port");
9971331766Sken
9972331766Sken	switch (evt) {
9973331766Sken	case OCS_EVT_EXIT:
9974331766Sken		/* ignore */
9975331766Sken		break;
9976331766Sken
9977331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
9978331766Sken	case OCS_EVT_HW_PORT_REQ_ATTACH:
9979331766Sken		if (data != NULL) {
9980331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
9981331766Sken		}
9982331766Sken		/* fall through */
9983331766Sken	default:
9984331766Sken		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
9985331766Sken		break;
9986331766Sken	}
9987331766Sken
9988331766Sken	return 0;
9989331766Sken}
9990331766Sken
9991331766Skenstatic void *
9992331766Sken__ocs_hw_port_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9993331766Sken{
9994331766Sken	ocs_sli_port_t	*sport = ctx->app;
9995331766Sken	ocs_hw_t	*hw = sport->hw;
9996331766Sken
9997331766Sken	smtrace("port");
9998331766Sken
9999331766Sken	switch (evt) {
10000331766Sken	case OCS_EVT_ENTER:
10001331766Sken		if (data != NULL) {
10002331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10003331766Sken		}
10004331766Sken		if (hw->callback.port != NULL) {
10005331766Sken			hw->callback.port(hw->args.port,
10006331766Sken					OCS_HW_PORT_FREE_FAIL, sport);
10007331766Sken		}
10008331766Sken		break;
10009331766Sken	default:
10010331766Sken		break;
10011331766Sken	}
10012331766Sken
10013331766Sken	return NULL;
10014331766Sken}
10015331766Sken
10016331766Skenstatic void *
10017331766Sken__ocs_hw_port_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10018331766Sken{
10019331766Sken	ocs_sli_port_t	*sport = ctx->app;
10020331766Sken	ocs_hw_t	*hw = sport->hw;
10021331766Sken
10022331766Sken	smtrace("port");
10023331766Sken
10024331766Sken	switch (evt) {
10025331766Sken	case OCS_EVT_ENTER:
10026331766Sken		/* free SLI resource */
10027331766Sken		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator)) {
10028331766Sken			ocs_log_err(hw->os, "FCOE_VPI free failure addr=%#x\n", sport->fc_id);
10029331766Sken		}
10030331766Sken
10031331766Sken		/* free mailbox buffer */
10032331766Sken		if (data != NULL) {
10033331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10034331766Sken		}
10035331766Sken		if (hw->callback.port != NULL) {
10036331766Sken			hw->callback.port(hw->args.port,
10037331766Sken					OCS_HW_PORT_FREE_OK, sport);
10038331766Sken		}
10039331766Sken		break;
10040331766Sken	default:
10041331766Sken		break;
10042331766Sken	}
10043331766Sken
10044331766Sken	return NULL;
10045331766Sken}
10046331766Sken
10047331766Skenstatic void *
10048331766Sken__ocs_hw_port_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10049331766Sken{
10050331766Sken	ocs_sli_port_t	*sport = ctx->app;
10051331766Sken	ocs_hw_t	*hw = sport->hw;
10052331766Sken
10053331766Sken	smtrace("port");
10054331766Sken
10055331766Sken	switch (evt) {
10056331766Sken	case OCS_EVT_ENTER:
10057331766Sken		/* free SLI resource */
10058331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10059331766Sken
10060331766Sken		/* free mailbox buffer */
10061331766Sken		if (data != NULL) {
10062331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10063331766Sken		}
10064331766Sken
10065331766Sken		if (hw->callback.port != NULL) {
10066331766Sken			hw->callback.port(hw->args.port,
10067331766Sken					OCS_HW_PORT_ATTACH_FAIL, sport);
10068331766Sken		}
10069331766Sken		if (sport->sm_free_req_pending) {
10070331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10071331766Sken		}
10072331766Sken		break;
10073331766Sken	default:
10074331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10075331766Sken		break;
10076331766Sken	}
10077331766Sken
10078331766Sken	return NULL;
10079331766Sken}
10080331766Sken
10081331766Skenstatic void *
10082331766Sken__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10083331766Sken{
10084331766Sken	ocs_sli_port_t	*sport = ctx->app;
10085331766Sken	ocs_hw_t	*hw = sport->hw;
10086331766Sken	uint8_t		*cmd = NULL;
10087331766Sken
10088331766Sken	smtrace("port");
10089331766Sken
10090331766Sken	switch (evt) {
10091331766Sken	case OCS_EVT_ENTER:
10092331766Sken		/* allocate memory and send unreg_vpi */
10093331766Sken		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10094331766Sken		if (!cmd) {
10095331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10096331766Sken			break;
10097331766Sken		}
10098331766Sken
10099331766Sken		if (0 == sli_cmd_unreg_vpi(&hw->sli, cmd, SLI4_BMBX_SIZE, sport->indicator,
10100331766Sken					   SLI4_UNREG_TYPE_PORT)) {
10101331766Sken			ocs_log_err(hw->os, "UNREG_VPI format failure\n");
10102331766Sken			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10103331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10104331766Sken			break;
10105331766Sken		}
10106331766Sken
10107331766Sken		if (ocs_hw_command(hw, cmd, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10108331766Sken			ocs_log_err(hw->os, "UNREG_VPI command failure\n");
10109331766Sken			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10110331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10111331766Sken			break;
10112331766Sken		}
10113331766Sken		break;
10114331766Sken	case OCS_EVT_RESPONSE:
10115331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10116331766Sken		break;
10117331766Sken	case OCS_EVT_ERROR:
10118331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10119331766Sken		break;
10120331766Sken	default:
10121331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10122331766Sken		break;
10123331766Sken	}
10124331766Sken
10125331766Sken	return NULL;
10126331766Sken}
10127331766Sken
10128331766Skenstatic void *
10129331766Sken__ocs_hw_port_free_nop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10130331766Sken{
10131331766Sken	ocs_sli_port_t	*sport = ctx->app;
10132331766Sken	ocs_hw_t	*hw = sport->hw;
10133331766Sken
10134331766Sken	smtrace("port");
10135331766Sken
10136331766Sken	switch (evt) {
10137331766Sken	case OCS_EVT_ENTER:
10138331766Sken		/* Forward to execute in mailbox completion processing context */
10139331766Sken		if (ocs_hw_async_call(hw, __ocs_hw_port_realloc_cb, sport)) {
10140331766Sken			ocs_log_err(hw->os, "ocs_hw_async_call failed\n");
10141331766Sken		}
10142331766Sken		break;
10143331766Sken	case OCS_EVT_RESPONSE:
10144331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10145331766Sken		break;
10146331766Sken	case OCS_EVT_ERROR:
10147331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10148331766Sken		break;
10149331766Sken	default:
10150331766Sken		break;
10151331766Sken	}
10152331766Sken
10153331766Sken	return NULL;
10154331766Sken}
10155331766Sken
10156331766Skenstatic void *
10157331766Sken__ocs_hw_port_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10158331766Sken{
10159331766Sken	ocs_sli_port_t	*sport = ctx->app;
10160331766Sken	ocs_hw_t	*hw = sport->hw;
10161331766Sken
10162331766Sken	smtrace("port");
10163331766Sken
10164331766Sken	switch (evt) {
10165331766Sken	case OCS_EVT_ENTER:
10166331766Sken		if (data != NULL) {
10167331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10168331766Sken		}
10169331766Sken		if (hw->callback.port != NULL) {
10170331766Sken			hw->callback.port(hw->args.port,
10171331766Sken					OCS_HW_PORT_ATTACH_OK, sport);
10172331766Sken		}
10173331766Sken		if (sport->sm_free_req_pending) {
10174331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10175331766Sken		}
10176331766Sken		break;
10177331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10178331766Sken		/* virtual/physical port request free */
10179331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10180331766Sken		break;
10181331766Sken	default:
10182331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10183331766Sken		break;
10184331766Sken	}
10185331766Sken
10186331766Sken	return NULL;
10187331766Sken}
10188331766Sken
10189331766Skenstatic void *
10190331766Sken__ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10191331766Sken{
10192331766Sken	ocs_sli_port_t	*sport = ctx->app;
10193331766Sken	ocs_hw_t	*hw = sport->hw;
10194331766Sken
10195331766Sken	smtrace("port");
10196331766Sken
10197331766Sken	switch (evt) {
10198331766Sken	case OCS_EVT_ENTER:
10199331766Sken		if (0 == sli_cmd_reg_vpi(&hw->sli, data, SLI4_BMBX_SIZE, sport, FALSE)) {
10200331766Sken			ocs_log_err(hw->os, "REG_VPI format failure\n");
10201331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10202331766Sken			break;
10203331766Sken		}
10204331766Sken
10205331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10206331766Sken			ocs_log_err(hw->os, "REG_VPI command failure\n");
10207331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10208331766Sken			break;
10209331766Sken		}
10210331766Sken		break;
10211331766Sken	case OCS_EVT_RESPONSE:
10212331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10213331766Sken		break;
10214331766Sken	case OCS_EVT_ERROR:
10215331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_attach_report_fail, data);
10216331766Sken		break;
10217331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10218331766Sken		/* Wait for attach response and then free */
10219331766Sken		sport->sm_free_req_pending = 1;
10220331766Sken		break;
10221331766Sken	default:
10222331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10223331766Sken		break;
10224331766Sken	}
10225331766Sken
10226331766Sken	return NULL;
10227331766Sken}
10228331766Sken
10229331766Skenstatic void *
10230331766Sken__ocs_hw_port_done(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10231331766Sken{
10232331766Sken	ocs_sli_port_t	*sport = ctx->app;
10233331766Sken	ocs_hw_t	*hw = sport->hw;
10234331766Sken
10235331766Sken	smtrace("port");
10236331766Sken
10237331766Sken	switch (evt) {
10238331766Sken	case OCS_EVT_ENTER:
10239331766Sken		/* free SLI resource */
10240331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10241331766Sken
10242331766Sken		/* free mailbox buffer */
10243331766Sken		if (data != NULL) {
10244331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10245331766Sken		}
10246331766Sken		break;
10247331766Sken	default:
10248331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10249331766Sken		break;
10250331766Sken	}
10251331766Sken
10252331766Sken	return NULL;
10253331766Sken}
10254331766Sken
10255331766Skenstatic void *
10256331766Sken__ocs_hw_port_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10257331766Sken{
10258331766Sken	ocs_sli_port_t	*sport = ctx->app;
10259331766Sken	ocs_hw_t	*hw = sport->hw;
10260331766Sken
10261331766Sken	smtrace("port");
10262331766Sken
10263331766Sken	switch (evt) {
10264331766Sken	case OCS_EVT_ENTER:
10265331766Sken		if (data != NULL) {
10266331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10267331766Sken		}
10268331766Sken		if (hw->callback.port != NULL) {
10269331766Sken			hw->callback.port(hw->args.port,
10270331766Sken					OCS_HW_PORT_ALLOC_OK, sport);
10271331766Sken		}
10272331766Sken		/* If there is a pending free request, then handle it now */
10273331766Sken		if (sport->sm_free_req_pending) {
10274331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10275331766Sken		}
10276331766Sken		break;
10277331766Sken	case OCS_EVT_HW_PORT_REQ_ATTACH:
10278331766Sken		/* virtual port requests attach */
10279331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_attach_reg_vpi, data);
10280331766Sken		break;
10281331766Sken	case OCS_EVT_HW_PORT_ATTACH_OK:
10282331766Sken		/* physical port attached (as part of attaching domain) */
10283331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10284331766Sken		break;
10285331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10286331766Sken		/* virtual port request free */
10287331766Sken		if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
10288331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10289331766Sken		} else {
10290331766Sken			/*
10291331766Sken			 * Note: BE3/Skyhawk will respond with a status of 0x20
10292331766Sken			 *       unless the reg_vpi has been issued, so we can
10293331766Sken			 *       skip the unreg_vpi for these adapters.
10294331766Sken			 *
10295331766Sken			 * Send a nop to make sure that free doesn't occur in
10296331766Sken			 * same context
10297331766Sken			 */
10298331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_nop, NULL);
10299331766Sken		}
10300331766Sken		break;
10301331766Sken	default:
10302331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10303331766Sken		break;
10304331766Sken	}
10305331766Sken
10306331766Sken	return NULL;
10307331766Sken}
10308331766Sken
10309331766Skenstatic void *
10310331766Sken__ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10311331766Sken{
10312331766Sken	ocs_sli_port_t	*sport = ctx->app;
10313331766Sken	ocs_hw_t	*hw = sport->hw;
10314331766Sken
10315331766Sken	smtrace("port");
10316331766Sken
10317331766Sken	switch (evt) {
10318331766Sken	case OCS_EVT_ENTER:
10319331766Sken		/* free SLI resource */
10320331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10321331766Sken
10322331766Sken		/* free mailbox buffer */
10323331766Sken		if (data != NULL) {
10324331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10325331766Sken		}
10326331766Sken
10327331766Sken		if (hw->callback.port != NULL) {
10328331766Sken			hw->callback.port(hw->args.port,
10329331766Sken					OCS_HW_PORT_ALLOC_FAIL, sport);
10330331766Sken		}
10331331766Sken
10332331766Sken		/* If there is a pending free request, then handle it now */
10333331766Sken		if (sport->sm_free_req_pending) {
10334331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10335331766Sken		}
10336331766Sken		break;
10337331766Sken	default:
10338331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10339331766Sken		break;
10340331766Sken	}
10341331766Sken
10342331766Sken	return NULL;
10343331766Sken}
10344331766Sken
10345331766Skenstatic void *
10346331766Sken__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10347331766Sken{
10348331766Sken	ocs_sli_port_t	*sport = ctx->app;
10349331766Sken	ocs_hw_t	*hw = sport->hw;
10350331766Sken	uint8_t		*payload = NULL;
10351331766Sken
10352331766Sken	smtrace("port");
10353331766Sken
10354331766Sken	switch (evt) {
10355331766Sken	case OCS_EVT_ENTER:
10356331766Sken		/* allocate memory for the service parameters */
10357331766Sken		if (ocs_dma_alloc(hw->os, &sport->dma, 112, 4)) {
10358331766Sken			ocs_log_err(hw->os, "Failed to allocate DMA memory\n");
10359331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10360331766Sken			break;
10361331766Sken		}
10362331766Sken
10363331766Sken		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10364331766Sken					&sport->dma, sport->indicator)) {
10365331766Sken			ocs_log_err(hw->os, "READ_SPARM64 allocation failure\n");
10366331766Sken			ocs_dma_free(hw->os, &sport->dma);
10367331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10368331766Sken			break;
10369331766Sken		}
10370331766Sken
10371331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10372331766Sken			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10373331766Sken			ocs_dma_free(hw->os, &sport->dma);
10374331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10375331766Sken			break;
10376331766Sken		}
10377331766Sken		break;
10378331766Sken	case OCS_EVT_RESPONSE:
10379331766Sken		payload = sport->dma.virt;
10380331766Sken
10381331766Sken		ocs_display_sparams(sport->display_name, "sport sparm64", 0, NULL, payload);
10382331766Sken
10383331766Sken		ocs_memcpy(&sport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
10384331766Sken				sizeof(sport->sli_wwpn));
10385331766Sken		ocs_memcpy(&sport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
10386331766Sken				sizeof(sport->sli_wwnn));
10387331766Sken
10388331766Sken		ocs_dma_free(hw->os, &sport->dma);
10389331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_alloc_init_vpi, data);
10390331766Sken		break;
10391331766Sken	case OCS_EVT_ERROR:
10392331766Sken		ocs_dma_free(hw->os, &sport->dma);
10393331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10394331766Sken		break;
10395331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10396331766Sken		/* Wait for attach response and then free */
10397331766Sken		sport->sm_free_req_pending = 1;
10398331766Sken		break;
10399331766Sken	case OCS_EVT_EXIT:
10400331766Sken		break;
10401331766Sken	default:
10402331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10403331766Sken		break;
10404331766Sken	}
10405331766Sken
10406331766Sken	return NULL;
10407331766Sken}
10408331766Sken
10409331766Skenstatic void *
10410331766Sken__ocs_hw_port_alloc_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10411331766Sken{
10412331766Sken	ocs_sli_port_t	*sport = ctx->app;
10413331766Sken
10414331766Sken	smtrace("port");
10415331766Sken
10416331766Sken	switch (evt) {
10417331766Sken	case OCS_EVT_ENTER:
10418331766Sken		/* no-op */
10419331766Sken		break;
10420331766Sken	case OCS_EVT_HW_PORT_ALLOC_OK:
10421331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_allocated, NULL);
10422331766Sken		break;
10423331766Sken	case OCS_EVT_HW_PORT_ALLOC_FAIL:
10424331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, NULL);
10425331766Sken		break;
10426331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10427331766Sken		/* Wait for attach response and then free */
10428331766Sken		sport->sm_free_req_pending = 1;
10429331766Sken		break;
10430331766Sken	default:
10431331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10432331766Sken		break;
10433331766Sken	}
10434331766Sken
10435331766Sken	return NULL;
10436331766Sken}
10437331766Sken
10438331766Skenstatic void *
10439331766Sken__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10440331766Sken{
10441331766Sken	ocs_sli_port_t	*sport = ctx->app;
10442331766Sken	ocs_hw_t	*hw = sport->hw;
10443331766Sken
10444331766Sken	smtrace("port");
10445331766Sken
10446331766Sken	switch (evt) {
10447331766Sken	case OCS_EVT_ENTER:
10448331766Sken		/* If there is a pending free request, then handle it now */
10449331766Sken		if (sport->sm_free_req_pending) {
10450331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_freed, NULL);
10451331766Sken			return NULL;
10452331766Sken		}
10453331766Sken
10454331766Sken		/* TODO XXX transitioning to done only works if this is called
10455331766Sken		 * directly from ocs_hw_port_alloc BUT not if called from
10456331766Sken		 * read_sparm64. In the later case, we actually want to go
10457331766Sken		 * through report_ok/fail
10458331766Sken		 */
10459331766Sken		if (0 == sli_cmd_init_vpi(&hw->sli, data, SLI4_BMBX_SIZE,
10460331766Sken					sport->indicator, sport->domain->indicator)) {
10461331766Sken			ocs_log_err(hw->os, "INIT_VPI allocation failure\n");
10462331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10463331766Sken			break;
10464331766Sken		}
10465331766Sken
10466331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10467331766Sken			ocs_log_err(hw->os, "INIT_VPI command failure\n");
10468331766Sken			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10469331766Sken			break;
10470331766Sken		}
10471331766Sken		break;
10472331766Sken	case OCS_EVT_RESPONSE:
10473331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_allocated, data);
10474331766Sken		break;
10475331766Sken	case OCS_EVT_ERROR:
10476331766Sken		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10477331766Sken		break;
10478331766Sken	case OCS_EVT_HW_PORT_REQ_FREE:
10479331766Sken		/* Wait for attach response and then free */
10480331766Sken		sport->sm_free_req_pending = 1;
10481331766Sken		break;
10482331766Sken	case OCS_EVT_EXIT:
10483331766Sken		break;
10484331766Sken	default:
10485331766Sken		__ocs_hw_port_common(__func__, ctx, evt, data);
10486331766Sken		break;
10487331766Sken	}
10488331766Sken
10489331766Sken	return NULL;
10490331766Sken}
10491331766Sken
10492331766Skenstatic int32_t
10493331766Sken__ocs_hw_port_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10494331766Sken{
10495331766Sken	ocs_sli_port_t *sport = arg;
10496331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10497331766Sken	ocs_sm_event_t	evt;
10498331766Sken
10499331766Sken	if (status || hdr->status) {
10500331766Sken		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10501331766Sken			      sport->indicator, status, hdr->status);
10502331766Sken		evt = OCS_EVT_ERROR;
10503331766Sken	} else {
10504331766Sken		evt = OCS_EVT_RESPONSE;
10505331766Sken	}
10506331766Sken
10507331766Sken	ocs_sm_post_event(&sport->ctx, evt, mqe);
10508331766Sken
10509331766Sken	return 0;
10510331766Sken}
10511331766Sken
10512331766Skenstatic int32_t
10513331766Sken__ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10514331766Sken{
10515331766Sken	ocs_sli_port_t *sport = arg;
10516331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10517331766Sken	ocs_sm_event_t	evt;
10518331766Sken	uint8_t *mqecpy;
10519331766Sken
10520331766Sken	if (status || hdr->status) {
10521331766Sken		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10522331766Sken			      sport->indicator, status, hdr->status);
10523331766Sken		evt = OCS_EVT_ERROR;
10524331766Sken	} else {
10525331766Sken		evt = OCS_EVT_RESPONSE;
10526331766Sken	}
10527331766Sken
10528331766Sken	/*
10529331766Sken	 * In this case we have to malloc a mailbox command buffer, as it is reused
10530331766Sken	 * in the state machine post event call, and eventually freed
10531331766Sken	 */
10532331766Sken	mqecpy = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10533331766Sken	if (mqecpy == NULL) {
10534331766Sken		ocs_log_err(hw->os, "malloc mqecpy failed\n");
10535331766Sken		return -1;
10536331766Sken	}
10537331766Sken	ocs_memcpy(mqecpy, mqe, SLI4_BMBX_SIZE);
10538331766Sken
10539331766Sken	ocs_sm_post_event(&sport->ctx, evt, mqecpy);
10540331766Sken
10541331766Sken	return 0;
10542331766Sken}
10543331766Sken
10544331766Sken/***************************************************************************
10545331766Sken * Domain state machine
10546331766Sken */
10547331766Sken
10548331766Skenstatic int32_t
10549331766Sken__ocs_hw_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10550331766Sken{
10551331766Sken	ocs_domain_t	*domain = ctx->app;
10552331766Sken	ocs_hw_t	*hw = domain->hw;
10553331766Sken
10554331766Sken	smtrace("domain");
10555331766Sken
10556331766Sken	switch (evt) {
10557331766Sken	case OCS_EVT_EXIT:
10558331766Sken		/* ignore */
10559331766Sken		break;
10560331766Sken
10561331766Sken	default:
10562331766Sken		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
10563331766Sken		break;
10564331766Sken	}
10565331766Sken
10566331766Sken	return 0;
10567331766Sken}
10568331766Sken
10569331766Skenstatic void *
10570331766Sken__ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10571331766Sken{
10572331766Sken	ocs_domain_t	*domain = ctx->app;
10573331766Sken	ocs_hw_t	*hw = domain->hw;
10574331766Sken
10575331766Sken	smtrace("domain");
10576331766Sken
10577331766Sken	switch (evt) {
10578331766Sken	case OCS_EVT_ENTER:
10579331766Sken		/* free command buffer */
10580331766Sken		if (data != NULL) {
10581331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10582331766Sken		}
10583331766Sken		/* free SLI resources */
10584331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10585331766Sken		/* TODO how to free FCFI (or do we at all)? */
10586331766Sken
10587331766Sken		if (hw->callback.domain != NULL) {
10588331766Sken			hw->callback.domain(hw->args.domain,
10589331766Sken					OCS_HW_DOMAIN_ALLOC_FAIL,
10590331766Sken					domain);
10591331766Sken		}
10592331766Sken		break;
10593331766Sken	default:
10594331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10595331766Sken		break;
10596331766Sken	}
10597331766Sken
10598331766Sken	return NULL;
10599331766Sken}
10600331766Sken
10601331766Skenstatic void *
10602331766Sken__ocs_hw_domain_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10603331766Sken{
10604331766Sken	ocs_domain_t	*domain = ctx->app;
10605331766Sken	ocs_hw_t	*hw = domain->hw;
10606331766Sken
10607331766Sken	smtrace("domain");
10608331766Sken
10609331766Sken	switch (evt) {
10610331766Sken	case OCS_EVT_ENTER:
10611331766Sken		/* free mailbox buffer and send alloc ok to physical sport */
10612331766Sken		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10613331766Sken		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ATTACH_OK, NULL);
10614331766Sken
10615331766Sken		/* now inform registered callbacks */
10616331766Sken		if (hw->callback.domain != NULL) {
10617331766Sken			hw->callback.domain(hw->args.domain,
10618331766Sken					OCS_HW_DOMAIN_ATTACH_OK,
10619331766Sken					domain);
10620331766Sken		}
10621331766Sken		break;
10622331766Sken	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10623331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10624331766Sken		break;
10625331766Sken	default:
10626331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10627331766Sken		break;
10628331766Sken	}
10629331766Sken
10630331766Sken	return NULL;
10631331766Sken}
10632331766Sken
10633331766Skenstatic void *
10634331766Sken__ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10635331766Sken{
10636331766Sken	ocs_domain_t	*domain = ctx->app;
10637331766Sken	ocs_hw_t	*hw = domain->hw;
10638331766Sken
10639331766Sken	smtrace("domain");
10640331766Sken
10641331766Sken	switch (evt) {
10642331766Sken	case OCS_EVT_ENTER:
10643331766Sken		if (data != NULL) {
10644331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10645331766Sken		}
10646331766Sken		/* free SLI resources */
10647331766Sken		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10648331766Sken		/* TODO how to free FCFI (or do we at all)? */
10649331766Sken
10650331766Sken		if (hw->callback.domain != NULL) {
10651331766Sken			hw->callback.domain(hw->args.domain,
10652331766Sken					OCS_HW_DOMAIN_ATTACH_FAIL,
10653331766Sken					domain);
10654331766Sken		}
10655331766Sken		break;
10656331766Sken	case OCS_EVT_EXIT:
10657331766Sken		break;
10658331766Sken	default:
10659331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10660331766Sken		break;
10661331766Sken	}
10662331766Sken
10663331766Sken	return NULL;
10664331766Sken}
10665331766Sken
10666331766Skenstatic void *
10667331766Sken__ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10668331766Sken{
10669331766Sken	ocs_domain_t	*domain = ctx->app;
10670331766Sken	ocs_hw_t	*hw = domain->hw;
10671331766Sken
10672331766Sken	smtrace("domain");
10673331766Sken
10674331766Sken	switch (evt) {
10675331766Sken	case OCS_EVT_ENTER:
10676331766Sken
10677331766Sken		ocs_display_sparams("", "reg vpi", 0, NULL, domain->dma.virt);
10678331766Sken
10679331766Sken		if (0 == sli_cmd_reg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain)) {
10680331766Sken			ocs_log_err(hw->os, "REG_VFI format failure\n");
10681331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10682331766Sken			break;
10683331766Sken		}
10684331766Sken
10685331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10686331766Sken			ocs_log_err(hw->os, "REG_VFI command failure\n");
10687331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10688331766Sken			break;
10689331766Sken		}
10690331766Sken		break;
10691331766Sken	case OCS_EVT_RESPONSE:
10692331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_attached, data);
10693331766Sken		break;
10694331766Sken	case OCS_EVT_ERROR:
10695331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_attach_report_fail, data);
10696331766Sken		break;
10697331766Sken	default:
10698331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10699331766Sken		break;
10700331766Sken	}
10701331766Sken
10702331766Sken	return NULL;
10703331766Sken}
10704331766Sken
10705331766Skenstatic void *
10706331766Sken__ocs_hw_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10707331766Sken{
10708331766Sken	ocs_domain_t	*domain = ctx->app;
10709331766Sken	ocs_hw_t	*hw = domain->hw;
10710331766Sken
10711331766Sken	smtrace("domain");
10712331766Sken
10713331766Sken	switch (evt) {
10714331766Sken	case OCS_EVT_ENTER:
10715331766Sken		/* free mailbox buffer and send alloc ok to physical sport */
10716331766Sken		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10717331766Sken		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ALLOC_OK, NULL);
10718331766Sken
10719331766Sken		ocs_hw_domain_add(hw, domain);
10720331766Sken
10721331766Sken		/* now inform registered callbacks */
10722331766Sken		if (hw->callback.domain != NULL) {
10723331766Sken			hw->callback.domain(hw->args.domain,
10724331766Sken					OCS_HW_DOMAIN_ALLOC_OK,
10725331766Sken					domain);
10726331766Sken		}
10727331766Sken		break;
10728331766Sken	case OCS_EVT_HW_DOMAIN_REQ_ATTACH:
10729331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_attach_reg_vfi, data);
10730331766Sken		break;
10731331766Sken	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10732331766Sken		/* unreg_fcfi/vfi */
10733331766Sken		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10734331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, NULL);
10735331766Sken		} else {
10736331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10737331766Sken		}
10738331766Sken		break;
10739331766Sken	default:
10740331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10741331766Sken		break;
10742331766Sken	}
10743331766Sken
10744331766Sken	return NULL;
10745331766Sken}
10746331766Sken
10747331766Skenstatic void *
10748331766Sken__ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10749331766Sken{
10750331766Sken	ocs_domain_t	*domain = ctx->app;
10751331766Sken	ocs_hw_t	*hw = domain->hw;
10752331766Sken
10753331766Sken	smtrace("domain");
10754331766Sken
10755331766Sken	switch (evt) {
10756331766Sken	case OCS_EVT_ENTER:
10757331766Sken		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10758331766Sken					&domain->dma, SLI4_READ_SPARM64_VPI_DEFAULT)) {
10759331766Sken			ocs_log_err(hw->os, "READ_SPARM64 format failure\n");
10760331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10761331766Sken			break;
10762331766Sken		}
10763331766Sken
10764331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10765331766Sken			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10766331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10767331766Sken			break;
10768331766Sken		}
10769331766Sken		break;
10770331766Sken	case OCS_EVT_EXIT:
10771331766Sken		break;
10772331766Sken	case OCS_EVT_RESPONSE:
10773331766Sken		ocs_display_sparams(domain->display_name, "domain sparm64", 0, NULL, domain->dma.virt);
10774331766Sken
10775331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_allocated, data);
10776331766Sken		break;
10777331766Sken	case OCS_EVT_ERROR:
10778331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10779331766Sken		break;
10780331766Sken	default:
10781331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10782331766Sken		break;
10783331766Sken	}
10784331766Sken
10785331766Sken	return NULL;
10786331766Sken}
10787331766Sken
10788331766Skenstatic void *
10789331766Sken__ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10790331766Sken{
10791331766Sken	ocs_domain_t	*domain = ctx->app;
10792331766Sken	ocs_sli_port_t	*sport = domain->sport;
10793331766Sken	ocs_hw_t	*hw = domain->hw;
10794331766Sken
10795331766Sken	smtrace("domain");
10796331766Sken
10797331766Sken	switch (evt) {
10798331766Sken	case OCS_EVT_ENTER:
10799331766Sken		if (0 == sli_cmd_init_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->indicator,
10800331766Sken					domain->fcf_indicator, sport->indicator)) {
10801331766Sken			ocs_log_err(hw->os, "INIT_VFI format failure\n");
10802331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10803331766Sken			break;
10804331766Sken		}
10805331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10806331766Sken			ocs_log_err(hw->os, "INIT_VFI command failure\n");
10807331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10808331766Sken			break;
10809331766Sken		}
10810331766Sken		break;
10811331766Sken	case OCS_EVT_EXIT:
10812331766Sken		break;
10813331766Sken	case OCS_EVT_RESPONSE:
10814331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10815331766Sken		break;
10816331766Sken	case OCS_EVT_ERROR:
10817331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10818331766Sken		break;
10819331766Sken	default:
10820331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10821331766Sken		break;
10822331766Sken	}
10823331766Sken
10824331766Sken	return NULL;
10825331766Sken}
10826331766Sken
10827331766Skenstatic void *
10828331766Sken__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10829331766Sken{
10830331766Sken	ocs_domain_t	*domain = ctx->app;
10831331766Sken	ocs_hw_t	*hw = domain->hw;
10832331766Sken
10833331766Sken	smtrace("domain");
10834331766Sken
10835331766Sken	switch (evt) {
10836331766Sken	case OCS_EVT_ENTER: {
10837331766Sken		sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
10838331766Sken		uint32_t i;
10839331766Sken
10840331766Sken		/* Set the filter match/mask values from hw's filter_def values */
10841331766Sken		for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
10842331766Sken			rq_cfg[i].rq_id = 0xffff;
10843331766Sken			rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
10844331766Sken			rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
10845331766Sken			rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
10846331766Sken			rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
10847331766Sken		}
10848331766Sken
10849331766Sken		/* Set the rq_id for each, in order of RQ definition */
10850331766Sken		for (i = 0; i < hw->hw_rq_count; i++) {
10851331766Sken			if (i >= ARRAY_SIZE(rq_cfg)) {
10852331766Sken				ocs_log_warn(hw->os, "more RQs than REG_FCFI filter entries\n");
10853331766Sken				break;
10854331766Sken			}
10855331766Sken			rq_cfg[i].rq_id = hw->hw_rq[i]->hdr->id;
10856331766Sken		}
10857331766Sken
10858331766Sken		if (!data) {
10859331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10860331766Sken			break;
10861331766Sken		}
10862331766Sken
10863331766Sken		if (hw->hw_mrq_count) {
10864331766Sken			if (OCS_HW_RTN_SUCCESS != ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE,
10865331766Sken				 domain->vlan_id, domain->fcf)) {
10866331766Sken				ocs_log_err(hw->os, "REG_FCFI_MRQ format failure\n");
10867331766Sken				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10868331766Sken				break;
10869331766Sken			}
10870331766Sken
10871331766Sken		} else {
10872331766Sken			if (0 == sli_cmd_reg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf,
10873331766Sken						rq_cfg, domain->vlan_id)) {
10874331766Sken				ocs_log_err(hw->os, "REG_FCFI format failure\n");
10875331766Sken				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10876331766Sken				break;
10877331766Sken			}
10878331766Sken		}
10879331766Sken
10880331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10881331766Sken			ocs_log_err(hw->os, "REG_FCFI command failure\n");
10882331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10883331766Sken			break;
10884331766Sken		}
10885331766Sken		break;
10886331766Sken	}
10887331766Sken	case OCS_EVT_EXIT:
10888331766Sken		break;
10889331766Sken	case OCS_EVT_RESPONSE:
10890331766Sken		if (!data) {
10891331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10892331766Sken			break;
10893331766Sken		}
10894331766Sken
10895331766Sken		domain->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)data)->fcfi;
10896331766Sken
10897331766Sken		/*
10898331766Sken		 * IF_TYPE 0 devices do not support explicit VFI and VPI initialization
10899331766Sken		 * and instead rely on implicit initialization during VFI registration.
10900331766Sken		 * Short circuit normal processing here for those devices.
10901331766Sken		 */
10902331766Sken		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10903331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10904331766Sken		} else {
10905331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_init_vfi, data);
10906331766Sken		}
10907331766Sken		break;
10908331766Sken	case OCS_EVT_ERROR:
10909331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10910331766Sken		break;
10911331766Sken	default:
10912331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10913331766Sken		break;
10914331766Sken	}
10915331766Sken
10916331766Sken	return NULL;
10917331766Sken}
10918331766Sken
10919331766Skenstatic void *
10920331766Sken__ocs_hw_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10921331766Sken{
10922331766Sken	ocs_domain_t	*domain = ctx->app;
10923331766Sken	ocs_hw_t	*hw = domain->hw;
10924331766Sken
10925331766Sken	smtrace("domain");
10926331766Sken
10927331766Sken	switch (evt) {
10928331766Sken	case OCS_EVT_ENTER:
10929331766Sken		if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
10930331766Sken			/*
10931331766Sken			 * For FC, the HW alread registered a FCFI
10932331766Sken			 * Copy FCF information into the domain and jump to INIT_VFI
10933331766Sken			 */
10934331766Sken			domain->fcf_indicator = hw->fcf_indicator;
10935331766Sken			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_init_vfi, data);
10936331766Sken		} else {
10937331766Sken			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_reg_fcfi, data);
10938331766Sken		}
10939331766Sken		break;
10940331766Sken	default:
10941331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10942331766Sken		break;
10943331766Sken	}
10944331766Sken
10945331766Sken	return NULL;
10946331766Sken}
10947331766Sken
10948331766Skenstatic void *
10949331766Sken__ocs_hw_domain_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10950331766Sken{
10951331766Sken	ocs_domain_t	*domain = ctx->app;
10952331766Sken
10953331766Sken	smtrace("domain");
10954331766Sken
10955331766Sken	switch (evt) {
10956331766Sken	case OCS_EVT_ENTER:
10957331766Sken		if (domain != NULL) {
10958331766Sken			ocs_hw_t	*hw = domain->hw;
10959331766Sken
10960331766Sken			ocs_hw_domain_del(hw, domain);
10961331766Sken
10962331766Sken			if (hw->callback.domain != NULL) {
10963331766Sken				hw->callback.domain(hw->args.domain,
10964331766Sken						     OCS_HW_DOMAIN_FREE_FAIL,
10965331766Sken						     domain);
10966331766Sken			}
10967331766Sken		}
10968331766Sken
10969331766Sken		/* free command buffer */
10970331766Sken		if (data != NULL) {
10971331766Sken			ocs_free(domain != NULL ? domain->hw->os : NULL, data, SLI4_BMBX_SIZE);
10972331766Sken		}
10973331766Sken		break;
10974331766Sken	case OCS_EVT_EXIT:
10975331766Sken		break;
10976331766Sken	default:
10977331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
10978331766Sken		break;
10979331766Sken	}
10980331766Sken
10981331766Sken	return NULL;
10982331766Sken}
10983331766Sken
10984331766Skenstatic void *
10985331766Sken__ocs_hw_domain_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10986331766Sken{
10987331766Sken	ocs_domain_t	*domain = ctx->app;
10988331766Sken
10989331766Sken	smtrace("domain");
10990331766Sken
10991331766Sken	switch (evt) {
10992331766Sken	case OCS_EVT_ENTER:
10993331766Sken		/* Free DMA and mailbox buffer */
10994331766Sken		if (domain != NULL) {
10995331766Sken			ocs_hw_t *hw = domain->hw;
10996331766Sken
10997331766Sken			/* free VFI resource */
10998331766Sken			sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI,
10999331766Sken					  domain->indicator);
11000331766Sken
11001331766Sken			ocs_hw_domain_del(hw, domain);
11002331766Sken
11003331766Sken			/* inform registered callbacks */
11004331766Sken			if (hw->callback.domain != NULL) {
11005331766Sken				hw->callback.domain(hw->args.domain,
11006331766Sken						     OCS_HW_DOMAIN_FREE_OK,
11007331766Sken						     domain);
11008331766Sken			}
11009331766Sken		}
11010331766Sken		if (data != NULL) {
11011331766Sken			ocs_free(NULL, data, SLI4_BMBX_SIZE);
11012331766Sken		}
11013331766Sken		break;
11014331766Sken	case OCS_EVT_EXIT:
11015331766Sken		break;
11016331766Sken	default:
11017331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
11018331766Sken		break;
11019331766Sken	}
11020331766Sken
11021331766Sken	return NULL;
11022331766Sken}
11023331766Sken
11024331766Sken
11025331766Skenstatic void *
11026331766Sken__ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11027331766Sken{
11028331766Sken	ocs_domain_t	*domain = ctx->app;
11029331766Sken	ocs_hw_t	*hw = domain->hw;
11030331766Sken
11031331766Sken	smtrace("domain");
11032331766Sken
11033331766Sken	switch (evt) {
11034331766Sken	case OCS_EVT_ENTER:
11035331766Sken		/* if we're in the middle of a teardown, skip sending rediscover */
11036331766Sken		if (hw->state == OCS_HW_STATE_TEARDOWN_IN_PROGRESS) {
11037331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11038331766Sken			break;
11039331766Sken		}
11040331766Sken		if (0 == sli_cmd_fcoe_rediscover_fcf(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf)) {
11041331766Sken			ocs_log_err(hw->os, "REDISCOVER_FCF format failure\n");
11042331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11043331766Sken			break;
11044331766Sken		}
11045331766Sken
11046331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11047331766Sken			ocs_log_err(hw->os, "REDISCOVER_FCF command failure\n");
11048331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11049331766Sken		}
11050331766Sken		break;
11051331766Sken	case OCS_EVT_RESPONSE:
11052331766Sken	case OCS_EVT_ERROR:
11053331766Sken		/* REDISCOVER_FCF can fail if none exist */
11054331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11055331766Sken		break;
11056331766Sken	case OCS_EVT_EXIT:
11057331766Sken		break;
11058331766Sken	default:
11059331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
11060331766Sken		break;
11061331766Sken	}
11062331766Sken
11063331766Sken	return NULL;
11064331766Sken}
11065331766Sken
11066331766Skenstatic void *
11067331766Sken__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11068331766Sken{
11069331766Sken	ocs_domain_t	*domain = ctx->app;
11070331766Sken	ocs_hw_t	*hw = domain->hw;
11071331766Sken
11072331766Sken	smtrace("domain");
11073331766Sken
11074331766Sken	switch (evt) {
11075331766Sken	case OCS_EVT_ENTER:
11076331766Sken		if (data == NULL) {
11077331766Sken			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11078331766Sken			if (!data) {
11079331766Sken				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11080331766Sken				break;
11081331766Sken			}
11082331766Sken		}
11083331766Sken
11084331766Sken		if (0 == sli_cmd_unreg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf_indicator)) {
11085331766Sken			ocs_log_err(hw->os, "UNREG_FCFI format failure\n");
11086331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11087331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11088331766Sken			break;
11089331766Sken		}
11090331766Sken
11091331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11092331766Sken			ocs_log_err(hw->os, "UNREG_FCFI command failure\n");
11093331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11094331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11095331766Sken			break;
11096331766Sken		}
11097331766Sken		break;
11098331766Sken	case OCS_EVT_RESPONSE:
11099331766Sken		if (domain->req_rediscover_fcf) {
11100331766Sken			domain->req_rediscover_fcf = FALSE;
11101331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_redisc_fcf, data);
11102331766Sken		} else {
11103331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11104331766Sken		}
11105331766Sken		break;
11106331766Sken	case OCS_EVT_ERROR:
11107331766Sken		ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11108331766Sken		break;
11109331766Sken	case OCS_EVT_EXIT:
11110331766Sken		break;
11111331766Sken	default:
11112331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
11113331766Sken		break;
11114331766Sken	}
11115331766Sken
11116331766Sken	return NULL;
11117331766Sken}
11118331766Sken
11119331766Skenstatic void *
11120331766Sken__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11121331766Sken{
11122331766Sken	ocs_domain_t	*domain = ctx->app;
11123331766Sken	ocs_hw_t	*hw = domain->hw;
11124331766Sken	uint8_t		is_fc = FALSE;
11125331766Sken
11126331766Sken	smtrace("domain");
11127331766Sken
11128331766Sken	is_fc = (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC);
11129331766Sken
11130331766Sken	switch (evt) {
11131331766Sken	case OCS_EVT_ENTER:
11132331766Sken		if (data == NULL) {
11133331766Sken			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11134331766Sken			if (!data) {
11135331766Sken				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11136331766Sken				break;
11137331766Sken			}
11138331766Sken		}
11139331766Sken
11140331766Sken		if (0 == sli_cmd_unreg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain,
11141331766Sken					SLI4_UNREG_TYPE_DOMAIN)) {
11142331766Sken			ocs_log_err(hw->os, "UNREG_VFI format failure\n");
11143331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11144331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11145331766Sken			break;
11146331766Sken		}
11147331766Sken
11148331766Sken		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11149331766Sken			ocs_log_err(hw->os, "UNREG_VFI command failure\n");
11150331766Sken			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11151331766Sken			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11152331766Sken			break;
11153331766Sken		}
11154331766Sken		break;
11155331766Sken	case OCS_EVT_ERROR:
11156331766Sken		if (is_fc) {
11157331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11158331766Sken		} else {
11159331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11160331766Sken		}
11161331766Sken		break;
11162331766Sken	case OCS_EVT_RESPONSE:
11163331766Sken		if (is_fc) {
11164331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11165331766Sken		} else {
11166331766Sken			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11167331766Sken		}
11168331766Sken		break;
11169331766Sken	default:
11170331766Sken		__ocs_hw_domain_common(__func__, ctx, evt, data);
11171331766Sken		break;
11172331766Sken	}
11173331766Sken
11174331766Sken	return NULL;
11175331766Sken}
11176331766Sken
11177331766Sken/* callback for domain alloc/attach/free */
11178331766Skenstatic int32_t
11179331766Sken__ocs_hw_domain_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11180331766Sken{
11181331766Sken	ocs_domain_t	*domain = arg;
11182331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11183331766Sken	ocs_sm_event_t	evt;
11184331766Sken
11185331766Sken	if (status || hdr->status) {
11186331766Sken		ocs_log_debug(hw->os, "bad status vfi=%#x st=%x hdr=%x\n",
11187331766Sken			      domain->indicator, status, hdr->status);
11188331766Sken		evt = OCS_EVT_ERROR;
11189331766Sken	} else {
11190331766Sken		evt = OCS_EVT_RESPONSE;
11191331766Sken	}
11192331766Sken
11193331766Sken	ocs_sm_post_event(&domain->sm, evt, mqe);
11194331766Sken
11195331766Sken	return 0;
11196331766Sken}
11197331766Sken
11198331766Skenstatic int32_t
11199331766Skentarget_wqe_timer_nop_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11200331766Sken{
11201331766Sken	ocs_hw_io_t *io = NULL;
11202331766Sken	ocs_hw_io_t *io_next = NULL;
11203331766Sken	uint64_t ticks_current = ocs_get_os_ticks();
11204331766Sken	uint32_t sec_elapsed;
11205331766Sken
11206331766Sken	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11207331766Sken
11208331766Sken	if (status || hdr->status) {
11209331766Sken		ocs_log_debug(hw->os, "bad status st=%x hdr=%x\n",
11210331766Sken			      status, hdr->status);
11211331766Sken		/* go ahead and proceed with wqe timer checks... */
11212331766Sken	}
11213331766Sken
11214331766Sken	/* loop through active WQE list and check for timeouts */
11215331766Sken	ocs_lock(&hw->io_lock);
11216331766Sken		ocs_list_foreach_safe(&hw->io_timed_wqe, io, io_next) {
11217331766Sken			sec_elapsed = ((ticks_current - io->submit_ticks) / ocs_get_os_tick_freq());
11218331766Sken
11219331766Sken			/*
11220331766Sken			 * If elapsed time > timeout, abort it. No need to check type since
11221331766Sken			 * it wouldn't be on this list unless it was a target WQE
11222331766Sken			 */
11223331766Sken			if (sec_elapsed > io->tgt_wqe_timeout) {
11224331766Sken				ocs_log_test(hw->os, "IO timeout xri=0x%x tag=0x%x type=%d\n",
11225331766Sken					     io->indicator, io->reqtag, io->type);
11226331766Sken
11227331766Sken				/* remove from active_wqe list so won't try to abort again */
11228331766Sken				ocs_list_remove(&hw->io_timed_wqe, io);
11229331766Sken
11230331766Sken				/* save status of "timed out" for when abort completes */
11231331766Sken				io->status_saved = 1;
11232331766Sken				io->saved_status = SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT;
11233331766Sken				io->saved_ext = 0;
11234331766Sken				io->saved_len = 0;
11235331766Sken
11236331766Sken				/* now abort outstanding IO */
11237331766Sken				ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
11238331766Sken			}
11239331766Sken			/*
11240331766Sken			 * need to go through entire list since each IO could have a
11241331766Sken			 * different timeout value
11242331766Sken			 */
11243331766Sken		}
11244331766Sken	ocs_unlock(&hw->io_lock);
11245331766Sken
11246331766Sken	/* if we're not in the middle of shutting down, schedule next timer */
11247331766Sken	if (!hw->active_wqe_timer_shutdown) {
11248331766Sken		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw, OCS_HW_WQ_TIMER_PERIOD_MS);
11249331766Sken	}
11250331766Sken	hw->in_active_wqe_timer = FALSE;
11251331766Sken	return 0;
11252331766Sken}
11253331766Sken
11254331766Skenstatic void
11255331766Skentarget_wqe_timer_cb(void *arg)
11256331766Sken{
11257331766Sken	ocs_hw_t *hw = (ocs_hw_t *)arg;
11258331766Sken
11259331766Sken	/* delete existing timer; will kick off new timer after checking wqe timeouts */
11260331766Sken	hw->in_active_wqe_timer = TRUE;
11261331766Sken	ocs_del_timer(&hw->wqe_timer);
11262331766Sken
11263331766Sken	/* Forward timer callback to execute in the mailbox completion processing context */
11264331766Sken	if (ocs_hw_async_call(hw, target_wqe_timer_nop_cb, hw)) {
11265331766Sken		ocs_log_test(hw->os, "ocs_hw_async_call failed\n");
11266331766Sken	}
11267331766Sken}
11268331766Sken
11269331766Skenstatic void
11270331766Skenshutdown_target_wqe_timer(ocs_hw_t *hw)
11271331766Sken{
11272331766Sken	uint32_t	iters = 100;
11273331766Sken
11274331766Sken	if (hw->config.emulate_tgt_wqe_timeout) {
11275331766Sken		/* request active wqe timer shutdown, then wait for it to complete */
11276331766Sken		hw->active_wqe_timer_shutdown = TRUE;
11277331766Sken
11278331766Sken		/* delete WQE timer and wait for timer handler to complete (if necessary) */
11279331766Sken		ocs_del_timer(&hw->wqe_timer);
11280331766Sken
11281331766Sken		/* now wait for timer handler to complete (if necessary) */
11282331766Sken		while (hw->in_active_wqe_timer && iters) {
11283331766Sken			/*
11284331766Sken			 * if we happen to have just sent NOP mailbox command, make sure
11285331766Sken			 * completions are being processed
11286331766Sken			 */
11287331766Sken			ocs_hw_flush(hw);
11288331766Sken			iters--;
11289331766Sken		}
11290331766Sken
11291331766Sken		if (iters == 0) {
11292331766Sken			ocs_log_test(hw->os, "Failed to shutdown active wqe timer\n");
11293331766Sken		}
11294331766Sken	}
11295331766Sken}
11296331766Sken
11297331766Sken/**
11298331766Sken * @brief Determine if HW IO is owned by the port.
11299331766Sken *
11300331766Sken * @par Description
11301331766Sken * Determines if the given HW IO has been posted to the chip.
11302331766Sken *
11303331766Sken * @param hw Hardware context allocated by the caller.
11304331766Sken * @param io HW IO.
11305331766Sken *
11306331766Sken * @return Returns TRUE if given HW IO is port-owned.
11307331766Sken */
11308331766Skenuint8_t
11309331766Skenocs_hw_is_io_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
11310331766Sken{
11311331766Sken	/* Check to see if this is a port owned XRI */
11312331766Sken	return io->is_port_owned;
11313331766Sken}
11314331766Sken
11315331766Sken/**
11316331766Sken * @brief Return TRUE if exchange is port-owned.
11317331766Sken *
11318331766Sken * @par Description
11319331766Sken * Test to see if the xri is a port-owned xri.
11320331766Sken *
11321331766Sken * @param hw Hardware context.
11322331766Sken * @param xri Exchange indicator.
11323331766Sken *
11324331766Sken * @return Returns TRUE if XRI is a port owned XRI.
11325331766Sken */
11326331766Sken
11327331766Skenuint8_t
11328331766Skenocs_hw_is_xri_port_owned(ocs_hw_t *hw, uint32_t xri)
11329331766Sken{
11330331766Sken	ocs_hw_io_t *io = ocs_hw_io_lookup(hw, xri);
11331331766Sken	return (io == NULL ? FALSE : io->is_port_owned);
11332331766Sken}
11333331766Sken
11334331766Sken/**
11335331766Sken * @brief Returns an XRI from the port owned list to the host.
11336331766Sken *
11337331766Sken * @par Description
11338331766Sken * Used when the POST_XRI command fails as well as when the RELEASE_XRI completes.
11339331766Sken *
11340331766Sken * @param hw Hardware context.
11341331766Sken * @param xri_base The starting XRI number.
11342331766Sken * @param xri_count The number of XRIs to free from the base.
11343331766Sken */
11344331766Skenstatic void
11345331766Skenocs_hw_reclaim_xri(ocs_hw_t *hw, uint16_t xri_base, uint16_t xri_count)
11346331766Sken{
11347331766Sken	ocs_hw_io_t	*io;
11348331766Sken	uint32_t i;
11349331766Sken
11350331766Sken	for (i = 0; i < xri_count; i++) {
11351331766Sken		io = ocs_hw_io_lookup(hw, xri_base + i);
11352331766Sken
11353331766Sken		/*
11354331766Sken		 * if this is an auto xfer rdy XRI, then we need to release any
11355331766Sken		 * buffer attached to the XRI before moving the XRI back to the free pool.
11356331766Sken		 */
11357331766Sken		if (hw->auto_xfer_rdy_enabled) {
11358331766Sken			ocs_hw_rqpair_auto_xfer_rdy_move_to_host(hw, io);
11359331766Sken		}
11360331766Sken
11361331766Sken		ocs_lock(&hw->io_lock);
11362331766Sken			ocs_list_remove(&hw->io_port_owned, io);
11363331766Sken			io->is_port_owned = 0;
11364331766Sken			ocs_list_add_tail(&hw->io_free, io);
11365331766Sken		ocs_unlock(&hw->io_lock);
11366331766Sken	}
11367331766Sken}
11368331766Sken
11369331766Sken/**
11370331766Sken * @brief Called when the POST_XRI command completes.
11371331766Sken *
11372331766Sken * @par Description
11373331766Sken * Free the mailbox command buffer and reclaim the XRIs on failure.
11374331766Sken *
11375331766Sken * @param hw Hardware context.
11376331766Sken * @param status Status field from the mbox completion.
11377331766Sken * @param mqe Mailbox response structure.
11378331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
11379331766Sken *
11380331766Sken * @return Returns 0.
11381331766Sken */
11382331766Skenstatic int32_t
11383331766Skenocs_hw_cb_post_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11384331766Sken{
11385331766Sken	sli4_cmd_post_xri_t	*post_xri = (sli4_cmd_post_xri_t*)mqe;
11386331766Sken
11387331766Sken	/* Reclaim the XRIs as host owned if the command fails */
11388331766Sken	if (status != 0) {
11389331766Sken		ocs_log_debug(hw->os, "Status 0x%x for XRI base 0x%x, cnt =x%x\n",
11390331766Sken			      status, post_xri->xri_base, post_xri->xri_count);
11391331766Sken		ocs_hw_reclaim_xri(hw, post_xri->xri_base, post_xri->xri_count);
11392331766Sken	}
11393331766Sken
11394331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11395331766Sken	return 0;
11396331766Sken}
11397331766Sken
11398331766Sken/**
11399331766Sken * @brief Issues a mailbox command to move XRIs from the host-controlled pool to the port.
11400331766Sken *
11401331766Sken * @param hw Hardware context.
11402331766Sken * @param xri_start The starting XRI to post.
11403331766Sken * @param num_to_post The number of XRIs to post.
11404331766Sken *
11405331766Sken * @return Returns OCS_HW_RTN_NO_MEMORY, OCS_HW_RTN_ERROR, or OCS_HW_RTN_SUCCESS.
11406331766Sken */
11407331766Sken
11408331766Skenstatic ocs_hw_rtn_e
11409331766Skenocs_hw_post_xri(ocs_hw_t *hw, uint32_t xri_start, uint32_t num_to_post)
11410331766Sken{
11411331766Sken	uint8_t	*post_xri;
11412331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11413331766Sken
11414331766Sken	/* Since we need to allocate for mailbox queue, just always allocate */
11415331766Sken	post_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11416331766Sken	if (post_xri == NULL) {
11417331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
11418331766Sken		return OCS_HW_RTN_NO_MEMORY;
11419331766Sken	}
11420331766Sken
11421331766Sken	/* Register the XRIs */
11422331766Sken	if (sli_cmd_post_xri(&hw->sli, post_xri, SLI4_BMBX_SIZE,
11423331766Sken			     xri_start, num_to_post)) {
11424331766Sken		rc = ocs_hw_command(hw, post_xri, OCS_CMD_NOWAIT, ocs_hw_cb_post_xri, NULL);
11425331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
11426331766Sken			ocs_free(hw->os, post_xri, SLI4_BMBX_SIZE);
11427331766Sken			ocs_log_err(hw->os, "post_xri failed\n");
11428331766Sken		}
11429331766Sken	}
11430331766Sken	return rc;
11431331766Sken}
11432331766Sken
11433331766Sken/**
11434331766Sken * @brief Move XRIs from the host-controlled pool to the port.
11435331766Sken *
11436331766Sken * @par Description
11437331766Sken * Removes IOs from the free list and moves them to the port.
11438331766Sken *
11439331766Sken * @param hw Hardware context.
11440331766Sken * @param num_xri The number of XRIs being requested to move to the chip.
11441331766Sken *
11442331766Sken * @return Returns the number of XRIs that were moved.
11443331766Sken */
11444331766Sken
11445331766Skenuint32_t
11446331766Skenocs_hw_xri_move_to_port_owned(ocs_hw_t *hw, uint32_t num_xri)
11447331766Sken{
11448331766Sken	ocs_hw_io_t	*io;
11449331766Sken	uint32_t i;
11450331766Sken	uint32_t num_posted = 0;
11451331766Sken
11452331766Sken	/*
11453331766Sken	 * Note: We cannot use ocs_hw_io_alloc() because that would place the
11454331766Sken	 *       IO on the io_inuse list. We need to move from the io_free to
11455331766Sken	 *       the io_port_owned list.
11456331766Sken	 */
11457331766Sken	ocs_lock(&hw->io_lock);
11458331766Sken
11459331766Sken	for (i = 0; i < num_xri; i++) {
11460331766Sken
11461331766Sken		if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
11462331766Sken			ocs_hw_rtn_e rc;
11463331766Sken
11464331766Sken			/*
11465331766Sken			 * if this is an auto xfer rdy XRI, then we need to attach a
11466331766Sken			 * buffer to the XRI before submitting it to the chip. If a
11467331766Sken			 * buffer is unavailable, then we cannot post it, so return it
11468331766Sken			 * to the free pool.
11469331766Sken			 */
11470331766Sken			if (hw->auto_xfer_rdy_enabled) {
11471331766Sken				/* Note: uses the IO lock to get the auto xfer rdy buffer */
11472331766Sken				ocs_unlock(&hw->io_lock);
11473331766Sken				rc = ocs_hw_rqpair_auto_xfer_rdy_move_to_port(hw, io);
11474331766Sken				ocs_lock(&hw->io_lock);
11475331766Sken				if (rc != OCS_HW_RTN_SUCCESS) {
11476331766Sken					ocs_list_add_head(&hw->io_free, io);
11477331766Sken					break;
11478331766Sken				}
11479331766Sken			}
11480331766Sken			ocs_lock_init(hw->os, &io->axr_lock, "HW_axr_lock[%d]", io->indicator);
11481331766Sken			io->is_port_owned = 1;
11482331766Sken			ocs_list_add_tail(&hw->io_port_owned, io);
11483331766Sken
11484331766Sken			/* Post XRI */
11485331766Sken			if (ocs_hw_post_xri(hw, io->indicator, 1) != OCS_HW_RTN_SUCCESS ) {
11486331766Sken				ocs_hw_reclaim_xri(hw, io->indicator, i);
11487331766Sken				break;
11488331766Sken			}
11489331766Sken			num_posted++;
11490331766Sken		} else {
11491331766Sken			/* no more free XRIs */
11492331766Sken			break;
11493331766Sken		}
11494331766Sken	}
11495331766Sken	ocs_unlock(&hw->io_lock);
11496331766Sken
11497331766Sken	return num_posted;
11498331766Sken}
11499331766Sken
11500331766Sken/**
11501331766Sken * @brief Called when the RELEASE_XRI command completes.
11502331766Sken *
11503331766Sken * @par Description
11504331766Sken * Move the IOs back to the free pool on success.
11505331766Sken *
11506331766Sken * @param hw Hardware context.
11507331766Sken * @param status Status field from the mbox completion.
11508331766Sken * @param mqe Mailbox response structure.
11509331766Sken * @param arg Pointer to a callback function that signals the caller that the command is done.
11510331766Sken *
11511331766Sken * @return Returns 0.
11512331766Sken */
11513331766Skenstatic int32_t
11514331766Skenocs_hw_cb_release_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11515331766Sken{
11516331766Sken	sli4_cmd_release_xri_t	*release_xri = (sli4_cmd_release_xri_t*)mqe;
11517331766Sken	uint8_t i;
11518331766Sken
11519331766Sken	/* Reclaim the XRIs as host owned if the command fails */
11520331766Sken	if (status != 0) {
11521331766Sken		ocs_log_err(hw->os, "Status 0x%x\n", status);
11522331766Sken	} else {
11523331766Sken		for (i = 0; i < release_xri->released_xri_count; i++) {
11524331766Sken			uint16_t xri = ((i & 1) == 0 ? release_xri->xri_tbl[i/2].xri_tag0 :
11525331766Sken					release_xri->xri_tbl[i/2].xri_tag1);
11526331766Sken			ocs_hw_reclaim_xri(hw, xri, 1);
11527331766Sken		}
11528331766Sken	}
11529331766Sken
11530331766Sken	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11531331766Sken	return 0;
11532331766Sken}
11533331766Sken
11534331766Sken/**
11535331766Sken * @brief Move XRIs from the port-controlled pool to the host.
11536331766Sken *
11537331766Sken * Requests XRIs from the FW to return to the host-owned pool.
11538331766Sken *
11539331766Sken * @param hw Hardware context.
11540331766Sken * @param num_xri The number of XRIs being requested to moved from the chip.
11541331766Sken *
11542331766Sken * @return Returns 0 for success, or a negative error code value for failure.
11543331766Sken */
11544331766Sken
11545331766Skenocs_hw_rtn_e
11546331766Skenocs_hw_xri_move_to_host_owned(ocs_hw_t *hw, uint8_t num_xri)
11547331766Sken{
11548331766Sken	uint8_t	*release_xri;
11549331766Sken	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11550331766Sken
11551331766Sken	/* non-local buffer required for mailbox queue */
11552331766Sken	release_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11553331766Sken	if (release_xri == NULL) {
11554331766Sken		ocs_log_err(hw->os, "no buffer for command\n");
11555331766Sken		return OCS_HW_RTN_NO_MEMORY;
11556331766Sken	}
11557331766Sken
11558331766Sken	/* release the XRIs */
11559331766Sken	if (sli_cmd_release_xri(&hw->sli, release_xri, SLI4_BMBX_SIZE, num_xri)) {
11560331766Sken		rc = ocs_hw_command(hw, release_xri, OCS_CMD_NOWAIT, ocs_hw_cb_release_xri, NULL);
11561331766Sken		if (rc != OCS_HW_RTN_SUCCESS) {
11562331766Sken			ocs_log_err(hw->os, "release_xri failed\n");
11563331766Sken		}
11564331766Sken	}
11565331766Sken	/* If we are polling or an error occurred, then free the mailbox buffer */
11566331766Sken	if (release_xri != NULL && rc != OCS_HW_RTN_SUCCESS) {
11567331766Sken		ocs_free(hw->os, release_xri, SLI4_BMBX_SIZE);
11568331766Sken	}
11569331766Sken	return rc;
11570331766Sken}
11571331766Sken
11572331766Sken
11573331766Sken/**
11574331766Sken * @brief Allocate an ocs_hw_rx_buffer_t array.
11575331766Sken *
11576331766Sken * @par Description
11577331766Sken * An ocs_hw_rx_buffer_t array is allocated, along with the required DMA memory.
11578331766Sken *
11579331766Sken * @param hw Pointer to HW object.
11580331766Sken * @param rqindex RQ index for this buffer.
11581331766Sken * @param count Count of buffers in array.
11582331766Sken * @param size Size of buffer.
11583331766Sken *
11584331766Sken * @return Returns the pointer to the allocated ocs_hw_rq_buffer_t array.
11585331766Sken */
11586331766Skenstatic ocs_hw_rq_buffer_t *
11587331766Skenocs_hw_rx_buffer_alloc(ocs_hw_t *hw, uint32_t rqindex, uint32_t count, uint32_t size)
11588331766Sken{
11589331766Sken	ocs_t *ocs = hw->os;
11590331766Sken	ocs_hw_rq_buffer_t *rq_buf = NULL;
11591331766Sken	ocs_hw_rq_buffer_t *prq;
11592331766Sken	uint32_t i;
11593331766Sken
11594331766Sken	if (count != 0) {
11595331766Sken		rq_buf = ocs_malloc(hw->os, sizeof(*rq_buf) * count, OCS_M_NOWAIT | OCS_M_ZERO);
11596331766Sken		if (rq_buf == NULL) {
11597331766Sken			ocs_log_err(hw->os, "Failure to allocate unsolicited DMA trackers\n");
11598331766Sken			return NULL;
11599331766Sken		}
11600331766Sken
11601331766Sken		for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
11602331766Sken			prq->rqindex = rqindex;
11603331766Sken			if (ocs_dma_alloc(ocs, &prq->dma, size, OCS_MIN_DMA_ALIGNMENT)) {
11604331766Sken				ocs_log_err(hw->os, "DMA allocation failed\n");
11605331766Sken				ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11606331766Sken				rq_buf = NULL;
11607331766Sken				break;
11608331766Sken			}
11609331766Sken		}
11610331766Sken	}
11611331766Sken	return rq_buf;
11612331766Sken}
11613331766Sken
11614331766Sken/**
11615331766Sken * @brief Free an ocs_hw_rx_buffer_t array.
11616331766Sken *
11617331766Sken * @par Description
11618331766Sken * The ocs_hw_rx_buffer_t array is freed, along with allocated DMA memory.
11619331766Sken *
11620331766Sken * @param hw Pointer to HW object.
11621331766Sken * @param rq_buf Pointer to ocs_hw_rx_buffer_t array.
11622331766Sken * @param count Count of buffers in array.
11623331766Sken *
11624331766Sken * @return None.
11625331766Sken */
11626331766Skenstatic void
11627331766Skenocs_hw_rx_buffer_free(ocs_hw_t *hw, ocs_hw_rq_buffer_t *rq_buf, uint32_t count)
11628331766Sken{
11629331766Sken	ocs_t *ocs = hw->os;
11630331766Sken	uint32_t i;
11631331766Sken	ocs_hw_rq_buffer_t *prq;
11632331766Sken
11633331766Sken	if (rq_buf != NULL) {
11634331766Sken		for (i = 0, prq = rq_buf; i < count; i++, prq++) {
11635331766Sken			ocs_dma_free(ocs, &prq->dma);
11636331766Sken		}
11637331766Sken		ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11638331766Sken	}
11639331766Sken}
11640331766Sken
11641331766Sken/**
11642331766Sken * @brief Allocate the RQ data buffers.
11643331766Sken *
11644331766Sken * @param hw Pointer to HW object.
11645331766Sken *
11646331766Sken * @return Returns 0 on success, or a non-zero value on failure.
11647331766Sken */
11648331766Skenocs_hw_rtn_e
11649331766Skenocs_hw_rx_allocate(ocs_hw_t *hw)
11650331766Sken{
11651331766Sken	ocs_t *ocs = hw->os;
11652331766Sken	uint32_t i;
11653331766Sken	int32_t rc = OCS_HW_RTN_SUCCESS;
11654331766Sken	uint32_t rqindex = 0;
11655331766Sken	hw_rq_t *rq;
11656331766Sken	uint32_t hdr_size = OCS_HW_RQ_SIZE_HDR;
11657331766Sken	uint32_t payload_size = hw->config.rq_default_buffer_size;
11658331766Sken
11659331766Sken	rqindex = 0;
11660331766Sken
11661331766Sken	for (i = 0; i < hw->hw_rq_count; i++) {
11662331766Sken		rq = hw->hw_rq[i];
11663331766Sken
11664331766Sken		/* Allocate header buffers */
11665331766Sken		rq->hdr_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, hdr_size);
11666331766Sken		if (rq->hdr_buf == NULL) {
11667331766Sken			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc hdr_buf failed\n");
11668331766Sken			rc = OCS_HW_RTN_ERROR;
11669331766Sken			break;
11670331766Sken		}
11671331766Sken
11672331766Sken		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d header  %4d by %4d bytes\n", i, rq->hdr->id,
11673331766Sken			      rq->entry_count, hdr_size);
11674331766Sken
11675331766Sken		rqindex++;
11676331766Sken
11677331766Sken		/* Allocate payload buffers */
11678331766Sken		rq->payload_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, payload_size);
11679331766Sken		if (rq->payload_buf == NULL) {
11680331766Sken			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc fb_buf failed\n");
11681331766Sken			rc = OCS_HW_RTN_ERROR;
11682331766Sken			break;
11683331766Sken		}
11684331766Sken		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d default %4d by %4d bytes\n", i, rq->data->id,
11685331766Sken			      rq->entry_count, payload_size);
11686331766Sken		rqindex++;
11687331766Sken	}
11688331766Sken
11689331766Sken	return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
11690331766Sken}
11691331766Sken
11692331766Sken/**
11693331766Sken * @brief Post the RQ data buffers to the chip.
11694331766Sken *
11695331766Sken * @param hw Pointer to HW object.
11696331766Sken *
11697331766Sken * @return Returns 0 on success, or a non-zero value on failure.
11698331766Sken */
11699331766Skenocs_hw_rtn_e
11700331766Skenocs_hw_rx_post(ocs_hw_t *hw)
11701331766Sken{
11702331766Sken	uint32_t i;
11703331766Sken	uint32_t idx;
11704331766Sken	uint32_t rq_idx;
11705331766Sken	int32_t rc = 0;
11706331766Sken
11707331766Sken	/*
11708331766Sken	 * In RQ pair mode, we MUST post the header and payload buffer at the
11709331766Sken	 * same time.
11710331766Sken	 */
11711331766Sken	for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
11712331766Sken		hw_rq_t *rq = hw->hw_rq[rq_idx];
11713331766Sken
11714331766Sken		for (i = 0; i < rq->entry_count-1; i++) {
11715331766Sken			ocs_hw_sequence_t *seq = ocs_array_get(hw->seq_pool, idx++);
11716331766Sken			ocs_hw_assert(seq != NULL);
11717331766Sken
11718331766Sken			seq->header = &rq->hdr_buf[i];
11719331766Sken
11720331766Sken			seq->payload = &rq->payload_buf[i];
11721331766Sken
11722331766Sken			rc = ocs_hw_sequence_free(hw, seq);
11723331766Sken			if (rc) {
11724331766Sken				break;
11725331766Sken			}
11726331766Sken		}
11727331766Sken		if (rc) {
11728331766Sken			break;
11729331766Sken		}
11730331766Sken	}
11731331766Sken
11732331766Sken	return rc;
11733331766Sken}
11734331766Sken
11735331766Sken/**
11736331766Sken * @brief Free the RQ data buffers.
11737331766Sken *
11738331766Sken * @param hw Pointer to HW object.
11739331766Sken *
11740331766Sken */
11741331766Skenvoid
11742331766Skenocs_hw_rx_free(ocs_hw_t *hw)
11743331766Sken{
11744331766Sken	hw_rq_t *rq;
11745331766Sken	uint32_t i;
11746331766Sken
11747331766Sken	/* Free hw_rq buffers */
11748331766Sken	for (i = 0; i < hw->hw_rq_count; i++) {
11749331766Sken		rq = hw->hw_rq[i];
11750331766Sken		if (rq != NULL) {
11751331766Sken			ocs_hw_rx_buffer_free(hw, rq->hdr_buf, rq->entry_count);
11752331766Sken			rq->hdr_buf = NULL;
11753331766Sken			ocs_hw_rx_buffer_free(hw, rq->payload_buf, rq->entry_count);
11754331766Sken			rq->payload_buf = NULL;
11755331766Sken		}
11756331766Sken	}
11757331766Sken}
11758331766Sken
11759331766Sken/**
11760331766Sken * @brief HW async call context structure.
11761331766Sken */
11762331766Skentypedef struct {
11763331766Sken	ocs_hw_async_cb_t callback;
11764331766Sken	void *arg;
11765331766Sken	uint8_t cmd[SLI4_BMBX_SIZE];
11766331766Sken} ocs_hw_async_call_ctx_t;
11767331766Sken
11768331766Sken/**
11769331766Sken * @brief HW async callback handler
11770331766Sken *
11771331766Sken * @par Description
11772331766Sken * This function is called when the NOP mailbox command completes.  The callback stored
11773331766Sken * in the requesting context is invoked.
11774331766Sken *
11775331766Sken * @param hw Pointer to HW object.
11776331766Sken * @param status Completion status.
11777331766Sken * @param mqe Pointer to mailbox completion queue entry.
11778331766Sken * @param arg Caller-provided argument.
11779331766Sken *
11780331766Sken * @return None.
11781331766Sken */
11782331766Skenstatic void
11783331766Skenocs_hw_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11784331766Sken{
11785331766Sken	ocs_hw_async_call_ctx_t *ctx = arg;
11786331766Sken
11787331766Sken	if (ctx != NULL) {
11788331766Sken		if (ctx->callback != NULL) {
11789331766Sken			(*ctx->callback)(hw, status, mqe, ctx->arg);
11790331766Sken		}
11791331766Sken		ocs_free(hw->os, ctx, sizeof(*ctx));
11792331766Sken	}
11793331766Sken}
11794331766Sken
11795331766Sken/**
11796331766Sken * @brief Make an async callback using NOP mailbox command
11797331766Sken *
11798331766Sken * @par Description
11799331766Sken * Post a NOP mailbox command; the callback with argument is invoked upon completion
11800331766Sken * while in the event processing context.
11801331766Sken *
11802331766Sken * @param hw Pointer to HW object.
11803331766Sken * @param callback Pointer to callback function.
11804331766Sken * @param arg Caller-provided callback.
11805331766Sken *
11806331766Sken * @return Returns 0 on success, or a negative error code value on failure.
11807331766Sken */
11808331766Skenint32_t
11809331766Skenocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
11810331766Sken{
11811331766Sken	int32_t rc = 0;
11812331766Sken	ocs_hw_async_call_ctx_t *ctx;
11813331766Sken
11814331766Sken	/*
11815331766Sken	 * Allocate a callback context (which includes the mailbox command buffer), we need
11816331766Sken	 * this to be persistent as the mailbox command submission may be queued and executed later
11817331766Sken	 * execution.
11818331766Sken	 */
11819331766Sken	ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
11820331766Sken	if (ctx == NULL) {
11821331766Sken		ocs_log_err(hw->os, "failed to malloc async call context\n");
11822331766Sken		return OCS_HW_RTN_NO_MEMORY;
11823331766Sken	}
11824331766Sken	ctx->callback = callback;
11825331766Sken	ctx->arg = arg;
11826331766Sken
11827331766Sken	/* Build and send a NOP mailbox command */
11828331766Sken	if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
11829331766Sken		ocs_log_err(hw->os, "COMMON_NOP format failure\n");
11830331766Sken		ocs_free(hw->os, ctx, sizeof(*ctx));
11831331766Sken		rc = -1;
11832331766Sken	}
11833331766Sken
11834331766Sken	if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
11835331766Sken		ocs_log_err(hw->os, "COMMON_NOP command failure\n");
11836331766Sken		ocs_free(hw->os, ctx, sizeof(*ctx));
11837331766Sken		rc = -1;
11838331766Sken	}
11839331766Sken	return rc;
11840331766Sken}
11841331766Sken
11842331766Sken/**
11843331766Sken * @brief Initialize the reqtag pool.
11844331766Sken *
11845331766Sken * @par Description
11846331766Sken * The WQ request tag pool is initialized.
11847331766Sken *
11848331766Sken * @param hw Pointer to HW object.
11849331766Sken *
11850331766Sken * @return Returns 0 on success, or a negative error code value on failure.
11851331766Sken */
11852331766Skenocs_hw_rtn_e
11853331766Skenocs_hw_reqtag_init(ocs_hw_t *hw)
11854331766Sken{
11855331766Sken	if (hw->wq_reqtag_pool == NULL) {
11856331766Sken		hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
11857331766Sken		if (hw->wq_reqtag_pool == NULL) {
11858331766Sken			ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
11859331766Sken			return OCS_HW_RTN_NO_MEMORY;
11860331766Sken		}
11861331766Sken	}
11862331766Sken	ocs_hw_reqtag_reset(hw);
11863331766Sken	return OCS_HW_RTN_SUCCESS;
11864331766Sken}
11865331766Sken
11866331766Sken/**
11867331766Sken * @brief Allocate a WQ request tag.
11868331766Sken *
11869331766Sken * Allocate and populate a WQ request tag from the WQ request tag pool.
11870331766Sken *
11871331766Sken * @param hw Pointer to HW object.
11872331766Sken * @param callback Callback function.
11873331766Sken * @param arg Pointer to callback argument.
11874331766Sken *
11875331766Sken * @return Returns pointer to allocated WQ request tag, or NULL if object cannot be allocated.
11876331766Sken */
11877331766Skenhw_wq_callback_t *
11878331766Skenocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
11879331766Sken{
11880331766Sken	hw_wq_callback_t *wqcb;
11881331766Sken
11882331766Sken	ocs_hw_assert(callback != NULL);
11883331766Sken
11884331766Sken	wqcb = ocs_pool_get(hw->wq_reqtag_pool);
11885331766Sken	if (wqcb != NULL) {
11886331766Sken		ocs_hw_assert(wqcb->callback == NULL);
11887331766Sken		wqcb->callback = callback;
11888331766Sken		wqcb->arg = arg;
11889331766Sken	}
11890331766Sken	return wqcb;
11891331766Sken}
11892331766Sken
11893331766Sken/**
11894331766Sken * @brief Free a WQ request tag.
11895331766Sken *
11896331766Sken * Free the passed in WQ request tag.
11897331766Sken *
11898331766Sken * @param hw Pointer to HW object.
11899331766Sken * @param wqcb Pointer to WQ request tag object to free.
11900331766Sken *
11901331766Sken * @return None.
11902331766Sken */
11903331766Skenvoid
11904331766Skenocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
11905331766Sken{
11906331766Sken	ocs_hw_assert(wqcb->callback != NULL);
11907331766Sken	wqcb->callback = NULL;
11908331766Sken	wqcb->arg = NULL;
11909331766Sken	ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11910331766Sken}
11911331766Sken
11912331766Sken/**
11913331766Sken * @brief Return WQ request tag by index.
11914331766Sken *
11915331766Sken * @par Description
11916331766Sken * Return pointer to WQ request tag object given an index.
11917331766Sken *
11918331766Sken * @param hw Pointer to HW object.
11919331766Sken * @param instance_index Index of WQ request tag to return.
11920331766Sken *
11921331766Sken * @return Pointer to WQ request tag, or NULL.
11922331766Sken */
11923331766Skenhw_wq_callback_t *
11924331766Skenocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
11925331766Sken{
11926331766Sken	hw_wq_callback_t *wqcb;
11927331766Sken
11928331766Sken	wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
11929331766Sken	if (wqcb == NULL) {
11930331766Sken		ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
11931331766Sken	}
11932331766Sken	return wqcb;
11933331766Sken}
11934331766Sken
11935331766Sken/**
11936331766Sken * @brief Reset the WQ request tag pool.
11937331766Sken *
11938331766Sken * @par Description
11939331766Sken * Reset the WQ request tag pool, returning all to the free list.
11940331766Sken *
11941331766Sken * @param hw pointer to HW object.
11942331766Sken *
11943331766Sken * @return None.
11944331766Sken */
11945331766Skenvoid
11946331766Skenocs_hw_reqtag_reset(ocs_hw_t *hw)
11947331766Sken{
11948331766Sken	hw_wq_callback_t *wqcb;
11949331766Sken	uint32_t i;
11950331766Sken
11951331766Sken	/* Remove all from freelist */
11952331766Sken	while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
11953331766Sken		;
11954331766Sken	}
11955331766Sken
11956331766Sken	/* Put them all back */
11957331766Sken	for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
11958331766Sken		wqcb->instance_index = i;
11959331766Sken		wqcb->callback = NULL;
11960331766Sken		wqcb->arg = NULL;
11961331766Sken		ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11962331766Sken	}
11963331766Sken}
11964331766Sken
11965331766Sken/**
11966331766Sken * @brief Handle HW assertion
11967331766Sken *
11968331766Sken * HW assert, display diagnostic message, and abort.
11969331766Sken *
11970331766Sken * @param cond string describing failing assertion condition
11971331766Sken * @param filename file name
11972331766Sken * @param linenum line number
11973331766Sken *
11974331766Sken * @return none
11975331766Sken */
11976331766Skenvoid
11977331766Sken_ocs_hw_assert(const char *cond, const char *filename, int linenum)
11978331766Sken{
11979331766Sken	ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
11980331766Sken	ocs_abort();
11981331766Sken		/* no return */
11982331766Sken}
11983331766Sken
11984331766Sken/**
11985331766Sken * @brief Handle HW verify
11986331766Sken *
11987331766Sken * HW verify, display diagnostic message, dump stack and return.
11988331766Sken *
11989331766Sken * @param cond string describing failing verify condition
11990331766Sken * @param filename file name
11991331766Sken * @param linenum line number
11992331766Sken *
11993331766Sken * @return none
11994331766Sken */
11995331766Skenvoid
11996331766Sken_ocs_hw_verify(const char *cond, const char *filename, int linenum)
11997331766Sken{
11998331766Sken	ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
11999331766Sken	ocs_print_stack();
12000331766Sken}
12001331766Sken
12002331766Sken/**
12003331766Sken * @brief Reque XRI
12004331766Sken *
12005331766Sken * @par Description
12006331766Sken * Reque XRI
12007331766Sken *
12008331766Sken * @param hw Pointer to HW object.
12009331766Sken * @param io Pointer to HW IO
12010331766Sken *
12011331766Sken * @return Return 0 if successful else returns -1
12012331766Sken */
12013331766Skenint32_t
12014331766Skenocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
12015331766Sken{
12016331766Sken	int32_t rc = 0;
12017331766Sken
12018331766Sken	rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
12019331766Sken	if (rc) {
12020331766Sken		ocs_list_add_tail(&hw->io_port_dnrx, io);
12021331766Sken		rc = -1;
12022331766Sken		goto exit_ocs_hw_reque_xri;
12023331766Sken	}
12024331766Sken
12025331766Sken	io->auto_xfer_rdy_dnrx = 0;
12026331766Sken	io->type = OCS_HW_IO_DNRX_REQUEUE;
12027331766Sken	if (sli_requeue_xri_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->indicator, OCS_HW_REQUE_XRI_REGTAG, SLI4_CQ_DEFAULT)) {
12028331766Sken		/* Clear buffer from XRI */
12029331766Sken		ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
12030331766Sken		io->axr_buf = NULL;
12031331766Sken
12032331766Sken		ocs_log_err(hw->os, "requeue_xri WQE error\n");
12033331766Sken		ocs_list_add_tail(&hw->io_port_dnrx, io);
12034331766Sken
12035331766Sken		rc = -1;
12036331766Sken		goto exit_ocs_hw_reque_xri;
12037331766Sken	}
12038331766Sken
12039331766Sken	if (io->wq == NULL) {
12040331766Sken		io->wq = ocs_hw_queue_next_wq(hw, io);
12041331766Sken		ocs_hw_assert(io->wq != NULL);
12042331766Sken	}
12043331766Sken
12044331766Sken	/*
12045331766Sken	 * Add IO to active io wqe list before submitting, in case the
12046331766Sken	 * wcqe processing preempts this thread.
12047331766Sken	 */
12048331766Sken	OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
12049331766Sken	OCS_STAT(io->wq->use_count++);
12050331766Sken
12051331766Sken	rc = hw_wq_write(io->wq, &io->wqe);
12052331766Sken	if (rc < 0) {
12053331766Sken		ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
12054331766Sken		rc = -1;
12055331766Sken	}
12056331766Sken
12057331766Skenexit_ocs_hw_reque_xri:
12058331766Sken	return 0;
12059331766Sken}
12060331766Sken
12061331766Skenuint32_t
12062331766Skenocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
12063331766Sken{
12064331766Sken	sli4_t *sli4 = &ocs->hw.sli;
12065331766Sken	ocs_dma_t       dma;
12066331766Sken	uint8_t		*payload = NULL;
12067331766Sken
12068331766Sken	int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
12069331766Sken
12070331766Sken	/* allocate memory for the service parameters */
12071331766Sken	if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
12072331766Sken		ocs_log_err(ocs, "Failed to allocate DMA memory\n");
12073331766Sken		return 1;
12074331766Sken	}
12075331766Sken
12076331766Sken	if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
12077331766Sken				&dma, indicator)) {
12078331766Sken		ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
12079331766Sken		ocs_dma_free(ocs, &dma);
12080331766Sken		return 1;
12081331766Sken	}
12082331766Sken
12083331766Sken	if (sli_bmbx_command(sli4)) {
12084331766Sken		ocs_log_err(ocs, "READ_SPARM64 command failure\n");
12085331766Sken		ocs_dma_free(ocs, &dma);
12086331766Sken		return 1;
12087331766Sken	}
12088331766Sken
12089331766Sken	payload = dma.virt;
12090331766Sken	ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
12091331766Sken	ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
12092331766Sken	ocs_dma_free(ocs, &dma);
12093331766Sken	return 0;
12094331766Sken}
12095331766Sken
12096331766Sken/**
12097331766Sken * @page fc_hw_api_overview HW APIs
12098331766Sken * - @ref devInitShutdown
12099331766Sken * - @ref domain
12100331766Sken * - @ref port
12101331766Sken * - @ref node
12102331766Sken * - @ref io
12103331766Sken * - @ref interrupt
12104331766Sken *
12105331766Sken * <div class="overview">
12106331766Sken * The Hardware Abstraction Layer (HW) insulates the higher-level code from the SLI-4
12107331766Sken * message details, but the higher level code must still manage domains, ports,
12108331766Sken * IT nexuses, and IOs. The HW API is designed to help the higher level manage
12109331766Sken * these objects.<br><br>
12110331766Sken *
12111331766Sken * The HW uses function callbacks to notify the higher-level code of events
12112331766Sken * that are received from the chip. There are currently three types of
12113331766Sken * functions that may be registered:
12114331766Sken *
12115331766Sken * <ul><li>domain ��� This function is called whenever a domain event is generated
12116331766Sken * within the HW. Examples include a new FCF is discovered, a connection
12117331766Sken * to a domain is disrupted, and allocation callbacks.</li>
12118331766Sken * <li>unsolicited ��� This function is called whenever new data is received in
12119331766Sken * the SLI-4 receive queue.</li>
12120331766Sken * <li>rnode ��� This function is called for remote node events, such as attach status
12121331766Sken * and  allocation callbacks.</li></ul>
12122331766Sken *
12123331766Sken * Upper layer functions may be registered by using the ocs_hw_callback() function.
12124331766Sken *
12125331766Sken * <img src="elx_fc_hw.jpg" alt="FC/FCoE HW" title="FC/FCoE HW" align="right"/>
12126331766Sken * <h2>FC/FCoE HW API</h2>
12127331766Sken * The FC/FCoE HW component builds upon the SLI-4 component to establish a flexible
12128331766Sken * interface for creating the necessary common objects and sending I/Os. It may be used
12129331766Sken * ���as is��� in customer implementations or it can serve as an example of typical interactions
12130331766Sken * between a driver and the SLI-4 hardware. The broad categories of functionality include:
12131331766Sken *
12132331766Sken * <ul><li>Setting-up and tearing-down of the HW.</li>
12133331766Sken * <li>Allocating and using the common objects (SLI Port, domain, remote node).</li>
12134331766Sken * <li>Sending and receiving I/Os.</li></ul>
12135331766Sken *
12136331766Sken * <h3>HW Setup</h3>
12137331766Sken * To set up the HW:
12138331766Sken *
12139331766Sken * <ol>
12140331766Sken * <li>Set up the HW object using ocs_hw_setup().<br>
12141331766Sken * This step performs a basic configuration of the SLI-4 component and the HW to
12142331766Sken * enable querying the hardware for its capabilities. At this stage, the HW is not
12143331766Sken * capable of general operations (such as, receiving events or sending I/Os).</li><br><br>
12144331766Sken * <li>Configure the HW according to the driver requirements.<br>
12145331766Sken * The HW provides functions to discover hardware capabilities (ocs_hw_get()), as
12146331766Sken * well as configures the amount of resources required (ocs_hw_set()). The driver
12147331766Sken * must also register callback functions (ocs_hw_callback()) to receive notification of
12148331766Sken * various asynchronous events.<br><br>
12149331766Sken * @b Note: Once configured, the driver must initialize the HW (ocs_hw_init()). This
12150331766Sken * step creates the underlying queues, commits resources to the hardware, and
12151331766Sken * prepares the hardware for operation. While the hardware is operational, the
12152331766Sken * port is not online, and cannot send or receive data.</li><br><br>
12153331766Sken * <br><br>
12154331766Sken * <li>Finally, the driver can bring the port online (ocs_hw_port_control()).<br>
12155331766Sken * When the link comes up, the HW determines if a domain is present and notifies the
12156331766Sken * driver using the domain callback function. This is the starting point of the driver's
12157331766Sken * interaction with the common objects.<br><br>
12158331766Sken * @b Note: For FCoE, there may be more than one domain available and, therefore,
12159331766Sken * more than one callback.</li>
12160331766Sken * </ol>
12161331766Sken *
12162331766Sken * <h3>Allocating and Using Common Objects</h3>
12163331766Sken * Common objects provide a mechanism through which the various OneCore Storage
12164331766Sken * driver components share and track information. These data structures are primarily
12165331766Sken * used to track SLI component information but can be extended by other components, if
12166331766Sken * needed. The main objects are:
12167331766Sken *
12168331766Sken * <ul><li>DMA ��� the ocs_dma_t object describes a memory region suitable for direct
12169331766Sken * memory access (DMA) transactions.</li>
12170331766Sken * <li>SCSI domain ��� the ocs_domain_t object represents the SCSI domain, including
12171331766Sken * any infrastructure devices such as FC switches and FC forwarders. The domain
12172331766Sken * object contains both an FCFI and a VFI.</li>
12173331766Sken * <li>SLI Port (sport) ��� the ocs_sli_port_t object represents the connection between
12174331766Sken * the driver and the SCSI domain. The SLI Port object contains a VPI.</li>
12175331766Sken * <li>Remote node ��� the ocs_remote_node_t represents a connection between the SLI
12176331766Sken * Port and another device in the SCSI domain. The node object contains an RPI.</li></ul>
12177331766Sken *
12178331766Sken * Before the driver can send I/Os, it must allocate the SCSI domain, SLI Port, and remote
12179331766Sken * node common objects and establish the connections between them. The goal is to
12180331766Sken * connect the driver to the SCSI domain to exchange I/Os with other devices. These
12181331766Sken * common object connections are shown in the following figure, FC Driver Common Objects:
12182331766Sken * <img src="elx_fc_common_objects.jpg"
12183331766Sken * alt="FC Driver Common Objects" title="FC Driver Common Objects" align="center"/>
12184331766Sken *
12185331766Sken * The first step is to create a connection to the domain by allocating an SLI Port object.
12186331766Sken * The SLI Port object represents a particular FC ID and must be initialized with one. With
12187331766Sken * the SLI Port object, the driver can discover the available SCSI domain(s). On identifying
12188331766Sken * a domain, the driver allocates a domain object and attaches to it using the previous SLI
12189331766Sken * port object.<br><br>
12190331766Sken *
12191331766Sken * @b Note: In some cases, the driver may need to negotiate service parameters (that is,
12192331766Sken * FLOGI) with the domain before attaching.<br><br>
12193331766Sken *
12194331766Sken * Once attached to the domain, the driver can discover and attach to other devices
12195331766Sken * (remote nodes). The exact discovery method depends on the driver, but it typically
12196331766Sken * includes using a position map, querying the fabric name server, or an out-of-band
12197331766Sken * method. In most cases, it is necessary to log in with devices before performing I/Os.
12198331766Sken * Prior to sending login-related ELS commands (ocs_hw_srrs_send()), the driver must
12199331766Sken * allocate a remote node object (ocs_hw_node_alloc()). If the login negotiation is
12200331766Sken * successful, the driver must attach the nodes (ocs_hw_node_attach()) to the SLI Port
12201331766Sken * before exchanging FCP I/O.<br><br>
12202331766Sken *
12203331766Sken * @b Note: The HW manages both the well known fabric address and the name server as
12204331766Sken * nodes in the domain. Therefore, the driver must allocate node objects prior to
12205331766Sken * communicating with either of these entities.
12206331766Sken *
12207331766Sken * <h3>Sending and Receiving I/Os</h3>
12208331766Sken * The HW provides separate interfaces for sending BLS/ ELS/ FC-CT and FCP, but the
12209331766Sken * commands are conceptually similar. Since the commands complete asynchronously,
12210331766Sken * the caller must provide a HW I/O object that maintains the I/O state, as well as
12211331766Sken * provide a callback function. The driver may use the same callback function for all I/O
12212331766Sken * operations, but each operation must use a unique HW I/O object. In the SLI-4
12213331766Sken * architecture, there is a direct association between the HW I/O object and the SGL used
12214331766Sken * to describe the data. Therefore, a driver typically performs the following operations:
12215331766Sken *
12216331766Sken * <ul><li>Allocates a HW I/O object (ocs_hw_io_alloc()).</li>
12217331766Sken * <li>Formats the SGL, specifying both the HW I/O object and the SGL.
12218331766Sken * (ocs_hw_io_init_sges() and ocs_hw_io_add_sge()).</li>
12219331766Sken * <li>Sends the HW I/O (ocs_hw_io_send()).</li></ul>
12220331766Sken *
12221331766Sken * <h3>HW Tear Down</h3>
12222331766Sken * To tear-down the HW:
12223331766Sken *
12224331766Sken * <ol><li>Take the port offline (ocs_hw_port_control()) to prevent receiving further
12225331766Sken * data andevents.</li>
12226331766Sken * <li>Destroy the HW object (ocs_hw_teardown()).</li>
12227331766Sken * <li>Free any memory used by the HW, such as buffers for unsolicited data.</li></ol>
12228331766Sken * <br>
12229331766Sken * </div><!-- overview -->
12230331766Sken *
12231331766Sken */
12232331766Sken
12233331766Sken
12234331766Sken
12235331766Sken
12236331766Sken/**
12237331766Sken * This contains all hw runtime workaround code.  Based on the asic type,
12238331766Sken * asic revision, and range of fw revisions, a particular workaround may be enabled.
12239331766Sken *
12240331766Sken * A workaround may consist of overriding a particular HW/SLI4 value that was initialized
12241331766Sken * during ocs_hw_setup() (for example the MAX_QUEUE overrides for mis-reported queue
12242331766Sken * sizes). Or if required, elements of the ocs_hw_workaround_t structure may be set to
12243331766Sken * control specific runtime behavior.
12244331766Sken *
12245331766Sken * It is intended that the controls in ocs_hw_workaround_t be defined functionally.  So we
12246331766Sken * would have the driver look like:  "if (hw->workaround.enable_xxx) then ...", rather than
12247331766Sken * what we might previously see as "if this is a BE3, then do xxx"
12248331766Sken *
12249331766Sken */
12250331766Sken
12251331766Sken
12252331766Sken#define HW_FWREV_ZERO		(0ull)
12253331766Sken#define HW_FWREV_MAX		(~0ull)
12254331766Sken
12255331766Sken#define SLI4_ASIC_TYPE_ANY	0
12256331766Sken#define SLI4_ASIC_REV_ANY	0
12257331766Sken
12258331766Sken/**
12259331766Sken * @brief Internal definition of workarounds
12260331766Sken */
12261331766Sken
12262331766Skentypedef enum {
12263331766Sken	HW_WORKAROUND_TEST = 1,
12264331766Sken	HW_WORKAROUND_MAX_QUEUE,	/**< Limits all queues */
12265331766Sken	HW_WORKAROUND_MAX_RQ,		/**< Limits only the RQ */
12266331766Sken	HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
12267331766Sken	HW_WORKAROUND_WQE_COUNT_METHOD,
12268331766Sken	HW_WORKAROUND_RQE_COUNT_METHOD,
12269331766Sken	HW_WORKAROUND_USE_UNREGISTERD_RPI,
12270331766Sken	HW_WORKAROUND_DISABLE_AR_TGT_DIF, /**< Disable of auto-response target DIF */
12271331766Sken	HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
12272331766Sken	HW_WORKAROUND_USE_DIF_QUARANTINE,
12273331766Sken	HW_WORKAROUND_USE_DIF_SEC_XRI,		/**< Use secondary xri for multiple data phases */
12274331766Sken	HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB,	/**< FCFI reported in SRB not correct, use "first" registered domain */
12275331766Sken	HW_WORKAROUND_FW_VERSION_TOO_LOW,	/**< The FW version is not the min version supported by this driver */
12276331766Sken	HW_WORKAROUND_SGLC_MISREPORTED,	/**< Chip supports SGL Chaining but SGLC is not set in SLI4_PARAMS */
12277331766Sken	HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE,	/**< Don't use SEND_FRAME capable if FW version is too old */
12278331766Sken} hw_workaround_e;
12279331766Sken
12280331766Sken/**
12281331766Sken * @brief Internal workaround structure instance
12282331766Sken */
12283331766Sken
12284331766Skentypedef struct {
12285331766Sken	sli4_asic_type_e asic_type;
12286331766Sken	sli4_asic_rev_e asic_rev;
12287331766Sken	uint64_t fwrev_low;
12288331766Sken	uint64_t fwrev_high;
12289331766Sken
12290331766Sken	hw_workaround_e workaround;
12291331766Sken	uint32_t value;
12292331766Sken} hw_workaround_t;
12293331766Sken
12294331766Skenstatic hw_workaround_t hw_workarounds[] = {
12295331766Sken	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12296331766Sken		HW_WORKAROUND_TEST, 999},
12297331766Sken
12298331766Sken	/* Bug: 127585: if_type == 2 returns 0 for total length placed on
12299331766Sken	 * FCP_TSEND64_WQE completions.   Note, original driver code enables this
12300331766Sken	 * workaround for all asic types
12301331766Sken	 */
12302331766Sken	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12303331766Sken		HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
12304331766Sken
12305331766Sken	/* Bug: unknown, Lancer A0 has mis-reported max queue depth */
12306331766Sken	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
12307331766Sken		HW_WORKAROUND_MAX_QUEUE, 2048},
12308331766Sken
12309331766Sken	/* Bug: 143399, BE3 has mis-reported max RQ queue depth */
12310331766Sken	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
12311331766Sken		HW_WORKAROUND_MAX_RQ, 2048},
12312331766Sken
12313331766Sken	/* Bug: 143399, skyhawk has mis-reported max RQ queue depth */
12314331766Sken	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
12315331766Sken		HW_WORKAROUND_MAX_RQ, 2048},
12316331766Sken
12317331766Sken	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported WQE count method */
12318331766Sken	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12319331766Sken		HW_WORKAROUND_WQE_COUNT_METHOD, 1},
12320331766Sken
12321331766Sken	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported RQE count method */
12322331766Sken	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12323331766Sken		HW_WORKAROUND_RQE_COUNT_METHOD, 1},
12324331766Sken
12325331766Sken	/* Bug: 142968, BE3 UE with RPI == 0xffff */
12326331766Sken	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12327331766Sken		HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
12328331766Sken
12329331766Sken	/* Bug: unknown, Skyhawk won't support auto-response on target T10-PI  */
12330331766Sken	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12331331766Sken		HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
12332331766Sken
12333331766Sken	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
12334331766Sken		HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
12335331766Sken
12336331766Sken	/* Bug: 160124, Skyhawk quarantine DIF XRIs  */
12337331766Sken	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12338331766Sken		HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
12339331766Sken
12340331766Sken	/* Bug: 161832, Skyhawk use secondary XRI for multiple data phase TRECV */
12341331766Sken	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12342331766Sken		HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
12343331766Sken
12344331766Sken	/* Bug: xxxxxx, FCFI reported in SRB not corrrect */
12345331766Sken	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12346331766Sken		HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
12347331766Sken#if 0
12348331766Sken	/* Bug: 165642, FW version check for driver */
12349331766Sken	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
12350331766Sken		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12351331766Sken#endif
12352331766Sken	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
12353331766Sken		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12354331766Sken
12355331766Sken	/* Bug 177061, Lancer FW does not set the SGLC bit */
12356331766Sken	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12357331766Sken		HW_WORKAROUND_SGLC_MISREPORTED, 0},
12358331766Sken
12359331766Sken	/* BZ 181208/183914, enable this workaround for ALL revisions */
12360331766Sken	{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12361331766Sken		HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
12362331766Sken};
12363331766Sken
12364331766Sken/**
12365331766Sken * @brief Function prototypes
12366331766Sken */
12367331766Sken
12368331766Skenstatic int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
12369331766Sken
12370331766Sken/**
12371331766Sken * @brief Parse the firmware version (name)
12372331766Sken *
12373331766Sken * Parse a string of the form a.b.c.d, returning a uint64_t packed as defined
12374331766Sken * by the HW_FWREV() macro
12375331766Sken *
12376331766Sken * @param fwrev_string pointer to the firmware string
12377331766Sken *
12378331766Sken * @return packed firmware revision value
12379331766Sken */
12380331766Sken
12381331766Skenstatic uint64_t
12382331766Skenparse_fw_version(const char *fwrev_string)
12383331766Sken{
12384331766Sken	int v[4] = {0};
12385331766Sken	const char *p;
12386331766Sken	int i;
12387331766Sken
12388331766Sken	for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
12389331766Sken		v[i] = ocs_strtoul(p, 0, 0);
12390331766Sken		while(*p && *p != '.') {
12391331766Sken			p ++;
12392331766Sken		}
12393331766Sken		if (*p) {
12394331766Sken			p ++;
12395331766Sken		}
12396331766Sken	}
12397331766Sken
12398331766Sken	/* Special case for bootleg releases with f/w rev 0.0.9999.0, set to max value */
12399331766Sken	if (v[2] == 9999) {
12400331766Sken		return HW_FWREV_MAX;
12401331766Sken	} else {
12402331766Sken		return HW_FWREV(v[0], v[1], v[2], v[3]);
12403331766Sken	}
12404331766Sken}
12405331766Sken
12406331766Sken/**
12407331766Sken * @brief Test for a workaround match
12408331766Sken *
12409331766Sken * Looks at the asic type, asic revision, and fw revision, and returns TRUE if match.
12410331766Sken *
12411331766Sken * @param hw Pointer to the HW structure
12412331766Sken * @param w Pointer to a workaround structure entry
12413331766Sken *
12414331766Sken * @return Return TRUE for a match
12415331766Sken */
12416331766Sken
12417331766Skenstatic int32_t
12418331766Skenocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
12419331766Sken{
12420331766Sken	return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
12421331766Sken		    ((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
12422331766Sken		    (w->fwrev_low <= hw->workaround.fwrev) &&
12423331766Sken		    ((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
12424331766Sken}
12425331766Sken
12426331766Sken/**
12427331766Sken * @brief Setup HW runtime workarounds
12428331766Sken *
12429331766Sken * The function is called at the end of ocs_hw_setup() to setup any runtime workarounds
12430331766Sken * based on the HW/SLI setup.
12431331766Sken *
12432331766Sken * @param hw Pointer to HW structure
12433331766Sken *
12434331766Sken * @return none
12435331766Sken */
12436331766Sken
12437331766Skenvoid
12438331766Skenocs_hw_workaround_setup(struct ocs_hw_s *hw)
12439331766Sken{
12440331766Sken	hw_workaround_t *w;
12441331766Sken	sli4_t *sli4 = &hw->sli;
12442331766Sken	uint32_t i;
12443331766Sken
12444331766Sken	/* Initialize the workaround settings */
12445331766Sken	ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
12446331766Sken
12447331766Sken	/* If hw_war_version is non-null, then its a value that was set by a module parameter
12448331766Sken	 * (sorry for the break in abstraction, but workarounds are ... well, workarounds)
12449331766Sken	 */
12450331766Sken
12451331766Sken	if (hw->hw_war_version) {
12452331766Sken		hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
12453331766Sken	} else {
12454331766Sken		hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
12455331766Sken	}
12456331766Sken
12457331766Sken	/* Walk the workaround list, if a match is found, then handle it */
12458331766Sken	for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
12459331766Sken		if (ocs_hw_workaround_match(hw, w)) {
12460331766Sken			switch(w->workaround) {
12461331766Sken
12462331766Sken			case HW_WORKAROUND_TEST: {
12463331766Sken				ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
12464331766Sken				break;
12465331766Sken			}
12466331766Sken
12467331766Sken			case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
12468331766Sken				ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
12469331766Sken				hw->workaround.retain_tsend_io_length = 1;
12470331766Sken				break;
12471331766Sken			}
12472331766Sken			case HW_WORKAROUND_MAX_QUEUE: {
12473331766Sken				sli4_qtype_e q;
12474331766Sken
12475331766Sken				ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
12476331766Sken				for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
12477331766Sken					if (hw->num_qentries[q] > w->value) {
12478331766Sken						hw->num_qentries[q] = w->value;
12479331766Sken					}
12480331766Sken				}
12481331766Sken				break;
12482331766Sken			}
12483331766Sken			case HW_WORKAROUND_MAX_RQ: {
12484331766Sken				ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
12485331766Sken				if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
12486331766Sken					hw->num_qentries[SLI_QTYPE_RQ] = w->value;
12487331766Sken				}
12488331766Sken				break;
12489331766Sken			}
12490331766Sken			case HW_WORKAROUND_WQE_COUNT_METHOD: {
12491331766Sken				ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
12492331766Sken				sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
12493331766Sken				sli_calc_max_qentries(sli4);
12494331766Sken				break;
12495331766Sken			}
12496331766Sken			case HW_WORKAROUND_RQE_COUNT_METHOD: {
12497331766Sken				ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
12498331766Sken				sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
12499331766Sken				sli_calc_max_qentries(sli4);
12500331766Sken				break;
12501331766Sken			}
12502331766Sken			case HW_WORKAROUND_USE_UNREGISTERD_RPI:
12503331766Sken				ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
12504331766Sken				hw->workaround.use_unregistered_rpi = TRUE;
12505331766Sken				/*
12506331766Sken				 * Allocate an RPI that is never registered, to be used in the case where
12507331766Sken				 * a node has been unregistered, and its indicator (RPI) value is set to 0xFFFF
12508331766Sken				 */
12509331766Sken				if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
12510331766Sken					&hw->workaround.unregistered_index)) {
12511331766Sken					ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
12512331766Sken					hw->workaround.use_unregistered_rpi = FALSE;
12513331766Sken				}
12514331766Sken				break;
12515331766Sken			case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
12516331766Sken				ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
12517331766Sken				hw->workaround.disable_ar_tgt_dif = TRUE;
12518331766Sken				break;
12519331766Sken			case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
12520331766Sken				ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
12521331766Sken				hw->workaround.disable_dump_loc = TRUE;
12522331766Sken				break;
12523331766Sken			case HW_WORKAROUND_USE_DIF_QUARANTINE:
12524331766Sken				ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
12525331766Sken				hw->workaround.use_dif_quarantine = TRUE;
12526331766Sken				break;
12527331766Sken			case HW_WORKAROUND_USE_DIF_SEC_XRI:
12528331766Sken				ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
12529331766Sken				hw->workaround.use_dif_sec_xri = TRUE;
12530331766Sken				break;
12531331766Sken			case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
12532331766Sken				ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
12533331766Sken				hw->workaround.override_fcfi = TRUE;
12534331766Sken				break;
12535331766Sken
12536331766Sken			case HW_WORKAROUND_FW_VERSION_TOO_LOW:
12537331766Sken				ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
12538331766Sken				hw->workaround.fw_version_too_low = TRUE;
12539331766Sken				break;
12540331766Sken			case HW_WORKAROUND_SGLC_MISREPORTED:
12541331766Sken				ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
12542331766Sken				hw->workaround.sglc_misreported = TRUE;
12543331766Sken				break;
12544331766Sken			case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
12545331766Sken				ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
12546331766Sken				hw->workaround.ignore_send_frame = TRUE;
12547331766Sken				break;
12548331766Sken			} /* switch(w->workaround) */
12549331766Sken		}
12550331766Sken	}
12551331766Sken}
12552