xref: /freebsd/sys/dev/ocs_fc/ocs_hw.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 /**
35  * @file
36  * Defines and implements the Hardware Abstraction Layer (HW).
37  * All interaction with the hardware is performed through the HW, which abstracts
38  * the details of the underlying SLI-4 implementation.
39  */
40 
41 /**
42  * @defgroup devInitShutdown Device Initialization and Shutdown
43  * @defgroup domain Domain Functions
44  * @defgroup port Port Functions
45  * @defgroup node Remote Node Functions
46  * @defgroup io IO Functions
47  * @defgroup interrupt Interrupt handling
48  * @defgroup os OS Required Functions
49  */
50 
51 #include "ocs.h"
52 #include "ocs_os.h"
53 #include "ocs_hw.h"
54 #include "ocs_hw_queues.h"
55 
56 #define OCS_HW_MQ_DEPTH	128
57 #define OCS_HW_READ_FCF_SIZE	4096
58 #define OCS_HW_DEFAULT_AUTO_XFER_RDY_IOS	256
59 #define OCS_HW_WQ_TIMER_PERIOD_MS	500
60 
61 /* values used for setting the auto xfer rdy parameters */
62 #define OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT		0 /* 512 bytes */
63 #define OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT	TRUE
64 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT	FALSE
65 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT	0
66 #define OCS_HW_REQUE_XRI_REGTAG			65534
67 /* max command and response buffer lengths -- arbitrary at the moment */
68 #define OCS_HW_DMTF_CLP_CMD_MAX	256
69 #define OCS_HW_DMTF_CLP_RSP_MAX	256
70 
71 /* HW global data */
72 ocs_hw_global_t hw_global;
73 
74 static void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
75 static void ocs_hw_adjust_wqs(ocs_hw_t *hw);
76 static uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
77 static int32_t ocs_hw_cb_link(void *, void *);
78 static int32_t ocs_hw_cb_fip(void *, void *);
79 static int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
80 static int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
81 static int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
82 static int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
83 static int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
84 static int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
85 static ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
86 static ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
87 static int32_t ocs_hw_flush(ocs_hw_t *);
88 static int32_t ocs_hw_command_cancel(ocs_hw_t *);
89 static int32_t ocs_hw_io_cancel(ocs_hw_t *);
90 static void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
91 static void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
92 static int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
93 static 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);
94 static int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void  *);
95 static int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void  *);
96 static int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void  *);
97 static int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void  *);
98 static int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
99 static void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
100 static 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);
101 typedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
102 static 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);
103 static void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
104 
105 static int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
106 static ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
107 static ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
108 static ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
109 static ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
110 static 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 *);
111 static 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 *);
112 static void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
113 static ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
114 static ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
115 static ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
116 static void ocs_hw_io_free_internal(void *arg);
117 static void ocs_hw_io_free_port_owned(void *arg);
118 static ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
119 static ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
120 static void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
121 static int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
122 static ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
123 static ocs_hw_rtn_e ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable);
124 
125 /* HW domain database operations */
126 static int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
127 static int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
128 
129 /* Port state machine */
130 static void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
131 static void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
132 static void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
133 static void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
134 static void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
135 
136 /* Domain state machine */
137 static void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
138 static void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
139 static void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
140 static void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
141 static void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
142 static int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
143 static int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
144 static int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
145 
146 /* BZ 161832 */
147 static void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
148 
149 /* WQE timeouts */
150 static void target_wqe_timer_cb(void *arg);
151 static void shutdown_target_wqe_timer(ocs_hw_t *hw);
152 
153 static inline void
154 ocs_hw_add_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
155 {
156 	if (hw->config.emulate_tgt_wqe_timeout && io->tgt_wqe_timeout) {
157 		/*
158 		 * Active WQE list currently only used for
159 		 * target WQE timeouts.
160 		 */
161 		ocs_lock(&hw->io_lock);
162 			ocs_list_add_tail(&hw->io_timed_wqe, io);
163 			io->submit_ticks = ocs_get_os_ticks();
164 		ocs_unlock(&hw->io_lock);
165 	}
166 }
167 
168 static inline void
169 ocs_hw_remove_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
170 {
171 	if (hw->config.emulate_tgt_wqe_timeout) {
172 		/*
173 		 * If target wqe timeouts are enabled,
174 		 * remove from active wqe list.
175 		 */
176 		ocs_lock(&hw->io_lock);
177 			if (ocs_list_on_list(&io->wqe_link)) {
178 				ocs_list_remove(&hw->io_timed_wqe, io);
179 			}
180 		ocs_unlock(&hw->io_lock);
181 	}
182 }
183 
184 static uint8_t ocs_hw_iotype_is_originator(uint16_t io_type)
185 {
186 	switch (io_type) {
187 	case OCS_HW_IO_INITIATOR_READ:
188 	case OCS_HW_IO_INITIATOR_WRITE:
189 	case OCS_HW_IO_INITIATOR_NODATA:
190 	case OCS_HW_FC_CT:
191 	case OCS_HW_ELS_REQ:
192 		return 1;
193 	default:
194 		return 0;
195 	}
196 }
197 
198 static uint8_t ocs_hw_wcqe_abort_needed(uint16_t status, uint8_t ext, uint8_t xb)
199 {
200 	/* if exchange not active, nothing to abort */
201 	if (!xb) {
202 		return FALSE;
203 	}
204 	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT) {
205 		switch (ext) {
206 		/* exceptions where abort is not needed */
207 		case SLI4_FC_LOCAL_REJECT_INVALID_RPI: /* lancer returns this after unreg_rpi */
208 		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED: /* abort already in progress */
209 			return FALSE;
210 		default:
211 			break;
212 		}
213 	}
214 	return TRUE;
215 }
216 
217 /**
218  * @brief Determine the number of chutes on the device.
219  *
220  * @par Description
221  * Some devices require queue resources allocated per protocol processor
222  * (chute). This function returns the number of chutes on this device.
223  *
224  * @param hw Hardware context allocated by the caller.
225  *
226  * @return Returns the number of chutes on the device for protocol.
227  */
228 static uint32_t
229 ocs_hw_get_num_chutes(ocs_hw_t *hw)
230 {
231 	uint32_t num_chutes = 1;
232 
233 	if (sli_get_is_dual_ulp_capable(&hw->sli) &&
234 	    sli_get_is_ulp_enabled(&hw->sli, 0) &&
235 	    sli_get_is_ulp_enabled(&hw->sli, 1)) {
236 		num_chutes = 2;
237 	}
238 	return num_chutes;
239 }
240 
241 static ocs_hw_rtn_e
242 ocs_hw_link_event_init(ocs_hw_t *hw)
243 {
244 	ocs_hw_assert(hw);
245 
246 	hw->link.status = SLI_LINK_STATUS_MAX;
247 	hw->link.topology = SLI_LINK_TOPO_NONE;
248 	hw->link.medium = SLI_LINK_MEDIUM_MAX;
249 	hw->link.speed = 0;
250 	hw->link.loop_map = NULL;
251 	hw->link.fc_id = UINT32_MAX;
252 
253 	return OCS_HW_RTN_SUCCESS;
254 }
255 
256 /**
257  * @ingroup devInitShutdown
258  * @brief If this is physical port 0, then read the max dump size.
259  *
260  * @par Description
261  * Queries the FW for the maximum dump size
262  *
263  * @param hw Hardware context allocated by the caller.
264  *
265  * @return Returns 0 on success, or a non-zero value on failure.
266  */
267 static ocs_hw_rtn_e
268 ocs_hw_read_max_dump_size(ocs_hw_t *hw)
269 {
270 	uint8_t	buf[SLI4_BMBX_SIZE];
271 	uint8_t bus, dev, func;
272 	int 	rc;
273 
274 	/* lancer only */
275 	if ((SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) &&
276 	    (SLI4_IF_TYPE_LANCER_G7 != sli_get_if_type(&hw->sli))) {
277 		ocs_log_debug(hw->os, "Function only supported for I/F type 2\n");
278 		return OCS_HW_RTN_ERROR;
279 	}
280 
281 	/*
282 	 * Make sure the FW is new enough to support this command. If the FW
283 	 * is too old, the FW will UE.
284 	 */
285 	if (hw->workaround.disable_dump_loc) {
286 		ocs_log_test(hw->os, "FW version is too old for this feature\n");
287 		return OCS_HW_RTN_ERROR;
288 	}
289 
290 	/* attempt to detemine the dump size for function 0 only. */
291 	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
292 	if (func == 0) {
293 		if (sli_cmd_common_set_dump_location(&hw->sli, buf,
294 							SLI4_BMBX_SIZE, 1, 0, NULL, 0)) {
295 			sli4_res_common_set_dump_location_t *rsp =
296 				(sli4_res_common_set_dump_location_t *)
297 				(buf + offsetof(sli4_cmd_sli_config_t,
298 						payload.embed));
299 
300 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
301 			if (rc != OCS_HW_RTN_SUCCESS) {
302 				ocs_log_test(hw->os, "set dump location command failed\n");
303 				return rc;
304 			} else {
305 				hw->dump_size = rsp->buffer_length;
306 				ocs_log_debug(hw->os, "Dump size %x\n", rsp->buffer_length);
307 			}
308 		}
309 	}
310 	return OCS_HW_RTN_SUCCESS;
311 }
312 
313 /**
314  * @ingroup devInitShutdown
315  * @brief Set up the Hardware Abstraction Layer module.
316  *
317  * @par Description
318  * Calls set up to configure the hardware.
319  *
320  * @param hw Hardware context allocated by the caller.
321  * @param os Device abstraction.
322  * @param port_type Protocol type of port, such as FC and NIC.
323  *
324  * @todo Why is port_type a parameter?
325  *
326  * @return Returns 0 on success, or a non-zero value on failure.
327  */
328 ocs_hw_rtn_e
329 ocs_hw_setup(ocs_hw_t *hw, ocs_os_handle_t os, sli4_port_type_e port_type)
330 {
331 	uint32_t i;
332 	char prop_buf[32];
333 
334 	if (hw == NULL) {
335 		ocs_log_err(os, "bad parameter(s) hw=%p\n", hw);
336 		return OCS_HW_RTN_ERROR;
337 	}
338 
339 	if (hw->hw_setup_called) {
340 		/* Setup run-time workarounds.
341 		 * Call for each setup, to allow for hw_war_version
342 		 */
343 		ocs_hw_workaround_setup(hw);
344 		return OCS_HW_RTN_SUCCESS;
345 	}
346 
347 	/*
348 	 * ocs_hw_init() relies on NULL pointers indicating that a structure
349 	 * needs allocation. If a structure is non-NULL, ocs_hw_init() won't
350 	 * free/realloc that memory
351 	 */
352 	ocs_memset(hw, 0, sizeof(ocs_hw_t));
353 
354 	hw->hw_setup_called = TRUE;
355 
356 	hw->os = os;
357 
358 	ocs_lock_init(hw->os, &hw->cmd_lock, "HW_cmd_lock[%d]", ocs_instance(hw->os));
359 	ocs_list_init(&hw->cmd_head, ocs_command_ctx_t, link);
360 	ocs_list_init(&hw->cmd_pending, ocs_command_ctx_t, link);
361 	hw->cmd_head_count = 0;
362 
363 	ocs_lock_init(hw->os, &hw->io_lock, "HW_io_lock[%d]", ocs_instance(hw->os));
364 	ocs_lock_init(hw->os, &hw->io_abort_lock, "HW_io_abort_lock[%d]", ocs_instance(hw->os));
365 
366 	ocs_atomic_init(&hw->io_alloc_failed_count, 0);
367 
368 	hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
369 	hw->config.dif_seed = 0;
370 	hw->config.auto_xfer_rdy_blk_size_chip = OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT;
371 	hw->config.auto_xfer_rdy_ref_tag_is_lba = OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT;
372 	hw->config.auto_xfer_rdy_app_tag_valid =  OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT;
373 	hw->config.auto_xfer_rdy_app_tag_value = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT;
374 
375 	if (sli_setup(&hw->sli, hw->os, port_type)) {
376 		ocs_log_err(hw->os, "SLI setup failed\n");
377 		return OCS_HW_RTN_ERROR;
378 	}
379 
380 	ocs_memset(hw->domains, 0, sizeof(hw->domains));
381 
382 	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
383 
384 	ocs_hw_link_event_init(hw);
385 
386 	sli_callback(&hw->sli, SLI4_CB_LINK, ocs_hw_cb_link, hw);
387 	sli_callback(&hw->sli, SLI4_CB_FIP, ocs_hw_cb_fip, hw);
388 
389 	/*
390 	 * Set all the queue sizes to the maximum allowed. These values may
391 	 * be changes later by the adjust and workaround functions.
392 	 */
393 	for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) {
394 		hw->num_qentries[i] = sli_get_max_qentries(&hw->sli, i);
395 	}
396 
397 	/*
398 	 * The RQ assignment for RQ pair mode.
399 	 */
400 	hw->config.rq_default_buffer_size = OCS_HW_RQ_SIZE_PAYLOAD;
401 	hw->config.n_io = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
402 	if (ocs_get_property("auto_xfer_rdy_xri_cnt", prop_buf, sizeof(prop_buf)) == 0) {
403 		hw->config.auto_xfer_rdy_xri_cnt = ocs_strtoul(prop_buf, 0, 0);
404 	}
405 
406 	/* by default, enable initiator-only auto-ABTS emulation */
407 	hw->config.i_only_aab = TRUE;
408 
409 	/* Setup run-time workarounds */
410 	ocs_hw_workaround_setup(hw);
411 
412 	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
413 	if (hw->workaround.override_fcfi) {
414 		hw->first_domain_idx = -1;
415 	}
416 
417 	/* Must be done after the workaround setup */
418 	if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
419 	    (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))) {
420 
421 		(void)ocs_hw_read_max_dump_size(hw);
422 	}
423 
424 	/* calculate the number of WQs required. */
425 	ocs_hw_adjust_wqs(hw);
426 
427 	/* Set the default dif mode */
428 	if (! sli_is_dif_inline_capable(&hw->sli)) {
429 		ocs_log_test(hw->os, "not inline capable, setting mode to separate\n");
430 		hw->config.dif_mode = OCS_HW_DIF_MODE_SEPARATE;
431 	}
432 	/* Workaround: BZ 161832 */
433 	if (hw->workaround.use_dif_sec_xri) {
434 		ocs_list_init(&hw->sec_hio_wait_list, ocs_hw_io_t, link);
435 	}
436 
437 	/*
438 	 * Figure out the starting and max ULP to spread the WQs across the
439 	 * ULPs.
440 	 */
441 	if (sli_get_is_dual_ulp_capable(&hw->sli)) {
442 		if (sli_get_is_ulp_enabled(&hw->sli, 0) &&
443 		    sli_get_is_ulp_enabled(&hw->sli, 1)) {
444 			hw->ulp_start = 0;
445 			hw->ulp_max   = 1;
446 		} else if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
447 			hw->ulp_start = 0;
448 			hw->ulp_max   = 0;
449 		} else {
450 			hw->ulp_start = 1;
451 			hw->ulp_max   = 1;
452 		}
453 	} else {
454 		if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
455 			hw->ulp_start = 0;
456 			hw->ulp_max   = 0;
457 		} else {
458 			hw->ulp_start = 1;
459 			hw->ulp_max   = 1;
460 		}
461 	}
462 	ocs_log_debug(hw->os, "ulp_start %d, ulp_max %d\n",
463 		hw->ulp_start, hw->ulp_max);
464 	hw->config.queue_topology = hw_global.queue_topology_string;
465 
466 	hw->qtop = ocs_hw_qtop_parse(hw, hw->config.queue_topology);
467 
468 	hw->config.n_eq = hw->qtop->entry_counts[QTOP_EQ];
469 	hw->config.n_cq = hw->qtop->entry_counts[QTOP_CQ];
470 	hw->config.n_rq = hw->qtop->entry_counts[QTOP_RQ];
471 	hw->config.n_wq = hw->qtop->entry_counts[QTOP_WQ];
472 	hw->config.n_mq = hw->qtop->entry_counts[QTOP_MQ];
473 
474 	/* Verify qtop configuration against driver supported configuration */
475 	if (hw->config.n_rq > OCE_HW_MAX_NUM_MRQ_PAIRS) {
476 		ocs_log_crit(hw->os, "Max supported MRQ pairs = %d\n",
477 				OCE_HW_MAX_NUM_MRQ_PAIRS);
478 		return OCS_HW_RTN_ERROR;
479 	}
480 
481 	if (hw->config.n_eq > OCS_HW_MAX_NUM_EQ) {
482 		ocs_log_crit(hw->os, "Max supported EQs = %d\n",
483 				OCS_HW_MAX_NUM_EQ);
484 		return OCS_HW_RTN_ERROR;
485 	}
486 
487 	if (hw->config.n_cq > OCS_HW_MAX_NUM_CQ) {
488 		ocs_log_crit(hw->os, "Max supported CQs = %d\n",
489 				OCS_HW_MAX_NUM_CQ);
490 		return OCS_HW_RTN_ERROR;
491 	}
492 
493 	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
494 		ocs_log_crit(hw->os, "Max supported WQs = %d\n",
495 				OCS_HW_MAX_NUM_WQ);
496 		return OCS_HW_RTN_ERROR;
497 	}
498 
499 	if (hw->config.n_mq > OCS_HW_MAX_NUM_MQ) {
500 		ocs_log_crit(hw->os, "Max supported MQs = %d\n",
501 				OCS_HW_MAX_NUM_MQ);
502 		return OCS_HW_RTN_ERROR;
503 	}
504 
505 	return OCS_HW_RTN_SUCCESS;
506 }
507 
508 /**
509  * @ingroup devInitShutdown
510  * @brief Allocate memory structures to prepare for the device operation.
511  *
512  * @par Description
513  * Allocates memory structures needed by the device and prepares the device
514  * for operation.
515  * @n @n @b Note: This function may be called more than once (for example, at
516  * initialization and then after a reset), but the size of the internal resources
517  * may not be changed without tearing down the HW (ocs_hw_teardown()).
518  *
519  * @param hw Hardware context allocated by the caller.
520  *
521  * @return Returns 0 on success, or a non-zero value on failure.
522  */
523 ocs_hw_rtn_e
524 ocs_hw_init(ocs_hw_t *hw)
525 {
526 	ocs_hw_rtn_e	rc;
527 	uint32_t	i = 0;
528 	uint8_t		buf[SLI4_BMBX_SIZE];
529 	uint32_t	max_rpi;
530 	int		rem_count;
531 	int	        written_size = 0;
532 	uint32_t	count;
533 	char		prop_buf[32];
534 	uint32_t ramdisc_blocksize = 512;
535 	uint32_t q_count = 0;
536 	/*
537 	 * Make sure the command lists are empty. If this is start-of-day,
538 	 * they'll be empty since they were just initialized in ocs_hw_setup.
539 	 * If we've just gone through a reset, the command and command pending
540 	 * lists should have been cleaned up as part of the reset (ocs_hw_reset()).
541 	 */
542 	ocs_lock(&hw->cmd_lock);
543 		if (!ocs_list_empty(&hw->cmd_head)) {
544 			ocs_log_test(hw->os, "command found on cmd list\n");
545 			ocs_unlock(&hw->cmd_lock);
546 			return OCS_HW_RTN_ERROR;
547 		}
548 		if (!ocs_list_empty(&hw->cmd_pending)) {
549 			ocs_log_test(hw->os, "command found on pending list\n");
550 			ocs_unlock(&hw->cmd_lock);
551 			return OCS_HW_RTN_ERROR;
552 		}
553 	ocs_unlock(&hw->cmd_lock);
554 
555 	/* Free RQ buffers if prevously allocated */
556 	ocs_hw_rx_free(hw);
557 
558 	/*
559 	 * The IO queues must be initialized here for the reset case. The
560 	 * ocs_hw_init_io() function will re-add the IOs to the free list.
561 	 * The cmd_head list should be OK since we free all entries in
562 	 * ocs_hw_command_cancel() that is called in the ocs_hw_reset().
563 	 */
564 
565 	/* If we are in this function due to a reset, there may be stale items
566 	 * on lists that need to be removed.  Clean them up.
567 	 */
568 	rem_count=0;
569 	if (ocs_list_valid(&hw->io_wait_free)) {
570 		while ((!ocs_list_empty(&hw->io_wait_free))) {
571 			rem_count++;
572 			ocs_list_remove_head(&hw->io_wait_free);
573 		}
574 		if (rem_count > 0) {
575 			ocs_log_debug(hw->os, "removed %d items from io_wait_free list\n", rem_count);
576 		}
577 	}
578 	rem_count=0;
579 	if (ocs_list_valid(&hw->io_inuse)) {
580 		while ((!ocs_list_empty(&hw->io_inuse))) {
581 			rem_count++;
582 			ocs_list_remove_head(&hw->io_inuse);
583 		}
584 		if (rem_count > 0) {
585 			ocs_log_debug(hw->os, "removed %d items from io_inuse list\n", rem_count);
586 		}
587 	}
588 	rem_count=0;
589 	if (ocs_list_valid(&hw->io_free)) {
590 		while ((!ocs_list_empty(&hw->io_free))) {
591 			rem_count++;
592 			ocs_list_remove_head(&hw->io_free);
593 		}
594 		if (rem_count > 0) {
595 			ocs_log_debug(hw->os, "removed %d items from io_free list\n", rem_count);
596 		}
597 	}
598 	if (ocs_list_valid(&hw->io_port_owned)) {
599 		while ((!ocs_list_empty(&hw->io_port_owned))) {
600 			ocs_list_remove_head(&hw->io_port_owned);
601 		}
602 	}
603 	ocs_list_init(&hw->io_inuse, ocs_hw_io_t, link);
604 	ocs_list_init(&hw->io_free, ocs_hw_io_t, link);
605 	ocs_list_init(&hw->io_port_owned, ocs_hw_io_t, link);
606 	ocs_list_init(&hw->io_wait_free, ocs_hw_io_t, link);
607 	ocs_list_init(&hw->io_timed_wqe, ocs_hw_io_t, wqe_link);
608 	ocs_list_init(&hw->io_port_dnrx, ocs_hw_io_t, dnrx_link);
609 
610 	/* If MRQ not required, Make sure we dont request feature. */
611 	if (hw->config.n_rq == 1) {
612 		hw->sli.config.features.flag.mrqp = FALSE;
613 	}
614 
615 	if (sli_init(&hw->sli)) {
616 		ocs_log_err(hw->os, "SLI failed to initialize\n");
617 		return OCS_HW_RTN_ERROR;
618 	}
619 
620 	/*
621 	 * Enable the auto xfer rdy feature if requested.
622 	 */
623 	hw->auto_xfer_rdy_enabled = FALSE;
624 	if (sli_get_auto_xfer_rdy_capable(&hw->sli) &&
625 	    hw->config.auto_xfer_rdy_size > 0) {
626 		if (hw->config.esoc){
627 			if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
628 				ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
629 			}
630 			written_size = sli_cmd_config_auto_xfer_rdy_hp(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size, 1, ramdisc_blocksize);
631 		} else {
632 			written_size = sli_cmd_config_auto_xfer_rdy(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size);
633 		}
634 		if (written_size) {
635 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
636 			if (rc != OCS_HW_RTN_SUCCESS) {
637 				ocs_log_err(hw->os, "config auto xfer rdy failed\n");
638 				return rc;
639 			}
640 		}
641 		hw->auto_xfer_rdy_enabled = TRUE;
642 
643 		if (hw->config.auto_xfer_rdy_t10_enable) {
644 			rc = ocs_hw_config_auto_xfer_rdy_t10pi(hw, buf);
645 			if (rc != OCS_HW_RTN_SUCCESS) {
646 				ocs_log_err(hw->os, "set parameters auto xfer rdy T10 PI failed\n");
647 				return rc;
648 			}
649 		}
650 	}
651 
652 	if(hw->sliport_healthcheck) {
653 		rc = ocs_hw_config_sli_port_health_check(hw, 0, 1);
654 		if (rc != OCS_HW_RTN_SUCCESS) {
655 			ocs_log_err(hw->os, "Enabling Sliport Health check failed \n");
656 			return rc;
657 		}
658 	}
659 
660 	/*
661 	 * Set FDT transfer hint, only works on Lancer
662 	 */
663 	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) && (OCS_HW_FDT_XFER_HINT != 0)) {
664 		/*
665 		 * Non-fatal error. In particular, we can disregard failure to set OCS_HW_FDT_XFER_HINT on
666 		 * devices with legacy firmware that do not support OCS_HW_FDT_XFER_HINT feature.
667 		 */
668 		ocs_hw_config_set_fdt_xfer_hint(hw, OCS_HW_FDT_XFER_HINT);
669 	}
670 
671 	/*
672 	 * Verify that we have not exceeded any queue sizes
673 	 */
674 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_EQ),
675 					OCS_HW_MAX_NUM_EQ);
676 	if (hw->config.n_eq > q_count) {
677 		ocs_log_err(hw->os, "requested %d EQ but %d allowed\n",
678 			    hw->config.n_eq, q_count);
679 		return OCS_HW_RTN_ERROR;
680 	}
681 
682 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_CQ),
683 					OCS_HW_MAX_NUM_CQ);
684 	if (hw->config.n_cq > q_count) {
685 		ocs_log_err(hw->os, "requested %d CQ but %d allowed\n",
686 			    hw->config.n_cq, q_count);
687 		return OCS_HW_RTN_ERROR;
688 	}
689 
690 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_MQ),
691 					OCS_HW_MAX_NUM_MQ);
692 	if (hw->config.n_mq > q_count) {
693 		ocs_log_err(hw->os, "requested %d MQ but %d allowed\n",
694 			    hw->config.n_mq, q_count);
695 		return OCS_HW_RTN_ERROR;
696 	}
697 
698 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_RQ),
699 					OCS_HW_MAX_NUM_RQ);
700 	if (hw->config.n_rq > q_count) {
701 		ocs_log_err(hw->os, "requested %d RQ but %d allowed\n",
702 			    hw->config.n_rq, q_count);
703 		return OCS_HW_RTN_ERROR;
704 	}
705 
706 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ),
707 					OCS_HW_MAX_NUM_WQ);
708 	if (hw->config.n_wq > q_count) {
709 		ocs_log_err(hw->os, "requested %d WQ but %d allowed\n",
710 			    hw->config.n_wq, q_count);
711 		return OCS_HW_RTN_ERROR;
712 	}
713 
714 	/* zero the hashes */
715 	ocs_memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
716 	ocs_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
717 			OCS_HW_MAX_NUM_CQ, OCS_HW_Q_HASH_SIZE);
718 
719 	ocs_memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
720 	ocs_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
721 			OCS_HW_MAX_NUM_RQ, OCS_HW_Q_HASH_SIZE);
722 
723 	ocs_memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
724 	ocs_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
725 			OCS_HW_MAX_NUM_WQ, OCS_HW_Q_HASH_SIZE);
726 
727 	rc = ocs_hw_init_queues(hw, hw->qtop);
728 	if (rc != OCS_HW_RTN_SUCCESS) {
729 		return rc;
730 	}
731 
732 	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
733 	i = sli_fc_get_rpi_requirements(&hw->sli, max_rpi);
734 	if (i) {
735 		ocs_dma_t payload_memory;
736 
737 		rc = OCS_HW_RTN_ERROR;
738 
739 		if (hw->rnode_mem.size) {
740 			ocs_dma_free(hw->os, &hw->rnode_mem);
741 		}
742 
743 		if (ocs_dma_alloc(hw->os, &hw->rnode_mem, i, 4096)) {
744 			ocs_log_err(hw->os, "remote node memory allocation fail\n");
745 			return OCS_HW_RTN_NO_MEMORY;
746 		}
747 
748 		payload_memory.size = 0;
749 		if (sli_cmd_fcoe_post_hdr_templates(&hw->sli, buf, SLI4_BMBX_SIZE,
750 					&hw->rnode_mem, UINT16_MAX, &payload_memory)) {
751 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
752 
753 			if (payload_memory.size != 0) {
754 				/* The command was non-embedded - need to free the dma buffer */
755 				ocs_dma_free(hw->os, &payload_memory);
756 			}
757 		}
758 
759 		if (rc != OCS_HW_RTN_SUCCESS) {
760 			ocs_log_err(hw->os, "header template registration failed\n");
761 			return rc;
762 		}
763 	}
764 
765 	/* Allocate and post RQ buffers */
766 	rc = ocs_hw_rx_allocate(hw);
767 	if (rc) {
768 		ocs_log_err(hw->os, "rx_allocate failed\n");
769 		return rc;
770 	}
771 
772 	/* Populate hw->seq_free_list */
773 	if (hw->seq_pool == NULL) {
774 		uint32_t count = 0;
775 		uint32_t i;
776 
777 		/* Sum up the total number of RQ entries, to use to allocate the sequence object pool */
778 		for (i = 0; i < hw->hw_rq_count; i++) {
779 			count += hw->hw_rq[i]->entry_count;
780 		}
781 
782 		hw->seq_pool = ocs_array_alloc(hw->os, sizeof(ocs_hw_sequence_t), count);
783 		if (hw->seq_pool == NULL) {
784 			ocs_log_err(hw->os, "malloc seq_pool failed\n");
785 			return OCS_HW_RTN_NO_MEMORY;
786 		}
787 	}
788 
789 	if(ocs_hw_rx_post(hw)) {
790 		ocs_log_err(hw->os, "WARNING - error posting RQ buffers\n");
791 	}
792 
793 	/* Allocate rpi_ref if not previously allocated */
794 	if (hw->rpi_ref == NULL) {
795 		hw->rpi_ref = ocs_malloc(hw->os, max_rpi * sizeof(*hw->rpi_ref),
796 					  OCS_M_ZERO | OCS_M_NOWAIT);
797 		if (hw->rpi_ref == NULL) {
798 			ocs_log_err(hw->os, "rpi_ref allocation failure (%d)\n", i);
799 			return OCS_HW_RTN_NO_MEMORY;
800 		}
801 	}
802 
803 	for (i = 0; i < max_rpi; i ++) {
804 		ocs_atomic_init(&hw->rpi_ref[i].rpi_count, 0);
805 		ocs_atomic_init(&hw->rpi_ref[i].rpi_attached, 0);
806 	}
807 
808 	ocs_memset(hw->domains, 0, sizeof(hw->domains));
809 
810 	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
811 	if (hw->workaround.override_fcfi) {
812 		hw->first_domain_idx = -1;
813 	}
814 
815 	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
816 
817 	/* Register a FCFI to allow unsolicited frames to be routed to the driver */
818 	if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
819 		if (hw->hw_mrq_count) {
820 			ocs_log_debug(hw->os, "using REG_FCFI MRQ\n");
821 
822 			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0, 0);
823 			if (rc != OCS_HW_RTN_SUCCESS) {
824 				ocs_log_err(hw->os, "REG_FCFI_MRQ FCFI registration failed\n");
825 				return rc;
826 			}
827 
828 			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0, 0);
829 			if (rc != OCS_HW_RTN_SUCCESS) {
830 				ocs_log_err(hw->os, "REG_FCFI_MRQ MRQ registration failed\n");
831 				return rc;
832 			}
833 		} else {
834 			sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
835 
836 			ocs_log_debug(hw->os, "using REG_FCFI standard\n");
837 
838 			/* Set the filter match/mask values from hw's filter_def values */
839 			for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
840 				rq_cfg[i].rq_id = 0xffff;
841 				rq_cfg[i].r_ctl_mask =	(uint8_t)  hw->config.filter_def[i];
842 				rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
843 				rq_cfg[i].type_mask =	(uint8_t) (hw->config.filter_def[i] >> 16);
844 				rq_cfg[i].type_match =	(uint8_t) (hw->config.filter_def[i] >> 24);
845 			}
846 
847 			/*
848 			 * Update the rq_id's of the FCF configuration (don't update more than the number
849 			 * of rq_cfg elements)
850 			 */
851 			for (i = 0; i < OCS_MIN(hw->hw_rq_count, SLI4_CMD_REG_FCFI_NUM_RQ_CFG); i++) {
852 				hw_rq_t *rq = hw->hw_rq[i];
853 				uint32_t j;
854 				for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
855 					uint32_t mask = (rq->filter_mask != 0) ? rq->filter_mask : 1;
856 					if (mask & (1U << j)) {
857 						rq_cfg[j].rq_id = rq->hdr->id;
858 						ocs_log_debug(hw->os, "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
859 							j, hw->config.filter_def[j], i, rq->hdr->id);
860 					}
861 				}
862 			}
863 
864 			rc = OCS_HW_RTN_ERROR;
865 
866 			if (sli_cmd_reg_fcfi(&hw->sli, buf, SLI4_BMBX_SIZE, 0, rq_cfg, 0)) {
867 				rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
868 			}
869 
870 			if (rc != OCS_HW_RTN_SUCCESS) {
871 				ocs_log_err(hw->os, "FCFI registration failed\n");
872 				return rc;
873 			}
874 			hw->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)buf)->fcfi;
875 		}
876 	}
877 
878 	/*
879 	 * Allocate the WQ request tag pool, if not previously allocated (the request tag value is 16 bits,
880 	 * thus the pool allocation size of 64k)
881 	 */
882 	rc = ocs_hw_reqtag_init(hw);
883 	if (rc) {
884 		ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed: %d\n", rc);
885 		return rc;
886 	}
887 
888 	rc = ocs_hw_setup_io(hw);
889 	if (rc) {
890 		ocs_log_err(hw->os, "IO allocation failure\n");
891 		return rc;
892 	}
893 
894 	rc = ocs_hw_init_io(hw);
895 	if (rc) {
896 		ocs_log_err(hw->os, "IO initialization failure\n");
897 		return rc;
898 	}
899 
900 	ocs_queue_history_init(hw->os, &hw->q_hist);
901 
902 	/* get hw link config; polling, so callback will be called immediately */
903 	hw->linkcfg = OCS_HW_LINKCFG_NA;
904 	ocs_hw_get_linkcfg(hw, OCS_CMD_POLL, ocs_hw_init_linkcfg_cb, hw);
905 
906 	/* if lancer ethernet, ethernet ports need to be enabled */
907 	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) &&
908 	    (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_ETHERNET)) {
909 		if (ocs_hw_set_eth_license(hw, hw->eth_license)) {
910 			/* log warning but continue */
911 			ocs_log_err(hw->os, "Failed to set ethernet license\n");
912 		}
913 	}
914 
915 	/* Set the DIF seed - only for lancer right now */
916 	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli) &&
917 	    ocs_hw_set_dif_seed(hw) != OCS_HW_RTN_SUCCESS) {
918 		ocs_log_err(hw->os, "Failed to set DIF seed value\n");
919 		return rc;
920 	}
921 
922 	/* Set the DIF mode - skyhawk only */
923 	if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli) &&
924 	    sli_get_dif_capable(&hw->sli)) {
925 		rc = ocs_hw_set_dif_mode(hw);
926 		if (rc != OCS_HW_RTN_SUCCESS) {
927 			ocs_log_err(hw->os, "Failed to set DIF mode value\n");
928 			return rc;
929 		}
930 	}
931 
932 	/*
933 	 * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ entries
934 	 */
935 	for (i = 0; i < hw->eq_count; i++) {
936 		sli_queue_arm(&hw->sli, &hw->eq[i], TRUE);
937 	}
938 
939 	/*
940 	 * Initialize RQ hash
941 	 */
942 	for (i = 0; i < hw->rq_count; i++) {
943 		ocs_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
944 	}
945 
946 	/*
947 	 * Initialize WQ hash
948 	 */
949 	for (i = 0; i < hw->wq_count; i++) {
950 		ocs_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
951 	}
952 
953 	/*
954 	 * Arming the CQ allows (e.g.) MQ completions to write CQ entries
955 	 */
956 	for (i = 0; i < hw->cq_count; i++) {
957 		ocs_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
958 		sli_queue_arm(&hw->sli, &hw->cq[i], TRUE);
959 	}
960 
961 	/* record the fact that the queues are functional */
962 	hw->state = OCS_HW_STATE_ACTIVE;
963 
964 	/* Note: Must be after the IOs are setup and the state is active*/
965 	if (ocs_hw_rqpair_init(hw)) {
966 		ocs_log_err(hw->os, "WARNING - error initializing RQ pair\n");
967 	}
968 
969 	/* finally kick off periodic timer to check for timed out target WQEs */
970 	if (hw->config.emulate_tgt_wqe_timeout) {
971 		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw,
972 				OCS_HW_WQ_TIMER_PERIOD_MS);
973 	}
974 
975 	/*
976 	 * Allocate a HW IOs for send frame.  Allocate one for each Class 1 WQ, or if there
977 	 * are none of those, allocate one for WQ[0]
978 	 */
979 	if ((count = ocs_varray_get_count(hw->wq_class_array[1])) > 0) {
980 		for (i = 0; i < count; i++) {
981 			hw_wq_t *wq = ocs_varray_iter_next(hw->wq_class_array[1]);
982 			wq->send_frame_io = ocs_hw_io_alloc(hw);
983 			if (wq->send_frame_io == NULL) {
984 				ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
985 			}
986 		}
987 	} else {
988 		hw->hw_wq[0]->send_frame_io = ocs_hw_io_alloc(hw);
989 		if (hw->hw_wq[0]->send_frame_io == NULL) {
990 			ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
991 		}
992 	}
993 
994 	/* Initialize send frame frame sequence id */
995 	ocs_atomic_init(&hw->send_frame_seq_id, 0);
996 
997 	/* Initialize watchdog timer if enabled by user */
998 	hw->expiration_logged = 0;
999 	if(hw->watchdog_timeout) {
1000 		if((hw->watchdog_timeout < 1) || (hw->watchdog_timeout > 65534)) {
1001 			ocs_log_err(hw->os, "watchdog_timeout out of range: Valid range is 1 - 65534\n");
1002 		}else if(!ocs_hw_config_watchdog_timer(hw)) {
1003 			ocs_log_info(hw->os, "watchdog timer configured with timeout = %d seconds \n", hw->watchdog_timeout);
1004 		}
1005 	}
1006 
1007 	if (ocs_dma_alloc(hw->os, &hw->domain_dmem, 112, 4)) {
1008 	   ocs_log_err(hw->os, "domain node memory allocation fail\n");
1009 	   return OCS_HW_RTN_NO_MEMORY;
1010 	}
1011 
1012 	if (ocs_dma_alloc(hw->os, &hw->fcf_dmem, OCS_HW_READ_FCF_SIZE, OCS_HW_READ_FCF_SIZE)) {
1013 	   ocs_log_err(hw->os, "domain fcf memory allocation fail\n");
1014 	   return OCS_HW_RTN_NO_MEMORY;
1015 	}
1016 
1017 	if ((0 == hw->loop_map.size) &&	ocs_dma_alloc(hw->os, &hw->loop_map,
1018 				SLI4_MIN_LOOP_MAP_BYTES, 4)) {
1019 		ocs_log_err(hw->os, "Loop dma alloc failed size:%d \n", hw->loop_map.size);
1020 	}
1021 
1022 	return OCS_HW_RTN_SUCCESS;
1023 }
1024 
1025 /**
1026  * @brief Configure Multi-RQ
1027  *
1028  * @param hw	Hardware context allocated by the caller.
1029  * @param mode	1 to set MRQ filters and 0 to set FCFI index
1030  * @param vlanid    valid in mode 0
1031  * @param fcf_index valid in mode 0
1032  *
1033  * @return Returns 0 on success, or a non-zero value on failure.
1034  */
1035 static int32_t
1036 ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t mode, uint16_t vlanid, uint16_t fcf_index)
1037 {
1038 	uint8_t buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
1039 	hw_rq_t *rq;
1040 	sli4_cmd_reg_fcfi_mrq_t *rsp = NULL;
1041 	uint32_t i, j;
1042 	sli4_cmd_rq_cfg_t rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
1043 	int32_t rc;
1044 
1045 	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1046 		goto issue_cmd;
1047 	}
1048 
1049 	/* Set the filter match/mask values from hw's filter_def values */
1050 	for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
1051 		rq_filter[i].rq_id = 0xffff;
1052 		rq_filter[i].r_ctl_mask  = (uint8_t)  hw->config.filter_def[i];
1053 		rq_filter[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
1054 		rq_filter[i].type_mask   = (uint8_t) (hw->config.filter_def[i] >> 16);
1055 		rq_filter[i].type_match  = (uint8_t) (hw->config.filter_def[i] >> 24);
1056 	}
1057 
1058 	/* Accumulate counts for each filter type used, build rq_ids[] list */
1059 	for (i = 0; i < hw->hw_rq_count; i++) {
1060 		rq = hw->hw_rq[i];
1061 		for (j = 0; j < SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG; j++) {
1062 			if (rq->filter_mask & (1U << j)) {
1063 				if (rq_filter[j].rq_id != 0xffff) {
1064 					/* Already used. Bailout ifts not RQset case */
1065 					if (!rq->is_mrq || (rq_filter[j].rq_id != rq->base_mrq_id)) {
1066 						ocs_log_err(hw->os, "Wrong queue topology.\n");
1067 						return OCS_HW_RTN_ERROR;
1068 					}
1069 					continue;
1070 				}
1071 
1072 				if (rq->is_mrq) {
1073 					rq_filter[j].rq_id = rq->base_mrq_id;
1074 					mrq_bitmask |= (1U << j);
1075 				} else {
1076 					rq_filter[j].rq_id = rq->hdr->id;
1077 				}
1078 			}
1079 		}
1080 	}
1081 
1082 issue_cmd:
1083 	/* Invoke REG_FCFI_MRQ */
1084 	rc = sli_cmd_reg_fcfi_mrq(&hw->sli,
1085 				 buf,					/* buf */
1086 				 SLI4_BMBX_SIZE,			/* size */
1087 				 mode,					/* mode 1 */
1088 				 fcf_index,				/* fcf_index */
1089 				 vlanid,				/* vlan_id */
1090 				 hw->config.rq_selection_policy,	/* RQ selection policy*/
1091 				 mrq_bitmask,				/* MRQ bitmask */
1092 				 hw->hw_mrq_count,			/* num_mrqs */
1093 				 rq_filter);				/* RQ filter */
1094 	if (rc == 0) {
1095 		ocs_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed: %d\n", rc);
1096 		return OCS_HW_RTN_ERROR;
1097 	}
1098 
1099 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
1100 
1101 	rsp = (sli4_cmd_reg_fcfi_mrq_t *)buf;
1102 
1103 	if ((rc != OCS_HW_RTN_SUCCESS) || (rsp->hdr.status)) {
1104 		ocs_log_err(hw->os, "FCFI MRQ registration failed. cmd = %x status = %x\n",
1105 			    rsp->hdr.command, rsp->hdr.status);
1106 		return OCS_HW_RTN_ERROR;
1107 	}
1108 
1109 	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1110 		hw->fcf_indicator = rsp->fcfi;
1111 	}
1112 	return 0;
1113 }
1114 
1115 /**
1116  * @brief Callback function for getting linkcfg during HW initialization.
1117  *
1118  * @param status Status of the linkcfg get operation.
1119  * @param value Link configuration enum to which the link configuration is set.
1120  * @param arg Callback argument (ocs_hw_t *).
1121  *
1122  * @return None.
1123  */
1124 static void
1125 ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
1126 {
1127 	ocs_hw_t *hw = (ocs_hw_t *)arg;
1128 	if (status == 0) {
1129 		hw->linkcfg = (ocs_hw_linkcfg_e)value;
1130 	} else {
1131 		hw->linkcfg = OCS_HW_LINKCFG_NA;
1132 	}
1133 	ocs_log_debug(hw->os, "linkcfg=%d\n", hw->linkcfg);
1134 }
1135 
1136 /**
1137  * @ingroup devInitShutdown
1138  * @brief Tear down the Hardware Abstraction Layer module.
1139  *
1140  * @par Description
1141  * Frees memory structures needed by the device, and shuts down the device. Does
1142  * not free the HW context memory (which is done by the caller).
1143  *
1144  * @param hw Hardware context allocated by the caller.
1145  *
1146  * @return Returns 0 on success, or a non-zero value on failure.
1147  */
1148 ocs_hw_rtn_e
1149 ocs_hw_teardown(ocs_hw_t *hw)
1150 {
1151 	uint32_t	i = 0;
1152 	uint32_t	iters = 10;/*XXX*/
1153 	uint32_t	max_rpi;
1154 	uint32_t destroy_queues;
1155 	uint32_t free_memory;
1156 
1157 	if (!hw) {
1158 		ocs_log_err(NULL, "bad parameter(s) hw=%p\n", hw);
1159 		return OCS_HW_RTN_ERROR;
1160 	}
1161 
1162 	destroy_queues = (hw->state == OCS_HW_STATE_ACTIVE);
1163 	free_memory = (hw->state != OCS_HW_STATE_UNINITIALIZED);
1164 
1165 	/* shutdown target wqe timer */
1166 	shutdown_target_wqe_timer(hw);
1167 
1168 	/* Cancel watchdog timer if enabled */
1169 	if(hw->watchdog_timeout) {
1170 		hw->watchdog_timeout = 0;
1171 		ocs_hw_config_watchdog_timer(hw);
1172 	}
1173 
1174 	/* Cancel Sliport Healthcheck */
1175 	if(hw->sliport_healthcheck) {
1176 		hw->sliport_healthcheck = 0;
1177 		ocs_hw_config_sli_port_health_check(hw, 0, 0);
1178 	}
1179 
1180 	if (hw->state != OCS_HW_STATE_QUEUES_ALLOCATED) {
1181 		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1182 
1183 		ocs_hw_flush(hw);
1184 
1185 		/* If there are outstanding commands, wait for them to complete */
1186 		while (!ocs_list_empty(&hw->cmd_head) && iters) {
1187 			ocs_udelay(10000);
1188 			ocs_hw_flush(hw);
1189 			iters--;
1190 		}
1191 
1192 		if (ocs_list_empty(&hw->cmd_head)) {
1193 			ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1194 		} else {
1195 			ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1196 		}
1197 
1198 		/* Cancel any remaining commands */
1199 		ocs_hw_command_cancel(hw);
1200 	} else {
1201 		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1202 	}
1203 
1204 	ocs_lock_free(&hw->cmd_lock);
1205 
1206 	/* Free unregistered RPI if workaround is in force */
1207 	if (hw->workaround.use_unregistered_rpi) {
1208 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, hw->workaround.unregistered_rid);
1209 	}
1210 
1211 	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1212 	if (hw->rpi_ref) {
1213 		for (i = 0; i < max_rpi; i++) {
1214 			if (ocs_atomic_read(&hw->rpi_ref[i].rpi_count)) {
1215 				ocs_log_debug(hw->os, "non-zero ref [%d]=%d\n",
1216 						i, ocs_atomic_read(&hw->rpi_ref[i].rpi_count));
1217 			}
1218 		}
1219 		ocs_free(hw->os, hw->rpi_ref, max_rpi * sizeof(*hw->rpi_ref));
1220 		hw->rpi_ref = NULL;
1221 	}
1222 
1223 	ocs_dma_free(hw->os, &hw->rnode_mem);
1224 
1225 	if (hw->io) {
1226 		for (i = 0; i < hw->config.n_io; i++) {
1227 			if (hw->io[i] && (hw->io[i]->sgl != NULL) &&
1228 			    (hw->io[i]->sgl->virt != NULL)) {
1229 				if(hw->io[i]->is_port_owned) {
1230 					ocs_lock_free(&hw->io[i]->axr_lock);
1231 				}
1232 				ocs_dma_free(hw->os, hw->io[i]->sgl);
1233 			}
1234 			ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
1235 			hw->io[i] = NULL;
1236 		}
1237 		ocs_free(hw->os, hw->wqe_buffs, hw->config.n_io * hw->sli.config.wqe_size);
1238 		hw->wqe_buffs = NULL;
1239 		ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t *));
1240 		hw->io = NULL;
1241 	}
1242 
1243 	ocs_dma_free(hw->os, &hw->xfer_rdy);
1244 	ocs_dma_free(hw->os, &hw->dump_sges);
1245 	ocs_dma_free(hw->os, &hw->loop_map);
1246 
1247 	ocs_lock_free(&hw->io_lock);
1248 	ocs_lock_free(&hw->io_abort_lock);
1249 
1250 	for (i = 0; i < hw->wq_count; i++) {
1251 		sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues, free_memory);
1252 	}
1253 
1254 	for (i = 0; i < hw->rq_count; i++) {
1255 		sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, free_memory);
1256 	}
1257 
1258 	for (i = 0; i < hw->mq_count; i++) {
1259 		sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, free_memory);
1260 	}
1261 
1262 	for (i = 0; i < hw->cq_count; i++) {
1263 		sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, free_memory);
1264 	}
1265 
1266 	for (i = 0; i < hw->eq_count; i++) {
1267 		sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, free_memory);
1268 	}
1269 
1270 	ocs_hw_qtop_free(hw->qtop);
1271 
1272 	/* Free rq buffers */
1273 	ocs_hw_rx_free(hw);
1274 
1275 	hw_queue_teardown(hw);
1276 
1277 	ocs_hw_rqpair_teardown(hw);
1278 
1279 	if (sli_teardown(&hw->sli)) {
1280 		ocs_log_err(hw->os, "SLI teardown failed\n");
1281 	}
1282 
1283 	ocs_queue_history_free(&hw->q_hist);
1284 
1285 	/* record the fact that the queues are non-functional */
1286 	hw->state = OCS_HW_STATE_UNINITIALIZED;
1287 
1288 	/* free sequence free pool */
1289 	ocs_array_free(hw->seq_pool);
1290 	hw->seq_pool = NULL;
1291 
1292 	/* free hw_wq_callback pool */
1293 	ocs_pool_free(hw->wq_reqtag_pool);
1294 
1295 	ocs_dma_free(hw->os, &hw->domain_dmem);
1296 	ocs_dma_free(hw->os, &hw->fcf_dmem);
1297 	/* Mark HW setup as not having been called */
1298 	hw->hw_setup_called = FALSE;
1299 
1300 	return OCS_HW_RTN_SUCCESS;
1301 }
1302 
1303 ocs_hw_rtn_e
1304 ocs_hw_reset(ocs_hw_t *hw, ocs_hw_reset_e reset)
1305 {
1306 	uint32_t	i;
1307 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1308 	uint32_t	iters;
1309 	ocs_hw_state_e prev_state = hw->state;
1310 
1311 	if (hw->state != OCS_HW_STATE_ACTIVE) {
1312 		ocs_log_test(hw->os, "HW state %d is not active\n", hw->state);
1313 	}
1314 
1315 	hw->state = OCS_HW_STATE_RESET_IN_PROGRESS;
1316 
1317 	/* shutdown target wqe timer */
1318 	shutdown_target_wqe_timer(hw);
1319 
1320 	ocs_hw_flush(hw);
1321 
1322 	/*
1323 	 * If an mailbox command requiring a DMA is outstanding (i.e. SFP/DDM),
1324 	 * then the FW will UE when the reset is issued. So attempt to complete
1325 	 * all mailbox commands.
1326 	 */
1327 	iters = 10;
1328 	while (!ocs_list_empty(&hw->cmd_head) && iters) {
1329 		ocs_udelay(10000);
1330 		ocs_hw_flush(hw);
1331 		iters--;
1332 	}
1333 
1334 	if (ocs_list_empty(&hw->cmd_head)) {
1335 		ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1336 	} else {
1337 		ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1338 	}
1339 
1340 	/* Reset the chip */
1341 	switch(reset) {
1342 	case OCS_HW_RESET_FUNCTION:
1343 		ocs_log_debug(hw->os, "issuing function level reset\n");
1344 		if (sli_reset(&hw->sli)) {
1345 			ocs_log_err(hw->os, "sli_reset failed\n");
1346 			rc = OCS_HW_RTN_ERROR;
1347 		}
1348 		break;
1349 	case OCS_HW_RESET_FIRMWARE:
1350 		ocs_log_debug(hw->os, "issuing firmware reset\n");
1351 		if (sli_fw_reset(&hw->sli)) {
1352 			ocs_log_err(hw->os, "sli_soft_reset failed\n");
1353 			rc = OCS_HW_RTN_ERROR;
1354 		}
1355 		/*
1356 		 * Because the FW reset leaves the FW in a non-running state,
1357 		 * follow that with a regular reset.
1358 		 */
1359 		ocs_log_debug(hw->os, "issuing function level reset\n");
1360 		if (sli_reset(&hw->sli)) {
1361 			ocs_log_err(hw->os, "sli_reset failed\n");
1362 			rc = OCS_HW_RTN_ERROR;
1363 		}
1364 		break;
1365 	default:
1366 		ocs_log_test(hw->os, "unknown reset type - no reset performed\n");
1367 		hw->state = prev_state;
1368 		return OCS_HW_RTN_ERROR;
1369 	}
1370 
1371 	/* Not safe to walk command/io lists unless they've been initialized */
1372 	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1373 		ocs_hw_command_cancel(hw);
1374 
1375 		/* Clean up the inuse list, the free list and the wait free list */
1376 		ocs_hw_io_cancel(hw);
1377 
1378 		ocs_memset(hw->domains, 0, sizeof(hw->domains));
1379 		ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
1380 
1381 		ocs_hw_link_event_init(hw);
1382 
1383 		ocs_lock(&hw->io_lock);
1384 			/* The io lists should be empty, but remove any that didn't get cleaned up. */
1385 			while (!ocs_list_empty(&hw->io_timed_wqe)) {
1386 				ocs_list_remove_head(&hw->io_timed_wqe);
1387 			}
1388 			/* Don't clean up the io_inuse list, the backend will do that when it finishes the IO */
1389 
1390 			while (!ocs_list_empty(&hw->io_free)) {
1391 				ocs_list_remove_head(&hw->io_free);
1392 			}
1393 			while (!ocs_list_empty(&hw->io_wait_free)) {
1394 				ocs_list_remove_head(&hw->io_wait_free);
1395 			}
1396 
1397 			/* Reset the request tag pool, the HW IO request tags are reassigned in ocs_hw_setup_io() */
1398 			ocs_hw_reqtag_reset(hw);
1399 
1400 		ocs_unlock(&hw->io_lock);
1401 	}
1402 
1403 	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1404 		for (i = 0; i < hw->wq_count; i++) {
1405 			sli_queue_reset(&hw->sli, &hw->wq[i]);
1406 		}
1407 
1408 		for (i = 0; i < hw->rq_count; i++) {
1409 			sli_queue_reset(&hw->sli, &hw->rq[i]);
1410 		}
1411 
1412 		for (i = 0; i < hw->hw_rq_count; i++) {
1413 			hw_rq_t *rq = hw->hw_rq[i];
1414 			if (rq->rq_tracker != NULL) {
1415 				uint32_t j;
1416 
1417 				for (j = 0; j < rq->entry_count; j++) {
1418 					rq->rq_tracker[j] = NULL;
1419 				}
1420 			}
1421 		}
1422 
1423 		for (i = 0; i < hw->mq_count; i++) {
1424 			sli_queue_reset(&hw->sli, &hw->mq[i]);
1425 		}
1426 
1427 		for (i = 0; i < hw->cq_count; i++) {
1428 			sli_queue_reset(&hw->sli, &hw->cq[i]);
1429 		}
1430 
1431 		for (i = 0; i < hw->eq_count; i++) {
1432 			sli_queue_reset(&hw->sli, &hw->eq[i]);
1433 		}
1434 
1435 		/* Free rq buffers */
1436 		ocs_hw_rx_free(hw);
1437 
1438 		/* Teardown the HW queue topology */
1439 		hw_queue_teardown(hw);
1440 	} else {
1441 		/* Free rq buffers */
1442 		ocs_hw_rx_free(hw);
1443 	}
1444 
1445 	/*
1446 	 * Re-apply the run-time workarounds after clearing the SLI config
1447 	 * fields in sli_reset.
1448 	 */
1449 	ocs_hw_workaround_setup(hw);
1450 	hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
1451 
1452 	return rc;
1453 }
1454 
1455 int32_t
1456 ocs_hw_get_num_eq(ocs_hw_t *hw)
1457 {
1458 	return hw->eq_count;
1459 }
1460 
1461 static int32_t
1462 ocs_hw_get_fw_timed_out(ocs_hw_t *hw)
1463 {
1464 	/* The error values below are taken from LOWLEVEL_SET_WATCHDOG_TIMER_rev1.pdf
1465 	* No further explanation is given in the document.
1466 	* */
1467 	return (sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1) == 0x2 &&
1468 		sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2) == 0x10);
1469 }
1470 
1471 ocs_hw_rtn_e
1472 ocs_hw_get(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t *value)
1473 {
1474 	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1475 	int32_t			tmp;
1476 
1477 	if (!value) {
1478 		return OCS_HW_RTN_ERROR;
1479 	}
1480 
1481 	*value = 0;
1482 
1483 	switch (prop) {
1484 	case OCS_HW_N_IO:
1485 		*value = hw->config.n_io;
1486 		break;
1487 	case OCS_HW_N_SGL:
1488 		*value = (hw->config.n_sgl - SLI4_SGE_MAX_RESERVED);
1489 		break;
1490 	case OCS_HW_MAX_IO:
1491 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
1492 		break;
1493 	case OCS_HW_MAX_NODES:
1494 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1495 		break;
1496 	case OCS_HW_MAX_RQ_ENTRIES:
1497 		*value = hw->num_qentries[SLI_QTYPE_RQ];
1498 		break;
1499 	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1500 		*value = hw->config.rq_default_buffer_size;
1501 		break;
1502 	case OCS_HW_AUTO_XFER_RDY_CAPABLE:
1503 		*value = sli_get_auto_xfer_rdy_capable(&hw->sli);
1504 		break;
1505 	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1506 		*value = hw->config.auto_xfer_rdy_xri_cnt;
1507 		break;
1508 	case OCS_HW_AUTO_XFER_RDY_SIZE:
1509 		*value = hw->config.auto_xfer_rdy_size;
1510 		break;
1511 	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1512 		switch (hw->config.auto_xfer_rdy_blk_size_chip) {
1513 		case 0:
1514 			*value = 512;
1515 			break;
1516 		case 1:
1517 			*value = 1024;
1518 			break;
1519 		case 2:
1520 			*value = 2048;
1521 			break;
1522 		case 3:
1523 			*value = 4096;
1524 			break;
1525 		case 4:
1526 			*value = 520;
1527 			break;
1528 		default:
1529 			*value = 0;
1530 			rc = OCS_HW_RTN_ERROR;
1531 			break;
1532 		}
1533 		break;
1534 	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1535 		*value = hw->config.auto_xfer_rdy_t10_enable;
1536 		break;
1537 	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1538 		*value = hw->config.auto_xfer_rdy_p_type;
1539 		break;
1540 	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1541 		*value = hw->config.auto_xfer_rdy_ref_tag_is_lba;
1542 		break;
1543 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1544 		*value = hw->config.auto_xfer_rdy_app_tag_valid;
1545 		break;
1546 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1547 		*value = hw->config.auto_xfer_rdy_app_tag_value;
1548 		break;
1549 	case OCS_HW_MAX_SGE:
1550 		*value = sli_get_max_sge(&hw->sli);
1551 		break;
1552 	case OCS_HW_MAX_SGL:
1553 		*value = sli_get_max_sgl(&hw->sli);
1554 		break;
1555 	case OCS_HW_TOPOLOGY:
1556 		/*
1557 		 * Infer link.status based on link.speed.
1558 		 * Report OCS_HW_TOPOLOGY_NONE if the link is down.
1559 		 */
1560 		if (hw->link.speed == 0) {
1561 			*value = OCS_HW_TOPOLOGY_NONE;
1562 			break;
1563 		}
1564 		switch (hw->link.topology) {
1565 		case SLI_LINK_TOPO_NPORT:
1566 			*value = OCS_HW_TOPOLOGY_NPORT;
1567 			break;
1568 		case SLI_LINK_TOPO_LOOP:
1569 			*value = OCS_HW_TOPOLOGY_LOOP;
1570 			break;
1571 		case SLI_LINK_TOPO_NONE:
1572 			*value = OCS_HW_TOPOLOGY_NONE;
1573 			break;
1574 		default:
1575 			ocs_log_test(hw->os, "unsupported topology %#x\n", hw->link.topology);
1576 			rc = OCS_HW_RTN_ERROR;
1577 			break;
1578 		}
1579 		break;
1580 	case OCS_HW_CONFIG_TOPOLOGY:
1581 		*value = hw->config.topology;
1582 		break;
1583 	case OCS_HW_LINK_SPEED:
1584 		*value = hw->link.speed;
1585 		break;
1586 	case OCS_HW_LINK_CONFIG_SPEED:
1587 		switch (hw->config.speed) {
1588 		case FC_LINK_SPEED_10G:
1589 			*value = 10000;
1590 			break;
1591 		case FC_LINK_SPEED_AUTO_16_8_4:
1592 			*value = 0;
1593 			break;
1594 		case FC_LINK_SPEED_2G:
1595 			*value = 2000;
1596 			break;
1597 		case FC_LINK_SPEED_4G:
1598 			*value = 4000;
1599 			break;
1600 		case FC_LINK_SPEED_8G:
1601 			*value = 8000;
1602 			break;
1603 		case FC_LINK_SPEED_16G:
1604 			*value = 16000;
1605 			break;
1606 		case FC_LINK_SPEED_32G:
1607 			*value = 32000;
1608 			break;
1609 		default:
1610 			ocs_log_test(hw->os, "unsupported speed %#x\n", hw->config.speed);
1611 			rc = OCS_HW_RTN_ERROR;
1612 			break;
1613 		}
1614 		break;
1615 	case OCS_HW_IF_TYPE:
1616 		*value = sli_get_if_type(&hw->sli);
1617 		break;
1618 	case OCS_HW_SLI_REV:
1619 		*value = sli_get_sli_rev(&hw->sli);
1620 		break;
1621 	case OCS_HW_SLI_FAMILY:
1622 		*value = sli_get_sli_family(&hw->sli);
1623 		break;
1624 	case OCS_HW_DIF_CAPABLE:
1625 		*value = sli_get_dif_capable(&hw->sli);
1626 		break;
1627 	case OCS_HW_DIF_SEED:
1628 		*value = hw->config.dif_seed;
1629 		break;
1630 	case OCS_HW_DIF_MODE:
1631 		*value = hw->config.dif_mode;
1632 		break;
1633 	case OCS_HW_DIF_MULTI_SEPARATE:
1634 		/* Lancer supports multiple DIF separates */
1635 		if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
1636 			*value = TRUE;
1637 		} else {
1638 			*value = FALSE;
1639 		}
1640 		break;
1641 	case OCS_HW_DUMP_MAX_SIZE:
1642 		*value = hw->dump_size;
1643 		break;
1644 	case OCS_HW_DUMP_READY:
1645 		*value = sli_dump_is_ready(&hw->sli);
1646 		break;
1647 	case OCS_HW_DUMP_PRESENT:
1648 		*value = sli_dump_is_present(&hw->sli);
1649 		break;
1650 	case OCS_HW_RESET_REQUIRED:
1651 		tmp = sli_reset_required(&hw->sli);
1652 		if(tmp < 0) {
1653 			rc = OCS_HW_RTN_ERROR;
1654 		} else {
1655 			*value = tmp;
1656 		}
1657 		break;
1658 	case OCS_HW_FW_ERROR:
1659 		*value = sli_fw_error_status(&hw->sli);
1660 		break;
1661 	case OCS_HW_FW_READY:
1662 		*value = sli_fw_ready(&hw->sli);
1663 		break;
1664 	case OCS_HW_FW_TIMED_OUT:
1665 		*value = ocs_hw_get_fw_timed_out(hw);
1666 		break;
1667 	case OCS_HW_HIGH_LOGIN_MODE:
1668 		*value = sli_get_hlm_capable(&hw->sli);
1669 		break;
1670 	case OCS_HW_PREREGISTER_SGL:
1671 		*value = sli_get_sgl_preregister_required(&hw->sli);
1672 		break;
1673 	case OCS_HW_HW_REV1:
1674 		*value = sli_get_hw_revision(&hw->sli, 0);
1675 		break;
1676 	case OCS_HW_HW_REV2:
1677 		*value = sli_get_hw_revision(&hw->sli, 1);
1678 		break;
1679 	case OCS_HW_HW_REV3:
1680 		*value = sli_get_hw_revision(&hw->sli, 2);
1681 		break;
1682 	case OCS_HW_LINKCFG:
1683 		*value = hw->linkcfg;
1684 		break;
1685 	case OCS_HW_ETH_LICENSE:
1686 		*value = hw->eth_license;
1687 		break;
1688 	case OCS_HW_LINK_MODULE_TYPE:
1689 		*value = sli_get_link_module_type(&hw->sli);
1690 		break;
1691 	case OCS_HW_NUM_CHUTES:
1692 		*value = ocs_hw_get_num_chutes(hw);
1693 		break;
1694 	case OCS_HW_DISABLE_AR_TGT_DIF:
1695 		*value = hw->workaround.disable_ar_tgt_dif;
1696 		break;
1697 	case OCS_HW_EMULATE_I_ONLY_AAB:
1698 		*value = hw->config.i_only_aab;
1699 		break;
1700 	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
1701 		*value = hw->config.emulate_tgt_wqe_timeout;
1702 		break;
1703 	case OCS_HW_VPD_LEN:
1704 		*value = sli_get_vpd_len(&hw->sli);
1705 		break;
1706 	case OCS_HW_SGL_CHAINING_CAPABLE:
1707 		*value = sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported;
1708 		break;
1709 	case OCS_HW_SGL_CHAINING_ALLOWED:
1710 		/*
1711 		 * SGL Chaining is allowed in the following cases:
1712 		 *   1. Lancer with host SGL Lists
1713 		 *   2. Skyhawk with pre-registered SGL Lists
1714 		 */
1715 		*value = FALSE;
1716 		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1717 		    !sli_get_sgl_preregister(&hw->sli) &&
1718 		    SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)) {
1719 			*value = TRUE;
1720 		}
1721 
1722 		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1723 		    sli_get_sgl_preregister(&hw->sli) &&
1724 		    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
1725 			(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
1726 			*value = TRUE;
1727 		}
1728 		break;
1729 	case OCS_HW_SGL_CHAINING_HOST_ALLOCATED:
1730 		/* Only lancer supports host allocated SGL Chaining buffers. */
1731 		*value = ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1732 			  (SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)));
1733 		break;
1734 	case OCS_HW_SEND_FRAME_CAPABLE:
1735 		if (hw->workaround.ignore_send_frame) {
1736 			*value = 0;
1737 		} else {
1738 			/* Only lancer is capable */
1739 			*value = sli_get_if_type(&hw->sli) == SLI4_IF_TYPE_LANCER_FC_ETH;
1740 		}
1741 		break;
1742 	case OCS_HW_RQ_SELECTION_POLICY:
1743 		*value = hw->config.rq_selection_policy;
1744 		break;
1745 	case OCS_HW_RR_QUANTA:
1746 		*value = hw->config.rr_quanta;
1747 		break;
1748 	case OCS_HW_MAX_VPORTS:
1749 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_VPI);
1750 		break;
1751 	default:
1752 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1753 		rc = OCS_HW_RTN_ERROR;
1754 	}
1755 
1756 	return rc;
1757 }
1758 
1759 void *
1760 ocs_hw_get_ptr(ocs_hw_t *hw, ocs_hw_property_e prop)
1761 {
1762 	void	*rc = NULL;
1763 
1764 	switch (prop) {
1765 	case OCS_HW_WWN_NODE:
1766 		rc = sli_get_wwn_node(&hw->sli);
1767 		break;
1768 	case OCS_HW_WWN_PORT:
1769 		rc = sli_get_wwn_port(&hw->sli);
1770 		break;
1771 	case OCS_HW_VPD:
1772 		/* make sure VPD length is non-zero */
1773 		if (sli_get_vpd_len(&hw->sli)) {
1774 			rc = sli_get_vpd(&hw->sli);
1775 		}
1776 		break;
1777 	case OCS_HW_FW_REV:
1778 		rc = sli_get_fw_name(&hw->sli, 0);
1779 		break;
1780 	case OCS_HW_FW_REV2:
1781 		rc = sli_get_fw_name(&hw->sli, 1);
1782 		break;
1783 	case OCS_HW_IPL:
1784 		rc = sli_get_ipl_name(&hw->sli);
1785 		break;
1786 	case OCS_HW_PORTNUM:
1787 		rc = sli_get_portnum(&hw->sli);
1788 		break;
1789 	case OCS_HW_BIOS_VERSION_STRING:
1790 		rc = sli_get_bios_version_string(&hw->sli);
1791 		break;
1792 	default:
1793 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1794 	}
1795 
1796 	return rc;
1797 }
1798 
1799 ocs_hw_rtn_e
1800 ocs_hw_set(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t value)
1801 {
1802 	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1803 
1804 	switch (prop) {
1805 	case OCS_HW_N_IO:
1806 		if (value > sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI) ||
1807 		    value == 0) {
1808 			ocs_log_test(hw->os, "IO value out of range %d vs %d\n",
1809 					value, sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI));
1810 			rc = OCS_HW_RTN_ERROR;
1811 		} else {
1812 			hw->config.n_io = value;
1813 		}
1814 		break;
1815 	case OCS_HW_N_SGL:
1816 		value += SLI4_SGE_MAX_RESERVED;
1817 		if (value > sli_get_max_sgl(&hw->sli)) {
1818 			ocs_log_test(hw->os, "SGL value out of range %d vs %d\n",
1819 					value, sli_get_max_sgl(&hw->sli));
1820 			rc = OCS_HW_RTN_ERROR;
1821 		} else {
1822 			hw->config.n_sgl = value;
1823 		}
1824 		break;
1825 	case OCS_HW_TOPOLOGY:
1826 		if ((sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) &&
1827 				(value != OCS_HW_TOPOLOGY_AUTO)) {
1828 			ocs_log_test(hw->os, "unsupported topology=%#x medium=%#x\n",
1829 					value, sli_get_medium(&hw->sli));
1830 			rc = OCS_HW_RTN_ERROR;
1831 			break;
1832 		}
1833 
1834 		switch (value) {
1835 		case OCS_HW_TOPOLOGY_AUTO:
1836 			if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
1837 				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC);
1838 			} else {
1839 				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FCOE);
1840 			}
1841 			break;
1842 		case OCS_HW_TOPOLOGY_NPORT:
1843 			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_DA);
1844 			break;
1845 		case OCS_HW_TOPOLOGY_LOOP:
1846 			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_AL);
1847 			break;
1848 		default:
1849 			ocs_log_test(hw->os, "unsupported topology %#x\n", value);
1850 			rc = OCS_HW_RTN_ERROR;
1851 		}
1852 		hw->config.topology = value;
1853 		break;
1854 	case OCS_HW_LINK_SPEED:
1855 		if (sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) {
1856 			switch (value) {
1857 			case 0: 	/* Auto-speed negotiation */
1858 			case 10000:	/* FCoE speed */
1859 				hw->config.speed = FC_LINK_SPEED_10G;
1860 				break;
1861 			default:
1862 				ocs_log_test(hw->os, "unsupported speed=%#x medium=%#x\n",
1863 						value, sli_get_medium(&hw->sli));
1864 				rc = OCS_HW_RTN_ERROR;
1865 			}
1866 			break;
1867 		}
1868 
1869 		switch (value) {
1870 		case 0:		/* Auto-speed negotiation */
1871 			hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
1872 			break;
1873 		case 2000:	/* FC speeds */
1874 			hw->config.speed = FC_LINK_SPEED_2G;
1875 			break;
1876 		case 4000:
1877 			hw->config.speed = FC_LINK_SPEED_4G;
1878 			break;
1879 		case 8000:
1880 			hw->config.speed = FC_LINK_SPEED_8G;
1881 			break;
1882 		case 16000:
1883 			hw->config.speed = FC_LINK_SPEED_16G;
1884 			break;
1885 		case 32000:
1886 			hw->config.speed = FC_LINK_SPEED_32G;
1887 			break;
1888 		default:
1889 			ocs_log_test(hw->os, "unsupported speed %d\n", value);
1890 			rc = OCS_HW_RTN_ERROR;
1891 		}
1892 		break;
1893 	case OCS_HW_DIF_SEED:
1894 		/* Set the DIF seed - only for lancer right now */
1895 		if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
1896 			ocs_log_test(hw->os, "DIF seed not supported for this device\n");
1897 			rc = OCS_HW_RTN_ERROR;
1898 		} else {
1899 			hw->config.dif_seed = value;
1900 		}
1901 		break;
1902 	case OCS_HW_DIF_MODE:
1903 		switch (value) {
1904 		case OCS_HW_DIF_MODE_INLINE:
1905 			/*
1906 			 *  Make sure we support inline DIF.
1907 			 *
1908 			 * Note: Having both bits clear means that we have old
1909 			 *	FW that doesn't set the bits.
1910 			 */
1911 			if (sli_is_dif_inline_capable(&hw->sli)) {
1912 				hw->config.dif_mode = value;
1913 			} else {
1914 				ocs_log_test(hw->os, "chip does not support DIF inline\n");
1915 				rc = OCS_HW_RTN_ERROR;
1916 			}
1917 			break;
1918 		case OCS_HW_DIF_MODE_SEPARATE:
1919 			/* Make sure we support DIF separates. */
1920 			if (sli_is_dif_separate_capable(&hw->sli)) {
1921 				hw->config.dif_mode = value;
1922 			} else {
1923 				ocs_log_test(hw->os, "chip does not support DIF separate\n");
1924 				rc = OCS_HW_RTN_ERROR;
1925 			}
1926 		}
1927 		break;
1928 	case OCS_HW_RQ_PROCESS_LIMIT: {
1929 		hw_rq_t *rq;
1930 		uint32_t i;
1931 
1932 		/* For each hw_rq object, set its parent CQ limit value */
1933 		for (i = 0; i < hw->hw_rq_count; i++) {
1934 			rq = hw->hw_rq[i];
1935 			hw->cq[rq->cq->instance].proc_limit = value;
1936 		}
1937 		break;
1938 	}
1939 	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1940 		hw->config.rq_default_buffer_size = value;
1941 		break;
1942 	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1943 		hw->config.auto_xfer_rdy_xri_cnt = value;
1944 		break;
1945 	case OCS_HW_AUTO_XFER_RDY_SIZE:
1946 		hw->config.auto_xfer_rdy_size = value;
1947 		break;
1948 	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1949 		switch (value) {
1950 		case 512:
1951 			hw->config.auto_xfer_rdy_blk_size_chip = 0;
1952 			break;
1953 		case 1024:
1954 			hw->config.auto_xfer_rdy_blk_size_chip = 1;
1955 			break;
1956 		case 2048:
1957 			hw->config.auto_xfer_rdy_blk_size_chip = 2;
1958 			break;
1959 		case 4096:
1960 			hw->config.auto_xfer_rdy_blk_size_chip = 3;
1961 			break;
1962 		case 520:
1963 			hw->config.auto_xfer_rdy_blk_size_chip = 4;
1964 			break;
1965 		default:
1966 			ocs_log_err(hw->os, "Invalid block size %d\n",
1967 				    value);
1968 			rc = OCS_HW_RTN_ERROR;
1969 		}
1970 		break;
1971 	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1972 		hw->config.auto_xfer_rdy_t10_enable = value;
1973 		break;
1974 	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1975 		hw->config.auto_xfer_rdy_p_type = value;
1976 		break;
1977 	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1978 		hw->config.auto_xfer_rdy_ref_tag_is_lba = value;
1979 		break;
1980 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1981 		hw->config.auto_xfer_rdy_app_tag_valid = value;
1982 		break;
1983 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1984 		hw->config.auto_xfer_rdy_app_tag_value = value;
1985 		break;
1986 	case OCS_ESOC:
1987 		hw->config.esoc = value;
1988 		break;
1989 	case OCS_HW_HIGH_LOGIN_MODE:
1990 		rc = sli_set_hlm(&hw->sli, value);
1991 		break;
1992 	case OCS_HW_PREREGISTER_SGL:
1993 		rc = sli_set_sgl_preregister(&hw->sli, value);
1994 		break;
1995 	case OCS_HW_ETH_LICENSE:
1996 		hw->eth_license = value;
1997 		break;
1998 	case OCS_HW_EMULATE_I_ONLY_AAB:
1999 		hw->config.i_only_aab = value;
2000 		break;
2001 	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
2002 		hw->config.emulate_tgt_wqe_timeout = value;
2003 		break;
2004 	case OCS_HW_BOUNCE:
2005 		hw->config.bounce = value;
2006 		break;
2007 	case OCS_HW_RQ_SELECTION_POLICY:
2008 		hw->config.rq_selection_policy = value;
2009 		break;
2010 	case OCS_HW_RR_QUANTA:
2011 		hw->config.rr_quanta = value;
2012 		break;
2013 	default:
2014 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2015 		rc = OCS_HW_RTN_ERROR;
2016 	}
2017 
2018 	return rc;
2019 }
2020 
2021 ocs_hw_rtn_e
2022 ocs_hw_set_ptr(ocs_hw_t *hw, ocs_hw_property_e prop, void *value)
2023 {
2024 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2025 
2026 	switch (prop) {
2027 	case OCS_HW_WAR_VERSION:
2028 		hw->hw_war_version = value;
2029 		break;
2030 	case OCS_HW_FILTER_DEF: {
2031 		char *p = value;
2032 		uint32_t idx = 0;
2033 
2034 		for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) {
2035 			hw->config.filter_def[idx] = 0;
2036 		}
2037 
2038 		for (idx = 0; (idx < ARRAY_SIZE(hw->config.filter_def)) && (p != NULL) && *p; ) {
2039 			hw->config.filter_def[idx++] = ocs_strtoul(p, 0, 0);
2040 			p = ocs_strchr(p, ',');
2041 			if (p != NULL) {
2042 				p++;
2043 			}
2044 		}
2045 
2046 		break;
2047 	}
2048 	default:
2049 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2050 		rc = OCS_HW_RTN_ERROR;
2051 		break;
2052 	}
2053 	return rc;
2054 }
2055 /**
2056  * @ingroup interrupt
2057  * @brief Check for the events associated with the interrupt vector.
2058  *
2059  * @param hw Hardware context.
2060  * @param vector Zero-based interrupt vector number.
2061  *
2062  * @return Returns 0 on success, or a non-zero value on failure.
2063  */
2064 int32_t
2065 ocs_hw_event_check(ocs_hw_t *hw, uint32_t vector)
2066 {
2067 	int32_t rc = 0;
2068 
2069 	if (!hw) {
2070 		ocs_log_err(NULL, "HW context NULL?!?\n");
2071 		return -1;
2072 	}
2073 
2074 	if (vector > hw->eq_count) {
2075 		ocs_log_err(hw->os, "vector %d. max %d\n",
2076 				vector, hw->eq_count);
2077 		return -1;
2078 	}
2079 
2080 	/*
2081 	 * The caller should disable interrupts if they wish to prevent us
2082 	 * from processing during a shutdown. The following states are defined:
2083 	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2084 	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2085 	 *                                    queues are cleared.
2086 	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2087 	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2088 	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2089 	 *                                        completions.
2090 	 */
2091 	if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
2092 		rc = sli_queue_is_empty(&hw->sli, &hw->eq[vector]);
2093 
2094 		/* Re-arm queue if there are no entries */
2095 		if (rc != 0) {
2096 			sli_queue_arm(&hw->sli, &hw->eq[vector], TRUE);
2097 		}
2098 	}
2099 	return rc;
2100 }
2101 
2102 void
2103 ocs_hw_unsol_process_bounce(void *arg)
2104 {
2105 	ocs_hw_sequence_t *seq = arg;
2106 	ocs_hw_t *hw = seq->hw;
2107 
2108 	ocs_hw_assert(hw != NULL);
2109 	ocs_hw_assert(hw->callback.unsolicited != NULL);
2110 
2111 	hw->callback.unsolicited(hw->args.unsolicited, seq);
2112 }
2113 
2114 int32_t
2115 ocs_hw_process(ocs_hw_t *hw, uint32_t vector, uint32_t max_isr_time_msec)
2116 {
2117 	hw_eq_t *eq;
2118 	int32_t rc = 0;
2119 
2120 	CPUTRACE("");
2121 
2122 	/*
2123 	 * The caller should disable interrupts if they wish to prevent us
2124 	 * from processing during a shutdown. The following states are defined:
2125 	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2126 	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2127 	 *                                    queues are cleared.
2128 	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2129 	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2130 	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2131 	 *                                        completions.
2132 	 */
2133 	if (hw->state == OCS_HW_STATE_UNINITIALIZED) {
2134 		return 0;
2135 	}
2136 
2137 	/* Get pointer to hw_eq_t */
2138 	eq = hw->hw_eq[vector];
2139 
2140 	OCS_STAT(eq->use_count++);
2141 
2142 	rc = ocs_hw_eq_process(hw, eq, max_isr_time_msec);
2143 
2144 	return rc;
2145 }
2146 
2147 /**
2148  * @ingroup interrupt
2149  * @brief Process events associated with an EQ.
2150  *
2151  * @par Description
2152  * Loop termination:
2153  * @n @n Without a mechanism to terminate the completion processing loop, it
2154  * is possible under some workload conditions for the loop to never terminate
2155  * (or at least take longer than the OS is happy to have an interrupt handler
2156  * or kernel thread context hold a CPU without yielding).
2157  * @n @n The approach taken here is to periodically check how much time
2158  * we have been in this
2159  * processing loop, and if we exceed a predetermined time (multiple seconds), the
2160  * loop is terminated, and ocs_hw_process() returns.
2161  *
2162  * @param hw Hardware context.
2163  * @param eq Pointer to HW EQ object.
2164  * @param max_isr_time_msec Maximum time in msec to stay in this function.
2165  *
2166  * @return Returns 0 on success, or a non-zero value on failure.
2167  */
2168 int32_t
2169 ocs_hw_eq_process(ocs_hw_t *hw, hw_eq_t *eq, uint32_t max_isr_time_msec)
2170 {
2171 	uint8_t		eqe[sizeof(sli4_eqe_t)] = { 0 };
2172 	uint32_t	done = FALSE;
2173 	uint32_t	tcheck_count;
2174 	time_t		tstart;
2175 	time_t		telapsed;
2176 
2177 	tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2178 	tstart = ocs_msectime();
2179 
2180 	CPUTRACE("");
2181 
2182 	while (!done && !sli_queue_read(&hw->sli, eq->queue, eqe)) {
2183 		uint16_t	cq_id = 0;
2184 		int32_t		rc;
2185 
2186 		rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
2187 		if (unlikely(rc)) {
2188 			if (rc > 0) {
2189 				uint32_t i;
2190 
2191 				/*
2192 				 * Received a sentinel EQE indicating the EQ is full.
2193 				 * Process all CQs
2194 				 */
2195 				for (i = 0; i < hw->cq_count; i++) {
2196 					ocs_hw_cq_process(hw, hw->hw_cq[i]);
2197 				}
2198 				continue;
2199 			} else {
2200 				return rc;
2201 			}
2202 		} else {
2203 			int32_t index = ocs_hw_queue_hash_find(hw->cq_hash, cq_id);
2204 			if (likely(index >= 0)) {
2205 				ocs_hw_cq_process(hw, hw->hw_cq[index]);
2206 			} else {
2207 				ocs_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
2208 			}
2209 		}
2210 
2211 		if (eq->queue->n_posted > (eq->queue->posted_limit)) {
2212 			sli_queue_arm(&hw->sli, eq->queue, FALSE);
2213 		}
2214 
2215 		if (tcheck_count && (--tcheck_count == 0)) {
2216 			tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2217 			telapsed = ocs_msectime() - tstart;
2218 			if (telapsed >= max_isr_time_msec) {
2219 				done = TRUE;
2220 			}
2221 		}
2222 	}
2223 	sli_queue_eq_arm(&hw->sli, eq->queue, TRUE);
2224 
2225 	return 0;
2226 }
2227 
2228 /**
2229  * @brief Submit queued (pending) mbx commands.
2230  *
2231  * @par Description
2232  * Submit queued mailbox commands.
2233  * --- Assumes that hw->cmd_lock is held ---
2234  *
2235  * @param hw Hardware context.
2236  *
2237  * @return Returns 0 on success, or a negative error code value on failure.
2238  */
2239 static int32_t
2240 ocs_hw_cmd_submit_pending(ocs_hw_t *hw)
2241 {
2242 	ocs_command_ctx_t *ctx;
2243 	int32_t rc = 0;
2244 
2245 	/* Assumes lock held */
2246 
2247 	/* Only submit MQE if there's room */
2248 	while (hw->cmd_head_count < (OCS_HW_MQ_DEPTH - 1)) {
2249 		ctx = ocs_list_remove_head(&hw->cmd_pending);
2250 		if (ctx == NULL) {
2251 			break;
2252 		}
2253 		ocs_list_add_tail(&hw->cmd_head, ctx);
2254 		hw->cmd_head_count++;
2255 		if (sli_queue_write(&hw->sli, hw->mq, ctx->buf) < 0) {
2256 			ocs_log_test(hw->os, "sli_queue_write failed: %d\n", rc);
2257 			rc = -1;
2258 			break;
2259 		}
2260 	}
2261 	return rc;
2262 }
2263 
2264 /**
2265  * @ingroup io
2266  * @brief Issue a SLI command.
2267  *
2268  * @par Description
2269  * Send a mailbox command to the hardware, and either wait for a completion
2270  * (OCS_CMD_POLL) or get an optional asynchronous completion (OCS_CMD_NOWAIT).
2271  *
2272  * @param hw Hardware context.
2273  * @param cmd Buffer containing a formatted command and results.
2274  * @param opts Command options:
2275  *  - OCS_CMD_POLL - Command executes synchronously and busy-waits for the completion.
2276  *  - OCS_CMD_NOWAIT - Command executes asynchronously. Uses callback.
2277  * @param cb Function callback used for asynchronous mode. May be NULL.
2278  * @n Prototype is <tt>(*cb)(void *arg, uint8_t *cmd)</tt>.
2279  * @n @n @b Note: If the
2280  * callback function pointer is NULL, the results of the command are silently
2281  * discarded, allowing this pointer to exist solely on the stack.
2282  * @param arg Argument passed to an asynchronous callback.
2283  *
2284  * @return Returns 0 on success, or a non-zero value on failure.
2285  */
2286 ocs_hw_rtn_e
2287 ocs_hw_command(ocs_hw_t *hw, uint8_t *cmd, uint32_t opts, void *cb, void *arg)
2288 {
2289 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2290 
2291 	/*
2292 	 * If the chip is in an error state (UE'd) then reject this mailbox
2293 	 *  command.
2294 	 */
2295 	if (sli_fw_error_status(&hw->sli) > 0) {
2296 		uint32_t err1 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1);
2297 		uint32_t err2 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2);
2298 		if (hw->expiration_logged == 0 && err1 == 0x2 && err2 == 0x10) {
2299 			hw->expiration_logged = 1;
2300 			ocs_log_crit(hw->os,"Emulex: Heartbeat expired after %d seconds\n",
2301 					hw->watchdog_timeout);
2302 		}
2303 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2304 		ocs_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
2305 			sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_STATUS),
2306 			err1, err2);
2307 
2308 		return OCS_HW_RTN_ERROR;
2309 	}
2310 
2311 	if (OCS_CMD_POLL == opts) {
2312 		ocs_lock(&hw->cmd_lock);
2313 		if (hw->mq->length && !sli_queue_is_empty(&hw->sli, hw->mq)) {
2314 			/*
2315 			 * Can't issue Boot-strap mailbox command with other
2316 			 * mail-queue commands pending as this interaction is
2317 			 * undefined
2318 			 */
2319 			rc = OCS_HW_RTN_ERROR;
2320 		} else {
2321 			void *bmbx = hw->sli.bmbx.virt;
2322 
2323 			ocs_memset(bmbx, 0, SLI4_BMBX_SIZE);
2324 			ocs_memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
2325 
2326 			if (sli_bmbx_command(&hw->sli) == 0) {
2327 				rc = OCS_HW_RTN_SUCCESS;
2328 				ocs_memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
2329 			}
2330 		}
2331 		ocs_unlock(&hw->cmd_lock);
2332 	} else if (OCS_CMD_NOWAIT == opts) {
2333 		ocs_command_ctx_t	*ctx = NULL;
2334 
2335 		ctx = ocs_malloc(hw->os, sizeof(ocs_command_ctx_t), OCS_M_ZERO | OCS_M_NOWAIT);
2336 		if (!ctx) {
2337 			ocs_log_err(hw->os, "can't allocate command context\n");
2338 			return OCS_HW_RTN_NO_RESOURCES;
2339 		}
2340 
2341 		if (hw->state != OCS_HW_STATE_ACTIVE) {
2342 			ocs_log_err(hw->os, "Can't send command, HW state=%d\n", hw->state);
2343 			ocs_free(hw->os, ctx, sizeof(*ctx));
2344 			return OCS_HW_RTN_ERROR;
2345 		}
2346 
2347 		if (cb) {
2348 			ctx->cb = cb;
2349 			ctx->arg = arg;
2350 		}
2351 		ctx->buf = cmd;
2352 		ctx->ctx = hw;
2353 
2354 		ocs_lock(&hw->cmd_lock);
2355 
2356 			/* Add to pending list */
2357 			ocs_list_add_tail(&hw->cmd_pending, ctx);
2358 
2359 			/* Submit as much of the pending list as we can */
2360 			if (ocs_hw_cmd_submit_pending(hw) == 0) {
2361 				rc = OCS_HW_RTN_SUCCESS;
2362 			}
2363 
2364 		ocs_unlock(&hw->cmd_lock);
2365 	}
2366 
2367 	return rc;
2368 }
2369 
2370 /**
2371  * @ingroup devInitShutdown
2372  * @brief Register a callback for the given event.
2373  *
2374  * @param hw Hardware context.
2375  * @param which Event of interest.
2376  * @param func Function to call when the event occurs.
2377  * @param arg Argument passed to the callback function.
2378  *
2379  * @return Returns 0 on success, or a non-zero value on failure.
2380  */
2381 ocs_hw_rtn_e
2382 ocs_hw_callback(ocs_hw_t *hw, ocs_hw_callback_e which, void *func, void *arg)
2383 {
2384 
2385 	if (!hw || !func || (which >= OCS_HW_CB_MAX)) {
2386 		ocs_log_err(NULL, "bad parameter hw=%p which=%#x func=%p\n",
2387 			    hw, which, func);
2388 		return OCS_HW_RTN_ERROR;
2389 	}
2390 
2391 	switch (which) {
2392 	case OCS_HW_CB_DOMAIN:
2393 		hw->callback.domain = func;
2394 		hw->args.domain = arg;
2395 		break;
2396 	case OCS_HW_CB_PORT:
2397 		hw->callback.port = func;
2398 		hw->args.port = arg;
2399 		break;
2400 	case OCS_HW_CB_UNSOLICITED:
2401 		hw->callback.unsolicited = func;
2402 		hw->args.unsolicited = arg;
2403 		break;
2404 	case OCS_HW_CB_REMOTE_NODE:
2405 		hw->callback.rnode = func;
2406 		hw->args.rnode = arg;
2407 		break;
2408 	case OCS_HW_CB_BOUNCE:
2409 		hw->callback.bounce = func;
2410 		hw->args.bounce = arg;
2411 		break;
2412 	default:
2413 		ocs_log_test(hw->os, "unknown callback %#x\n", which);
2414 		return OCS_HW_RTN_ERROR;
2415 	}
2416 
2417 	return OCS_HW_RTN_SUCCESS;
2418 }
2419 
2420 /**
2421  * @ingroup port
2422  * @brief Allocate a port object.
2423  *
2424  * @par Description
2425  * This function allocates a VPI object for the port and stores it in the
2426  * indicator field of the port object.
2427  *
2428  * @param hw Hardware context.
2429  * @param sport SLI port object used to connect to the domain.
2430  * @param domain Domain object associated with this port (may be NULL).
2431  * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
2432  *
2433  * @return Returns 0 on success, or a non-zero value on failure.
2434  */
2435 ocs_hw_rtn_e
2436 ocs_hw_port_alloc(ocs_hw_t *hw, ocs_sli_port_t *sport, ocs_domain_t *domain,
2437 		uint8_t *wwpn)
2438 {
2439 	uint8_t	*cmd = NULL;
2440 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2441 	uint32_t index;
2442 
2443 	sport->indicator = UINT32_MAX;
2444 	sport->hw = hw;
2445 	sport->ctx.app = sport;
2446 	sport->sm_free_req_pending = 0;
2447 
2448 	/*
2449 	 * Check if the chip is in an error state (UE'd) before proceeding.
2450 	 */
2451 	if (sli_fw_error_status(&hw->sli) > 0) {
2452 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2453 		return OCS_HW_RTN_ERROR;
2454 	}
2455 
2456 	if (wwpn) {
2457 		ocs_memcpy(&sport->sli_wwpn, wwpn, sizeof(sport->sli_wwpn));
2458 	}
2459 
2460 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VPI, &sport->indicator, &index)) {
2461 		ocs_log_err(hw->os, "FCOE_VPI allocation failure\n");
2462 		return OCS_HW_RTN_ERROR;
2463 	}
2464 
2465 	if (domain != NULL) {
2466 		ocs_sm_function_t	next = NULL;
2467 
2468 		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2469 		if (!cmd) {
2470 			ocs_log_err(hw->os, "command memory allocation failed\n");
2471 			rc = OCS_HW_RTN_NO_MEMORY;
2472 			goto ocs_hw_port_alloc_out;
2473 		}
2474 
2475 		/* If the WWPN is NULL, fetch the default WWPN and WWNN before
2476 		 * initializing the VPI
2477 		 */
2478 		if (!wwpn) {
2479 			next = __ocs_hw_port_alloc_read_sparm64;
2480 		} else {
2481 			next = __ocs_hw_port_alloc_init_vpi;
2482 		}
2483 
2484 		ocs_sm_transition(&sport->ctx, next, cmd);
2485 	} else if (!wwpn) {
2486 		/* This is the convention for the HW, not SLI */
2487 		ocs_log_test(hw->os, "need WWN for physical port\n");
2488 		rc = OCS_HW_RTN_ERROR;
2489 	} else {
2490 		/* domain NULL and wwpn non-NULL */
2491 		ocs_sm_transition(&sport->ctx, __ocs_hw_port_alloc_init, NULL);
2492 	}
2493 
2494 ocs_hw_port_alloc_out:
2495 	if (rc != OCS_HW_RTN_SUCCESS) {
2496 		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2497 
2498 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
2499 	}
2500 
2501 	return rc;
2502 }
2503 
2504 /**
2505  * @ingroup port
2506  * @brief Attach a physical/virtual SLI port to a domain.
2507  *
2508  * @par Description
2509  * This function registers a previously-allocated VPI with the
2510  * device.
2511  *
2512  * @param hw Hardware context.
2513  * @param sport Pointer to the SLI port object.
2514  * @param fc_id Fibre Channel ID to associate with this port.
2515  *
2516  * @return Returns OCS_HW_RTN_SUCCESS on success, or an error code on failure.
2517  */
2518 ocs_hw_rtn_e
2519 ocs_hw_port_attach(ocs_hw_t *hw, ocs_sli_port_t *sport, uint32_t fc_id)
2520 {
2521 	uint8_t	*buf = NULL;
2522 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2523 
2524 	if (!hw || !sport) {
2525 		ocs_log_err(hw ? hw->os : NULL,
2526 			"bad parameter(s) hw=%p sport=%p\n", hw,
2527 			sport);
2528 		return OCS_HW_RTN_ERROR;
2529 	}
2530 
2531 	/*
2532 	 * Check if the chip is in an error state (UE'd) before proceeding.
2533 	 */
2534 	if (sli_fw_error_status(&hw->sli) > 0) {
2535 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2536 		return OCS_HW_RTN_ERROR;
2537 	}
2538 
2539 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2540 	if (!buf) {
2541 		ocs_log_err(hw->os, "no buffer for command\n");
2542 		return OCS_HW_RTN_NO_MEMORY;
2543 	}
2544 
2545 	sport->fc_id = fc_id;
2546 	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_ATTACH, buf);
2547 	return rc;
2548 }
2549 
2550 /**
2551  * @brief Called when the port control command completes.
2552  *
2553  * @par Description
2554  * We only need to free the mailbox command buffer.
2555  *
2556  * @param hw Hardware context.
2557  * @param status Status field from the mbox completion.
2558  * @param mqe Mailbox response structure.
2559  * @param arg Pointer to a callback function that signals the caller that the command is done.
2560  *
2561  * @return Returns 0.
2562  */
2563 static int32_t
2564 ocs_hw_cb_port_control(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
2565 {
2566 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
2567 	return 0;
2568 }
2569 
2570 /**
2571  * @ingroup port
2572  * @brief Control a port (initialize, shutdown, or set link configuration).
2573  *
2574  * @par Description
2575  * This function controls a port depending on the @c ctrl parameter:
2576  * - @b OCS_HW_PORT_INIT -
2577  * Issues the CONFIG_LINK and INIT_LINK commands for the specified port.
2578  * The HW generates an OCS_HW_DOMAIN_FOUND event when the link comes up.
2579  * .
2580  * - @b OCS_HW_PORT_SHUTDOWN -
2581  * Issues the DOWN_LINK command for the specified port.
2582  * The HW generates an OCS_HW_DOMAIN_LOST event when the link is down.
2583  * .
2584  * - @b OCS_HW_PORT_SET_LINK_CONFIG -
2585  * Sets the link configuration.
2586  *
2587  * @param hw Hardware context.
2588  * @param ctrl Specifies the operation:
2589  * - OCS_HW_PORT_INIT
2590  * - OCS_HW_PORT_SHUTDOWN
2591  * - OCS_HW_PORT_SET_LINK_CONFIG
2592  *
2593  * @param value Operation-specific value.
2594  * - OCS_HW_PORT_INIT - Selective reset AL_PA
2595  * - OCS_HW_PORT_SHUTDOWN - N/A
2596  * - OCS_HW_PORT_SET_LINK_CONFIG - An enum #ocs_hw_linkcfg_e value.
2597  *
2598  * @param cb Callback function to invoke the following operation.
2599  * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2600  * are handled by the OCS_HW_CB_DOMAIN callbacks).
2601  * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2602  * completes.
2603  *
2604  * @param arg Callback argument invoked after the command completes.
2605  * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2606  * are handled by the OCS_HW_CB_DOMAIN callbacks).
2607  * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2608  * completes.
2609  *
2610  * @return Returns 0 on success, or a non-zero value on failure.
2611  */
2612 ocs_hw_rtn_e
2613 ocs_hw_port_control(ocs_hw_t *hw, ocs_hw_port_e ctrl, uintptr_t value, ocs_hw_port_control_cb_t cb, void *arg)
2614 {
2615 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2616 
2617 	switch (ctrl) {
2618 	case OCS_HW_PORT_INIT:
2619 	{
2620 		uint8_t	*init_link;
2621 		uint32_t speed = 0;
2622 		uint8_t reset_alpa = 0;
2623 
2624 		if (SLI_LINK_MEDIUM_FC == sli_get_medium(&hw->sli)) {
2625 			uint8_t	*cfg_link;
2626 
2627 			cfg_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2628 			if (cfg_link == NULL) {
2629 				ocs_log_err(hw->os, "no buffer for command\n");
2630 				return OCS_HW_RTN_NO_MEMORY;
2631 			}
2632 
2633 			if (sli_cmd_config_link(&hw->sli, cfg_link, SLI4_BMBX_SIZE)) {
2634 				rc = ocs_hw_command(hw, cfg_link, OCS_CMD_NOWAIT,
2635 							ocs_hw_cb_port_control, NULL);
2636 			}
2637 
2638 			if (rc != OCS_HW_RTN_SUCCESS) {
2639 				ocs_free(hw->os, cfg_link, SLI4_BMBX_SIZE);
2640 				ocs_log_err(hw->os, "CONFIG_LINK failed\n");
2641 				break;
2642 			}
2643 			speed = hw->config.speed;
2644 			reset_alpa = (uint8_t)(value & 0xff);
2645 		} else {
2646 			speed = FC_LINK_SPEED_10G;
2647 		}
2648 
2649 		/*
2650 		 * Bring link up, unless FW version is not supported
2651 		 */
2652 		if (hw->workaround.fw_version_too_low) {
2653 			if (SLI4_IF_TYPE_LANCER_FC_ETH == hw->sli.if_type) {
2654 				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2655 					OCS_FW_VER_STR(OCS_MIN_FW_VER_LANCER), (char *) sli_get_fw_name(&hw->sli,0));
2656 			} else {
2657 				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2658 					OCS_FW_VER_STR(OCS_MIN_FW_VER_SKYHAWK), (char *) sli_get_fw_name(&hw->sli, 0));
2659 			}
2660 
2661 			return OCS_HW_RTN_ERROR;
2662 		}
2663 
2664 		rc = OCS_HW_RTN_ERROR;
2665 
2666 		/* Allocate a new buffer for the init_link command */
2667 		init_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2668 		if (init_link == NULL) {
2669 			ocs_log_err(hw->os, "no buffer for command\n");
2670 			return OCS_HW_RTN_NO_MEMORY;
2671 		}
2672 
2673 		if (sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE, speed, reset_alpa)) {
2674 			rc = ocs_hw_command(hw, init_link, OCS_CMD_NOWAIT,
2675 						ocs_hw_cb_port_control, NULL);
2676 		}
2677 		/* Free buffer on error, since no callback is coming */
2678 		if (rc != OCS_HW_RTN_SUCCESS) {
2679 			ocs_free(hw->os, init_link, SLI4_BMBX_SIZE);
2680 			ocs_log_err(hw->os, "INIT_LINK failed\n");
2681 		}
2682 		break;
2683 	}
2684 	case OCS_HW_PORT_SHUTDOWN:
2685 	{
2686 		uint8_t	*down_link;
2687 
2688 		down_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2689 		if (down_link == NULL) {
2690 			ocs_log_err(hw->os, "no buffer for command\n");
2691 			return OCS_HW_RTN_NO_MEMORY;
2692 		}
2693 		if (sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE)) {
2694 			rc = ocs_hw_command(hw, down_link, OCS_CMD_NOWAIT,
2695 						ocs_hw_cb_port_control, NULL);
2696 		}
2697 		/* Free buffer on error, since no callback is coming */
2698 		if (rc != OCS_HW_RTN_SUCCESS) {
2699 			ocs_free(hw->os, down_link, SLI4_BMBX_SIZE);
2700 			ocs_log_err(hw->os, "DOWN_LINK failed\n");
2701 		}
2702 		break;
2703 	}
2704 	case OCS_HW_PORT_SET_LINK_CONFIG:
2705 		rc = ocs_hw_set_linkcfg(hw, (ocs_hw_linkcfg_e)value, OCS_CMD_NOWAIT, cb, arg);
2706 		break;
2707 	default:
2708 		ocs_log_test(hw->os, "unhandled control %#x\n", ctrl);
2709 		break;
2710 	}
2711 
2712 	return rc;
2713 }
2714 
2715 /**
2716  * @ingroup port
2717  * @brief Free port resources.
2718  *
2719  * @par Description
2720  * Issue the UNREG_VPI command to free the assigned VPI context.
2721  *
2722  * @param hw Hardware context.
2723  * @param sport SLI port object used to connect to the domain.
2724  *
2725  * @return Returns 0 on success, or a non-zero value on failure.
2726  */
2727 ocs_hw_rtn_e
2728 ocs_hw_port_free(ocs_hw_t *hw, ocs_sli_port_t *sport)
2729 {
2730 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2731 
2732 	if (!hw || !sport) {
2733 		ocs_log_err(hw ? hw->os : NULL,
2734 			"bad parameter(s) hw=%p sport=%p\n", hw,
2735 			sport);
2736 		return OCS_HW_RTN_ERROR;
2737 	}
2738 
2739 	/*
2740 	 * Check if the chip is in an error state (UE'd) before proceeding.
2741 	 */
2742 	if (sli_fw_error_status(&hw->sli) > 0) {
2743 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2744 		return OCS_HW_RTN_ERROR;
2745 	}
2746 
2747 	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_FREE, NULL);
2748 	return rc;
2749 }
2750 
2751 /**
2752  * @ingroup domain
2753  * @brief Allocate a fabric domain object.
2754  *
2755  * @par Description
2756  * This function starts a series of commands needed to connect to the domain, including
2757  *   - REG_FCFI
2758  *   - INIT_VFI
2759  *   - READ_SPARMS
2760  *   .
2761  * @b Note: Not all SLI interface types use all of the above commands.
2762  * @n @n Upon successful allocation, the HW generates a OCS_HW_DOMAIN_ALLOC_OK
2763  * event. On failure, it generates a OCS_HW_DOMAIN_ALLOC_FAIL event.
2764  *
2765  * @param hw Hardware context.
2766  * @param domain Pointer to the domain object.
2767  * @param fcf FCF index.
2768  * @param vlan VLAN ID.
2769  *
2770  * @return Returns 0 on success, or a non-zero value on failure.
2771  */
2772 ocs_hw_rtn_e
2773 ocs_hw_domain_alloc(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fcf, uint32_t vlan)
2774 {
2775 	uint8_t		*cmd = NULL;
2776 	uint32_t	index;
2777 
2778 	if (!hw || !domain || !domain->sport) {
2779 		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p sport=%p\n",
2780 				hw, domain, domain ? domain->sport : NULL);
2781 		return OCS_HW_RTN_ERROR;
2782 	}
2783 
2784 	/*
2785 	 * Check if the chip is in an error state (UE'd) before proceeding.
2786 	 */
2787 	if (sli_fw_error_status(&hw->sli) > 0) {
2788 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2789 		return OCS_HW_RTN_ERROR;
2790 	}
2791 
2792 	cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2793 	if (!cmd) {
2794 		ocs_log_err(hw->os, "command memory allocation failed\n");
2795 		return OCS_HW_RTN_NO_MEMORY;
2796 	}
2797 
2798 	domain->dma = hw->domain_dmem;
2799 
2800 	domain->hw = hw;
2801 	domain->sm.app = domain;
2802 	domain->fcf = fcf;
2803 	domain->fcf_indicator = UINT32_MAX;
2804 	domain->vlan_id = vlan;
2805 	domain->indicator = UINT32_MAX;
2806 
2807 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VFI, &domain->indicator, &index)) {
2808 		ocs_log_err(hw->os, "FCOE_VFI allocation failure\n");
2809 
2810 		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2811 
2812 		return OCS_HW_RTN_ERROR;
2813 	}
2814 
2815 	ocs_sm_transition(&domain->sm, __ocs_hw_domain_init, cmd);
2816 	return OCS_HW_RTN_SUCCESS;
2817 }
2818 
2819 /**
2820  * @ingroup domain
2821  * @brief Attach a SLI port to a domain.
2822  *
2823  * @param hw Hardware context.
2824  * @param domain Pointer to the domain object.
2825  * @param fc_id Fibre Channel ID to associate with this port.
2826  *
2827  * @return Returns 0 on success, or a non-zero value on failure.
2828  */
2829 ocs_hw_rtn_e
2830 ocs_hw_domain_attach(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fc_id)
2831 {
2832 	uint8_t	*buf = NULL;
2833 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2834 
2835 	if (!hw || !domain) {
2836 		ocs_log_err(hw ? hw->os : NULL,
2837 			"bad parameter(s) hw=%p domain=%p\n",
2838 			hw, domain);
2839 		return OCS_HW_RTN_ERROR;
2840 	}
2841 
2842 	/*
2843 	 * Check if the chip is in an error state (UE'd) before proceeding.
2844 	 */
2845 	if (sli_fw_error_status(&hw->sli) > 0) {
2846 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2847 		return OCS_HW_RTN_ERROR;
2848 	}
2849 
2850 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2851 	if (!buf) {
2852 		ocs_log_err(hw->os, "no buffer for command\n");
2853 		return OCS_HW_RTN_NO_MEMORY;
2854 	}
2855 
2856 	domain->sport->fc_id = fc_id;
2857 	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_ATTACH, buf);
2858 	return rc;
2859 }
2860 
2861 /**
2862  * @ingroup domain
2863  * @brief Free a fabric domain object.
2864  *
2865  * @par Description
2866  * Free both the driver and SLI port resources associated with the domain.
2867  *
2868  * @param hw Hardware context.
2869  * @param domain Pointer to the domain object.
2870  *
2871  * @return Returns 0 on success, or a non-zero value on failure.
2872  */
2873 ocs_hw_rtn_e
2874 ocs_hw_domain_free(ocs_hw_t *hw, ocs_domain_t *domain)
2875 {
2876 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2877 
2878 	if (!hw || !domain) {
2879 		ocs_log_err(hw ? hw->os : NULL,
2880 			"bad parameter(s) hw=%p domain=%p\n",
2881 			hw, domain);
2882 		return OCS_HW_RTN_ERROR;
2883 	}
2884 
2885 	/*
2886 	 * Check if the chip is in an error state (UE'd) before proceeding.
2887 	 */
2888 	if (sli_fw_error_status(&hw->sli) > 0) {
2889 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2890 		return OCS_HW_RTN_ERROR;
2891 	}
2892 
2893 	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_FREE, NULL);
2894 	return rc;
2895 }
2896 
2897 /**
2898  * @ingroup domain
2899  * @brief Free a fabric domain object.
2900  *
2901  * @par Description
2902  * Free the driver resources associated with the domain. The difference between
2903  * this call and ocs_hw_domain_free() is that this call assumes resources no longer
2904  * exist on the SLI port, due to a reset or after some error conditions.
2905  *
2906  * @param hw Hardware context.
2907  * @param domain Pointer to the domain object.
2908  *
2909  * @return Returns 0 on success, or a non-zero value on failure.
2910  */
2911 ocs_hw_rtn_e
2912 ocs_hw_domain_force_free(ocs_hw_t *hw, ocs_domain_t *domain)
2913 {
2914 	if (!hw || !domain) {
2915 		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p\n", hw, domain);
2916 		return OCS_HW_RTN_ERROR;
2917 	}
2918 
2919 	sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
2920 
2921 	return OCS_HW_RTN_SUCCESS;
2922 }
2923 
2924 /**
2925  * @ingroup node
2926  * @brief Allocate a remote node object.
2927  *
2928  * @param hw Hardware context.
2929  * @param rnode Allocated remote node object to initialize.
2930  * @param fc_addr FC address of the remote node.
2931  * @param sport SLI port used to connect to remote node.
2932  *
2933  * @return Returns 0 on success, or a non-zero value on failure.
2934  */
2935 ocs_hw_rtn_e
2936 ocs_hw_node_alloc(ocs_hw_t *hw, ocs_remote_node_t *rnode, uint32_t fc_addr,
2937 		ocs_sli_port_t *sport)
2938 {
2939 	/* Check for invalid indicator */
2940 	if (UINT32_MAX != rnode->indicator) {
2941 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x rpi=%#x\n",
2942 				fc_addr, rnode->indicator);
2943 		return OCS_HW_RTN_ERROR;
2944 	}
2945 
2946 	/*
2947 	 * Check if the chip is in an error state (UE'd) before proceeding.
2948 	 */
2949 	if (sli_fw_error_status(&hw->sli) > 0) {
2950 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2951 		return OCS_HW_RTN_ERROR;
2952 	}
2953 
2954 	/* NULL SLI port indicates an unallocated remote node */
2955 	rnode->sport = NULL;
2956 
2957 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &rnode->indicator, &rnode->index)) {
2958 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
2959 				fc_addr);
2960 		return OCS_HW_RTN_ERROR;
2961 	}
2962 
2963 	rnode->fc_id = fc_addr;
2964 	rnode->sport = sport;
2965 
2966 	return OCS_HW_RTN_SUCCESS;
2967 }
2968 
2969 /**
2970  * @ingroup node
2971  * @brief Update a remote node object with the remote port's service parameters.
2972  *
2973  * @param hw Hardware context.
2974  * @param rnode Allocated remote node object to initialize.
2975  * @param sparms DMA buffer containing the remote port's service parameters.
2976  *
2977  * @return Returns 0 on success, or a non-zero value on failure.
2978  */
2979 ocs_hw_rtn_e
2980 ocs_hw_node_attach(ocs_hw_t *hw, ocs_remote_node_t *rnode, ocs_dma_t *sparms)
2981 {
2982 	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
2983 	uint8_t		*buf = NULL;
2984 	uint32_t	count = 0;
2985 
2986 	if (!hw || !rnode || !sparms) {
2987 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p sparms=%p\n",
2988 			    hw, rnode, sparms);
2989 		return OCS_HW_RTN_ERROR;
2990 	}
2991 
2992 	/*
2993 	 * Check if the chip is in an error state (UE'd) before proceeding.
2994 	 */
2995 	if (sli_fw_error_status(&hw->sli) > 0) {
2996 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2997 		return OCS_HW_RTN_ERROR;
2998 	}
2999 
3000 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3001 	if (!buf) {
3002 		ocs_log_err(hw->os, "no buffer for command\n");
3003 		return OCS_HW_RTN_NO_MEMORY;
3004 	}
3005 
3006 	/*
3007 	 * If the attach count is non-zero, this RPI has already been registered.
3008 	 * Otherwise, register the RPI
3009 	 */
3010 	if (rnode->index == UINT32_MAX) {
3011 		ocs_log_err(NULL, "bad parameter rnode->index invalid\n");
3012 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3013 		return OCS_HW_RTN_ERROR;
3014 	}
3015 	count = ocs_atomic_add_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3016 	if (count) {
3017 		/*
3018 		 * Can't attach multiple FC_ID's to a node unless High Login
3019 		 * Mode is enabled
3020 		 */
3021 		if (sli_get_hlm(&hw->sli) == FALSE) {
3022 			ocs_log_test(hw->os, "attach to already attached node HLM=%d count=%d\n",
3023 					sli_get_hlm(&hw->sli), count);
3024 			rc = OCS_HW_RTN_SUCCESS;
3025 		} else {
3026 			rnode->node_group = TRUE;
3027 			rnode->attached = ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_attached);
3028 			rc = rnode->attached  ? OCS_HW_RTN_SUCCESS_SYNC : OCS_HW_RTN_SUCCESS;
3029 		}
3030 	} else {
3031 		rnode->node_group = FALSE;
3032 
3033 		ocs_display_sparams("", "reg rpi", 0, NULL, sparms->virt);
3034 		if (sli_cmd_reg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->fc_id,
3035 					rnode->indicator, rnode->sport->indicator,
3036 					sparms, 0, (hw->auto_xfer_rdy_enabled && hw->config.auto_xfer_rdy_t10_enable))) {
3037 			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT,
3038 					ocs_hw_cb_node_attach, rnode);
3039 		}
3040 	}
3041 
3042 	if (count || rc) {
3043 		if (rc < OCS_HW_RTN_SUCCESS) {
3044 			ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3045 			ocs_log_err(hw->os, "%s error\n", count ? "HLM" : "REG_RPI");
3046 		}
3047 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3048 	}
3049 
3050 	return rc;
3051 }
3052 
3053 /**
3054  * @ingroup node
3055  * @brief Free a remote node resource.
3056  *
3057  * @param hw Hardware context.
3058  * @param rnode Remote node object to free.
3059  *
3060  * @return Returns 0 on success, or a non-zero value on failure.
3061  */
3062 ocs_hw_rtn_e
3063 ocs_hw_node_free_resources(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3064 {
3065 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3066 
3067 	if (!hw || !rnode) {
3068 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3069 			    hw, rnode);
3070 		return OCS_HW_RTN_ERROR;
3071 	}
3072 
3073 	if (rnode->sport) {
3074 		if (!rnode->attached) {
3075 			if (rnode->indicator != UINT32_MAX) {
3076 				if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3077 					ocs_log_err(hw->os, "FCOE_RPI free failure RPI %d addr=%#x\n",
3078 						    rnode->indicator, rnode->fc_id);
3079 					rc = OCS_HW_RTN_ERROR;
3080 				} else {
3081 					rnode->node_group = FALSE;
3082 					rnode->indicator = UINT32_MAX;
3083 					rnode->index = UINT32_MAX;
3084 					rnode->free_group = FALSE;
3085 				}
3086 			}
3087 		} else {
3088 			ocs_log_err(hw->os, "Error: rnode is still attached\n");
3089 			rc = OCS_HW_RTN_ERROR;
3090 		}
3091 	}
3092 
3093 	return rc;
3094 }
3095 
3096 /**
3097  * @ingroup node
3098  * @brief Free a remote node object.
3099  *
3100  * @param hw Hardware context.
3101  * @param rnode Remote node object to free.
3102  *
3103  * @return Returns 0 on success, or a non-zero value on failure.
3104  */
3105 ocs_hw_rtn_e
3106 ocs_hw_node_detach(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3107 {
3108 	uint8_t	*buf = NULL;
3109 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS_SYNC;
3110 	uint32_t	index = UINT32_MAX;
3111 
3112 	if (!hw || !rnode) {
3113 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3114 			    hw, rnode);
3115 		return OCS_HW_RTN_ERROR;
3116 	}
3117 
3118 	/*
3119 	 * Check if the chip is in an error state (UE'd) before proceeding.
3120 	 */
3121 	if (sli_fw_error_status(&hw->sli) > 0) {
3122 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3123 		return OCS_HW_RTN_ERROR;
3124 	}
3125 
3126 	index = rnode->index;
3127 
3128 	if (rnode->sport) {
3129 		uint32_t	count = 0;
3130 		uint32_t	fc_id;
3131 
3132 		if (!rnode->attached) {
3133 			return OCS_HW_RTN_SUCCESS_SYNC;
3134 		}
3135 
3136 		buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3137 		if (!buf) {
3138 			ocs_log_err(hw->os, "no buffer for command\n");
3139 			return OCS_HW_RTN_NO_MEMORY;
3140 		}
3141 
3142 		count = ocs_atomic_sub_return(&hw->rpi_ref[index].rpi_count, 1);
3143 
3144 		if (count <= 1) {
3145 			/* There are no other references to this RPI
3146 			 * so unregister it and free the resource. */
3147 			fc_id = UINT32_MAX;
3148 			rnode->node_group = FALSE;
3149 			rnode->free_group = TRUE;
3150 		} else {
3151 			if (sli_get_hlm(&hw->sli) == FALSE) {
3152 				ocs_log_test(hw->os, "Invalid count with HLM disabled, count=%d\n",
3153 						count);
3154 			}
3155 			fc_id = rnode->fc_id & 0x00ffffff;
3156 		}
3157 
3158 		rc = OCS_HW_RTN_ERROR;
3159 
3160 		if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->indicator,
3161 					SLI_RSRC_FCOE_RPI, fc_id)) {
3162 			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free, rnode);
3163 		}
3164 
3165 		if (rc != OCS_HW_RTN_SUCCESS) {
3166 			ocs_log_err(hw->os, "UNREG_RPI failed\n");
3167 			ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3168 			rc = OCS_HW_RTN_ERROR;
3169 		}
3170 	}
3171 
3172 	return rc;
3173 }
3174 
3175 /**
3176  * @ingroup node
3177  * @brief Free all remote node objects.
3178  *
3179  * @param hw Hardware context.
3180  *
3181  * @return Returns 0 on success, or a non-zero value on failure.
3182  */
3183 ocs_hw_rtn_e
3184 ocs_hw_node_free_all(ocs_hw_t *hw)
3185 {
3186 	uint8_t	*buf = NULL;
3187 	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
3188 
3189 	if (!hw) {
3190 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
3191 		return OCS_HW_RTN_ERROR;
3192 	}
3193 
3194 	/*
3195 	 * Check if the chip is in an error state (UE'd) before proceeding.
3196 	 */
3197 	if (sli_fw_error_status(&hw->sli) > 0) {
3198 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3199 		return OCS_HW_RTN_ERROR;
3200 	}
3201 
3202 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3203 	if (!buf) {
3204 		ocs_log_err(hw->os, "no buffer for command\n");
3205 		return OCS_HW_RTN_NO_MEMORY;
3206 	}
3207 
3208 	if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, 0xffff,
3209 				SLI_RSRC_FCOE_FCFI, UINT32_MAX)) {
3210 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free_all,
3211 				NULL);
3212 	}
3213 
3214 	if (rc != OCS_HW_RTN_SUCCESS) {
3215 		ocs_log_err(hw->os, "UNREG_RPI failed\n");
3216 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3217 		rc = OCS_HW_RTN_ERROR;
3218 	}
3219 
3220 	return rc;
3221 }
3222 
3223 ocs_hw_rtn_e
3224 ocs_hw_node_group_alloc(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3225 {
3226 
3227 	if (!hw || !ngroup) {
3228 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3229 				hw, ngroup);
3230 		return OCS_HW_RTN_ERROR;
3231 	}
3232 
3233 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &ngroup->indicator,
3234 				&ngroup->index)) {
3235 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
3236 				ngroup->indicator);
3237 		return OCS_HW_RTN_ERROR;
3238 	}
3239 
3240 	return OCS_HW_RTN_SUCCESS;
3241 }
3242 
3243 ocs_hw_rtn_e
3244 ocs_hw_node_group_attach(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup, ocs_remote_node_t *rnode)
3245 {
3246 
3247 	if (!hw || !ngroup || !rnode) {
3248 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p rnode=%p\n",
3249 			    hw, ngroup, rnode);
3250 		return OCS_HW_RTN_ERROR;
3251 	}
3252 
3253 	if (rnode->attached) {
3254 		ocs_log_err(hw->os, "node already attached RPI=%#x addr=%#x\n",
3255 			    rnode->indicator, rnode->fc_id);
3256 		return OCS_HW_RTN_ERROR;
3257 	}
3258 
3259 	if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3260 		ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3261 				rnode->indicator);
3262 		return OCS_HW_RTN_ERROR;
3263 	}
3264 
3265 	rnode->indicator = ngroup->indicator;
3266 	rnode->index = ngroup->index;
3267 
3268 	return OCS_HW_RTN_SUCCESS;
3269 }
3270 
3271 ocs_hw_rtn_e
3272 ocs_hw_node_group_free(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3273 {
3274 	int	ref;
3275 
3276 	if (!hw || !ngroup) {
3277 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3278 				hw, ngroup);
3279 		return OCS_HW_RTN_ERROR;
3280 	}
3281 
3282 	ref = ocs_atomic_read(&hw->rpi_ref[ngroup->index].rpi_count);
3283 	if (ref) {
3284 		/* Hmmm, the reference count is non-zero */
3285 		ocs_log_debug(hw->os, "node group reference=%d (RPI=%#x)\n",
3286 				ref, ngroup->indicator);
3287 
3288 		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, ngroup->indicator)) {
3289 			ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3290 				    ngroup->indicator);
3291 			return OCS_HW_RTN_ERROR;
3292 		}
3293 
3294 		ocs_atomic_set(&hw->rpi_ref[ngroup->index].rpi_count, 0);
3295 	}
3296 
3297 	ngroup->indicator = UINT32_MAX;
3298 	ngroup->index = UINT32_MAX;
3299 
3300 	return OCS_HW_RTN_SUCCESS;
3301 }
3302 
3303 /**
3304  * @brief Initialize IO fields on each free call.
3305  *
3306  * @n @b Note: This is done on each free call (as opposed to each
3307  * alloc call) because port-owned XRIs are not
3308  * allocated with ocs_hw_io_alloc() but are freed with this
3309  * function.
3310  *
3311  * @param io Pointer to HW IO.
3312  */
3313 static inline void
3314 ocs_hw_init_free_io(ocs_hw_io_t *io)
3315 {
3316 	/*
3317 	 * Set io->done to NULL, to avoid any callbacks, should
3318 	 * a completion be received for one of these IOs
3319 	 */
3320 	io->done = NULL;
3321 	io->abort_done = NULL;
3322 	io->status_saved = 0;
3323 	io->abort_in_progress = FALSE;
3324 	io->port_owned_abort_count = 0;
3325 	io->rnode = NULL;
3326 	io->type = 0xFFFF;
3327 	io->wq = NULL;
3328 	io->ul_io = NULL;
3329 	io->tgt_wqe_timeout = 0;
3330 }
3331 
3332 /**
3333  * @ingroup io
3334  * @brief Lockless allocate a HW IO object.
3335  *
3336  * @par Description
3337  * Assume that hw->ocs_lock is held. This function is only used if
3338  * use_dif_sec_xri workaround is being used.
3339  *
3340  * @param hw Hardware context.
3341  *
3342  * @return Returns a pointer to an object on success, or NULL on failure.
3343  */
3344 static inline ocs_hw_io_t *
3345 _ocs_hw_io_alloc(ocs_hw_t *hw)
3346 {
3347 	ocs_hw_io_t	*io = NULL;
3348 
3349 	if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
3350 		ocs_list_add_tail(&hw->io_inuse, io);
3351 		io->state = OCS_HW_IO_STATE_INUSE;
3352 		io->quarantine = FALSE;
3353 		io->quarantine_first_phase = TRUE;
3354 		io->abort_reqtag = UINT32_MAX;
3355 		ocs_ref_init(&io->ref, ocs_hw_io_free_internal, io);
3356 	} else {
3357 		ocs_atomic_add_return(&hw->io_alloc_failed_count, 1);
3358 	}
3359 
3360 	return io;
3361 }
3362 /**
3363  * @ingroup io
3364  * @brief Allocate a HW IO object.
3365  *
3366  * @par Description
3367  * @n @b Note: This function applies to non-port owned XRIs
3368  * only.
3369  *
3370  * @param hw Hardware context.
3371  *
3372  * @return Returns a pointer to an object on success, or NULL on failure.
3373  */
3374 ocs_hw_io_t *
3375 ocs_hw_io_alloc(ocs_hw_t *hw)
3376 {
3377 	ocs_hw_io_t	*io = NULL;
3378 
3379 	ocs_lock(&hw->io_lock);
3380 		io = _ocs_hw_io_alloc(hw);
3381 	ocs_unlock(&hw->io_lock);
3382 
3383 	return io;
3384 }
3385 
3386 /**
3387  * @ingroup io
3388  * @brief Allocate/Activate a port owned HW IO object.
3389  *
3390  * @par Description
3391  * This function is called by the transport layer when an XRI is
3392  * allocated by the SLI-Port. This will "activate" the HW IO
3393  * associated with the XRI received from the SLI-Port to mirror
3394  * the state of the XRI.
3395  * @n @n @b Note: This function applies to port owned XRIs only.
3396  *
3397  * @param hw Hardware context.
3398  * @param io Pointer HW IO to activate/allocate.
3399  *
3400  * @return Returns a pointer to an object on success, or NULL on failure.
3401  */
3402 ocs_hw_io_t *
3403 ocs_hw_io_activate_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
3404 {
3405 	if (ocs_ref_read_count(&io->ref) > 0) {
3406 		ocs_log_err(hw->os, "Bad parameter: refcount > 0\n");
3407 		return NULL;
3408 	}
3409 
3410 	if (io->wq != NULL) {
3411 		ocs_log_err(hw->os, "XRI %x already in use\n", io->indicator);
3412 		return NULL;
3413 	}
3414 
3415 	ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3416 	io->xbusy = TRUE;
3417 
3418 	return io;
3419 }
3420 
3421 /**
3422  * @ingroup io
3423  * @brief When an IO is freed, depending on the exchange busy flag, and other
3424  * workarounds, move it to the correct list.
3425  *
3426  * @par Description
3427  * @n @b Note: Assumes that the hw->io_lock is held and the item has been removed
3428  * from the busy or wait_free list.
3429  *
3430  * @param hw Hardware context.
3431  * @param io Pointer to the IO object to move.
3432  */
3433 static void
3434 ocs_hw_io_free_move_correct_list(ocs_hw_t *hw, ocs_hw_io_t *io)
3435 {
3436 	if (io->xbusy) {
3437 		/* add to wait_free list and wait for XRI_ABORTED CQEs to clean up */
3438 		ocs_list_add_tail(&hw->io_wait_free, io);
3439 		io->state = OCS_HW_IO_STATE_WAIT_FREE;
3440 	} else {
3441 		/* IO not busy, add to free list */
3442 		ocs_list_add_tail(&hw->io_free, io);
3443 		io->state = OCS_HW_IO_STATE_FREE;
3444 	}
3445 
3446 	/* BZ 161832 workaround */
3447 	if (hw->workaround.use_dif_sec_xri) {
3448 		ocs_hw_check_sec_hio_list(hw);
3449 	}
3450 }
3451 
3452 /**
3453  * @ingroup io
3454  * @brief Free a HW IO object. Perform cleanup common to
3455  * port and host-owned IOs.
3456  *
3457  * @param hw Hardware context.
3458  * @param io Pointer to the HW IO object.
3459  */
3460 static inline void
3461 ocs_hw_io_free_common(ocs_hw_t *hw, ocs_hw_io_t *io)
3462 {
3463 	/* initialize IO fields */
3464 	ocs_hw_init_free_io(io);
3465 
3466 	/* Restore default SGL */
3467 	ocs_hw_io_restore_sgl(hw, io);
3468 }
3469 
3470 /**
3471  * @ingroup io
3472  * @brief Free a HW IO object associated with a port-owned XRI.
3473  *
3474  * @param arg Pointer to the HW IO object.
3475  */
3476 static void
3477 ocs_hw_io_free_port_owned(void *arg)
3478 {
3479 	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3480 	ocs_hw_t *hw = io->hw;
3481 
3482 	/*
3483 	 * For auto xfer rdy, if the dnrx bit is set, then add it to the list of XRIs
3484 	 * waiting for buffers.
3485 	 */
3486 	if (io->auto_xfer_rdy_dnrx) {
3487 		ocs_lock(&hw->io_lock);
3488 			/* take a reference count because we still own the IO until the buffer is posted */
3489 			ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3490 			ocs_list_add_tail(&hw->io_port_dnrx, io);
3491 		ocs_unlock(&hw->io_lock);
3492 	}
3493 
3494 	/* perform common cleanup */
3495 	ocs_hw_io_free_common(hw, io);
3496 }
3497 
3498 /**
3499  * @ingroup io
3500  * @brief Free a previously-allocated HW IO object. Called when
3501  * IO refcount goes to zero (host-owned IOs only).
3502  *
3503  * @param arg Pointer to the HW IO object.
3504  */
3505 static void
3506 ocs_hw_io_free_internal(void *arg)
3507 {
3508 	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3509 	ocs_hw_t *hw = io->hw;
3510 
3511 	/* perform common cleanup */
3512 	ocs_hw_io_free_common(hw, io);
3513 
3514 	ocs_lock(&hw->io_lock);
3515 		/* remove from in-use list */
3516 		ocs_list_remove(&hw->io_inuse, io);
3517 		ocs_hw_io_free_move_correct_list(hw, io);
3518 	ocs_unlock(&hw->io_lock);
3519 }
3520 
3521 /**
3522  * @ingroup io
3523  * @brief Free a previously-allocated HW IO object.
3524  *
3525  * @par Description
3526  * @n @b Note: This function applies to port and host owned XRIs.
3527  *
3528  * @param hw Hardware context.
3529  * @param io Pointer to the HW IO object.
3530  *
3531  * @return Returns a non-zero value if HW IO was freed, 0 if references
3532  * on the IO still exist, or a negative value if an error occurred.
3533  */
3534 int32_t
3535 ocs_hw_io_free(ocs_hw_t *hw, ocs_hw_io_t *io)
3536 {
3537 	/* just put refcount */
3538 	if (ocs_ref_read_count(&io->ref) <= 0) {
3539 		ocs_log_err(hw->os, "Bad parameter: refcount <= 0 xri=%x tag=%x\n",
3540 			    io->indicator, io->reqtag);
3541 		return -1;
3542 	}
3543 
3544 	return ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_hw_io_alloc() */
3545 }
3546 
3547 /**
3548  * @ingroup io
3549  * @brief Check if given HW IO is in-use
3550  *
3551  * @par Description
3552  * This function returns TRUE if the given HW IO has been
3553  * allocated and is in-use, and FALSE otherwise. It applies to
3554  * port and host owned XRIs.
3555  *
3556  * @param hw Hardware context.
3557  * @param io Pointer to the HW IO object.
3558  *
3559  * @return TRUE if an IO is in use, or FALSE otherwise.
3560  */
3561 uint8_t
3562 ocs_hw_io_inuse(ocs_hw_t *hw, ocs_hw_io_t *io)
3563 {
3564 	return (ocs_ref_read_count(&io->ref) > 0);
3565 }
3566 
3567 /**
3568  * @brief Write a HW IO to a work queue.
3569  *
3570  * @par Description
3571  * A HW IO is written to a work queue.
3572  *
3573  * @param wq Pointer to work queue.
3574  * @param wqe Pointer to WQ entry.
3575  *
3576  * @n @b Note: Assumes the SLI-4 queue lock is held.
3577  *
3578  * @return Returns 0 on success, or a negative error code value on failure.
3579  */
3580 static int32_t
3581 _hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3582 {
3583 	int32_t rc;
3584 	int32_t queue_rc;
3585 
3586 	/* Every so often, set the wqec bit to generate comsummed completions */
3587 	if (wq->wqec_count) {
3588 		wq->wqec_count--;
3589 	}
3590 	if (wq->wqec_count == 0) {
3591 		sli4_generic_wqe_t *genwqe = (void*)wqe->wqebuf;
3592 		genwqe->wqec = 1;
3593 		wq->wqec_count = wq->wqec_set_count;
3594 	}
3595 
3596 	/* Decrement WQ free count */
3597 	wq->free_count--;
3598 
3599 	queue_rc = _sli_queue_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
3600 
3601 	if (queue_rc < 0) {
3602 		rc = -1;
3603 	} else {
3604 		rc = 0;
3605 		ocs_queue_history_wq(&wq->hw->q_hist, (void *) wqe->wqebuf, wq->queue->id, queue_rc);
3606 	}
3607 
3608 	return rc;
3609 }
3610 
3611 /**
3612  * @brief Write a HW IO to a work queue.
3613  *
3614  * @par Description
3615  * A HW IO is written to a work queue.
3616  *
3617  * @param wq Pointer to work queue.
3618  * @param wqe Pointer to WQE entry.
3619  *
3620  * @n @b Note: Takes the SLI-4 queue lock.
3621  *
3622  * @return Returns 0 on success, or a negative error code value on failure.
3623  */
3624 int32_t
3625 hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3626 {
3627 	int32_t rc = 0;
3628 
3629 	sli_queue_lock(wq->queue);
3630 		if ( ! ocs_list_empty(&wq->pending_list)) {
3631 			ocs_list_add_tail(&wq->pending_list, wqe);
3632 			OCS_STAT(wq->wq_pending_count++;)
3633 			while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3634 				rc = _hw_wq_write(wq, wqe);
3635 				if (rc < 0) {
3636 					break;
3637 				}
3638 				if (wqe->abort_wqe_submit_needed) {
3639 					wqe->abort_wqe_submit_needed = 0;
3640 					sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3641 							wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT );
3642 					ocs_list_add_tail(&wq->pending_list, wqe);
3643 					OCS_STAT(wq->wq_pending_count++;)
3644 				}
3645 			}
3646 		} else {
3647 			if (wq->free_count > 0) {
3648 				rc = _hw_wq_write(wq, wqe);
3649 			} else {
3650 				ocs_list_add_tail(&wq->pending_list, wqe);
3651 				OCS_STAT(wq->wq_pending_count++;)
3652 			}
3653 		}
3654 
3655 	sli_queue_unlock(wq->queue);
3656 
3657 	return rc;
3658 
3659 }
3660 
3661 /**
3662  * @brief Update free count and submit any pending HW IOs
3663  *
3664  * @par Description
3665  * The WQ free count is updated, and any pending HW IOs are submitted that
3666  * will fit in the queue.
3667  *
3668  * @param wq Pointer to work queue.
3669  * @param update_free_count Value added to WQs free count.
3670  *
3671  * @return None.
3672  */
3673 static void
3674 hw_wq_submit_pending(hw_wq_t *wq, uint32_t update_free_count)
3675 {
3676 	ocs_hw_wqe_t *wqe;
3677 
3678 	sli_queue_lock(wq->queue);
3679 
3680 		/* Update free count with value passed in */
3681 		wq->free_count += update_free_count;
3682 
3683 		while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3684 			_hw_wq_write(wq, wqe);
3685 
3686 			if (wqe->abort_wqe_submit_needed) {
3687 				wqe->abort_wqe_submit_needed = 0;
3688 				sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3689 						wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT);
3690 				ocs_list_add_tail(&wq->pending_list, wqe);
3691 				OCS_STAT(wq->wq_pending_count++;)
3692 			}
3693 		}
3694 
3695 	sli_queue_unlock(wq->queue);
3696 }
3697 
3698 /**
3699  * @brief Check to see if there are any BZ 161832 workaround waiting IOs
3700  *
3701  * @par Description
3702  * Checks hw->sec_hio_wait_list, if an IO is waiting for a HW IO, then try
3703  * to allocate a secondary HW io, and dispatch it.
3704  *
3705  * @n @b Note: hw->io_lock MUST be taken when called.
3706  *
3707  * @param hw pointer to HW object
3708  *
3709  * @return none
3710  */
3711 static void
3712 ocs_hw_check_sec_hio_list(ocs_hw_t *hw)
3713 {
3714 	ocs_hw_io_t *io;
3715 	ocs_hw_io_t *sec_io;
3716 	int rc = 0;
3717 
3718 	while (!ocs_list_empty(&hw->sec_hio_wait_list)) {
3719 		uint16_t flags;
3720 
3721 		sec_io = _ocs_hw_io_alloc(hw);
3722 		if (sec_io == NULL) {
3723 			break;
3724 		}
3725 
3726 		io = ocs_list_remove_head(&hw->sec_hio_wait_list);
3727 		ocs_list_add_tail(&hw->io_inuse, io);
3728 		io->state = OCS_HW_IO_STATE_INUSE;
3729 		io->sec_hio = sec_io;
3730 
3731 		/* mark secondary XRI for second and subsequent data phase as quarantine */
3732 		if (io->xbusy) {
3733 			sec_io->quarantine = TRUE;
3734 		}
3735 
3736 		flags = io->sec_iparam.fcp_tgt.flags;
3737 		if (io->xbusy) {
3738 			flags |= SLI4_IO_CONTINUATION;
3739 		} else {
3740 			flags &= ~SLI4_IO_CONTINUATION;
3741 		}
3742 
3743 		io->tgt_wqe_timeout = io->sec_iparam.fcp_tgt.timeout;
3744 
3745 		/* Complete (continue) TRECV IO */
3746 		if (io->xbusy) {
3747 			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3748 				io->first_data_sge,
3749 				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator, io->sec_hio->indicator,
3750 				io->reqtag, SLI4_CQ_DEFAULT,
3751 				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3752 				flags,
3753 				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)) {
3754 					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3755 					break;
3756 			}
3757 		} else {
3758 			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3759 				io->first_data_sge,
3760 				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator,
3761 				io->reqtag, SLI4_CQ_DEFAULT,
3762 				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3763 				flags,
3764 				io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size,
3765 				io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3766 					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3767 					break;
3768 			}
3769 		}
3770 
3771 		if (io->wq == NULL) {
3772 			io->wq = ocs_hw_queue_next_wq(hw, io);
3773 			ocs_hw_assert(io->wq != NULL);
3774 		}
3775 		io->xbusy = TRUE;
3776 
3777 		/*
3778 		 * Add IO to active io wqe list before submitting, in case the
3779 		 * wcqe processing preempts this thread.
3780 		 */
3781 		ocs_hw_add_io_timed_wqe(hw, io);
3782 		rc = hw_wq_write(io->wq, &io->wqe);
3783 		if (rc >= 0) {
3784 			/* non-negative return is success */
3785 			rc = 0;
3786 		} else {
3787 			/* failed to write wqe, remove from active wqe list */
3788 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
3789 			io->xbusy = FALSE;
3790 			ocs_hw_remove_io_timed_wqe(hw, io);
3791 		}
3792 	}
3793 }
3794 
3795 /**
3796  * @ingroup io
3797  * @brief Send a Single Request/Response Sequence (SRRS).
3798  *
3799  * @par Description
3800  * This routine supports communication sequences consisting of a single
3801  * request and single response between two endpoints. Examples include:
3802  *  - Sending an ELS request.
3803  *  - Sending an ELS response - To send an ELS reponse, the caller must provide
3804  * the OX_ID from the received request.
3805  *  - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
3806  * the caller must provide the R_CTL, TYPE, and DF_CTL
3807  * values to place in the FC frame header.
3808  *  .
3809  * @n @b Note: The caller is expected to provide both send and receive
3810  * buffers for requests. In the case of sending a response, no receive buffer
3811  * is necessary and the caller may pass in a NULL pointer.
3812  *
3813  * @param hw Hardware context.
3814  * @param type Type of sequence (ELS request/response, FC-CT).
3815  * @param io Previously-allocated HW IO object.
3816  * @param send DMA memory holding data to send (for example, ELS request, BLS response).
3817  * @param len Length, in bytes, of data to send.
3818  * @param receive Optional DMA memory to hold a response.
3819  * @param rnode Destination of data (that is, a remote node).
3820  * @param iparam IO parameters (ELS response and FC-CT).
3821  * @param cb Function call upon completion of sending the data (may be NULL).
3822  * @param arg Argument to pass to IO completion function.
3823  *
3824  * @return Returns 0 on success, or a non-zero on failure.
3825  */
3826 ocs_hw_rtn_e
3827 ocs_hw_srrs_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
3828 		  ocs_dma_t *send, uint32_t len, ocs_dma_t *receive,
3829 		  ocs_remote_node_t *rnode, ocs_hw_io_param_t *iparam,
3830 		  ocs_hw_srrs_cb_t cb, void *arg)
3831 {
3832 	sli4_sge_t	*sge = NULL;
3833 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3834 	uint16_t	local_flags = 0;
3835 
3836 	if (!hw || !io || !rnode || !iparam) {
3837 		ocs_log_err(NULL, "bad parm hw=%p io=%p send=%p receive=%p rnode=%p iparam=%p\n",
3838 			    hw, io, send, receive, rnode, iparam);
3839 		return OCS_HW_RTN_ERROR;
3840 	}
3841 
3842 	if (hw->state != OCS_HW_STATE_ACTIVE) {
3843 		ocs_log_test(hw->os, "cannot send SRRS, HW state=%d\n", hw->state);
3844 		return OCS_HW_RTN_ERROR;
3845 	}
3846 
3847 	if (ocs_hw_is_xri_port_owned(hw, io->indicator)) {
3848 		/* We must set the XC bit for port owned XRIs */
3849 		local_flags |= SLI4_IO_CONTINUATION;
3850 	}
3851 	io->rnode = rnode;
3852 	io->type  = type;
3853 	io->done = cb;
3854 	io->arg  = arg;
3855 
3856 	sge = io->sgl->virt;
3857 
3858 	/* clear both SGE */
3859 	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
3860 
3861 	if (send) {
3862 		sge[0].buffer_address_high = ocs_addr32_hi(send->phys);
3863 		sge[0].buffer_address_low  = ocs_addr32_lo(send->phys);
3864 		sge[0].sge_type = SLI4_SGE_TYPE_DATA;
3865 		sge[0].buffer_length = len;
3866 	}
3867 
3868 	if ((OCS_HW_ELS_REQ == type) || (OCS_HW_FC_CT == type)) {
3869 		sge[1].buffer_address_high = ocs_addr32_hi(receive->phys);
3870 		sge[1].buffer_address_low  = ocs_addr32_lo(receive->phys);
3871 		sge[1].sge_type = SLI4_SGE_TYPE_DATA;
3872 		sge[1].buffer_length = receive->size;
3873 		sge[1].last = TRUE;
3874 	} else {
3875 		sge[0].last = TRUE;
3876 	}
3877 
3878 	switch (type) {
3879 	case OCS_HW_ELS_REQ:
3880 		if ( (!send) || sli_els_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl,
3881 							*((uint8_t *)(send->virt)), /* req_type */
3882 							len, receive->size,
3883 							iparam->els.timeout, io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rnode)) {
3884 			ocs_log_err(hw->os, "REQ WQE error\n");
3885 			rc = OCS_HW_RTN_ERROR;
3886 		}
3887 		break;
3888 	case OCS_HW_ELS_RSP:
3889 		if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3890 					   io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3891 					   iparam->els.ox_id,
3892 							rnode, local_flags, UINT32_MAX)) {
3893 			ocs_log_err(hw->os, "RSP WQE error\n");
3894 			rc = OCS_HW_RTN_ERROR;
3895 		}
3896 		break;
3897 	case OCS_HW_ELS_RSP_SID:
3898 		if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3899 					   io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3900 					   iparam->els_sid.ox_id,
3901 							rnode, local_flags, iparam->els_sid.s_id)) {
3902 			ocs_log_err(hw->os, "RSP (SID) WQE error\n");
3903 			rc = OCS_HW_RTN_ERROR;
3904 		}
3905 		break;
3906 	case OCS_HW_FC_CT:
3907 		if ( (!send) || sli_gen_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3908 					  receive->size, iparam->fc_ct.timeout, io->indicator,
3909 					  io->reqtag, SLI4_CQ_DEFAULT, rnode, iparam->fc_ct.r_ctl,
3910 					  iparam->fc_ct.type, iparam->fc_ct.df_ctl)) {
3911 			ocs_log_err(hw->os, "GEN WQE error\n");
3912 			rc = OCS_HW_RTN_ERROR;
3913 		}
3914 		break;
3915 	case OCS_HW_FC_CT_RSP:
3916 		if ( (!send) || sli_xmit_sequence64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3917 					  iparam->fc_ct_rsp.timeout, iparam->fc_ct_rsp.ox_id, io->indicator,
3918 					  io->reqtag, rnode, iparam->fc_ct_rsp.r_ctl,
3919 					  iparam->fc_ct_rsp.type, iparam->fc_ct_rsp.df_ctl)) {
3920 			ocs_log_err(hw->os, "XMIT SEQ WQE error\n");
3921 			rc = OCS_HW_RTN_ERROR;
3922 		}
3923 		break;
3924 	case OCS_HW_BLS_ACC:
3925 	case OCS_HW_BLS_RJT:
3926 	{
3927 		sli_bls_payload_t	bls;
3928 
3929 		if (OCS_HW_BLS_ACC == type) {
3930 			bls.type = SLI_BLS_ACC;
3931 			ocs_memcpy(&bls.u.acc, iparam->bls.payload, sizeof(bls.u.acc));
3932 		} else {
3933 			bls.type = SLI_BLS_RJT;
3934 			ocs_memcpy(&bls.u.rjt, iparam->bls.payload, sizeof(bls.u.rjt));
3935 		}
3936 
3937 		bls.ox_id = iparam->bls.ox_id;
3938 		bls.rx_id = iparam->bls.rx_id;
3939 
3940 		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3941 					   io->indicator, io->reqtag,
3942 					   SLI4_CQ_DEFAULT,
3943 					   rnode, UINT32_MAX)) {
3944 			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
3945 			rc = OCS_HW_RTN_ERROR;
3946 		}
3947 		break;
3948 	}
3949 	case OCS_HW_BLS_ACC_SID:
3950 	{
3951 		sli_bls_payload_t	bls;
3952 
3953 		bls.type = SLI_BLS_ACC;
3954 		ocs_memcpy(&bls.u.acc, iparam->bls_sid.payload, sizeof(bls.u.acc));
3955 
3956 		bls.ox_id = iparam->bls_sid.ox_id;
3957 		bls.rx_id = iparam->bls_sid.rx_id;
3958 
3959 		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3960 					   io->indicator, io->reqtag,
3961 					   SLI4_CQ_DEFAULT,
3962 					   rnode, iparam->bls_sid.s_id)) {
3963 			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE SID error\n");
3964 			rc = OCS_HW_RTN_ERROR;
3965 		}
3966 		break;
3967 	}
3968 	case OCS_HW_BCAST:
3969 		if ( (!send) || sli_xmit_bcast64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3970 					iparam->bcast.timeout, io->indicator, io->reqtag,
3971 					SLI4_CQ_DEFAULT, rnode,
3972 					iparam->bcast.r_ctl, iparam->bcast.type, iparam->bcast.df_ctl)) {
3973 			ocs_log_err(hw->os, "XMIT_BCAST64 WQE error\n");
3974 			rc = OCS_HW_RTN_ERROR;
3975 		}
3976 		break;
3977 	default:
3978 		ocs_log_err(hw->os, "bad SRRS type %#x\n", type);
3979 		rc = OCS_HW_RTN_ERROR;
3980 	}
3981 
3982 	if (OCS_HW_RTN_SUCCESS == rc) {
3983 		if (io->wq == NULL) {
3984 			io->wq = ocs_hw_queue_next_wq(hw, io);
3985 			ocs_hw_assert(io->wq != NULL);
3986 		}
3987 		io->xbusy = TRUE;
3988 
3989 		/*
3990 		 * Add IO to active io wqe list before submitting, in case the
3991 		 * wcqe processing preempts this thread.
3992 		 */
3993 		OCS_STAT(io->wq->use_count++);
3994 		ocs_hw_add_io_timed_wqe(hw, io);
3995 		rc = hw_wq_write(io->wq, &io->wqe);
3996 		if (rc >= 0) {
3997 			/* non-negative return is success */
3998 			rc = 0;
3999 		} else {
4000 			/* failed to write wqe, remove from active wqe list */
4001 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4002 			io->xbusy = FALSE;
4003 			ocs_hw_remove_io_timed_wqe(hw, io);
4004 		}
4005 	}
4006 
4007 	return rc;
4008 }
4009 
4010 /**
4011  * @ingroup io
4012  * @brief Send a read, write, or response IO.
4013  *
4014  * @par Description
4015  * This routine supports sending a higher-level IO (for example, FCP) between two endpoints
4016  * as a target or initiator. Examples include:
4017  *  - Sending read data and good response (target).
4018  *  - Sending a response (target with no data or after receiving write data).
4019  *  .
4020  * This routine assumes all IOs use the SGL associated with the HW IO. Prior to
4021  * calling this routine, the data should be loaded using ocs_hw_io_add_sge().
4022  *
4023  * @param hw Hardware context.
4024  * @param type Type of IO (target read, target response, and so on).
4025  * @param io Previously-allocated HW IO object.
4026  * @param len Length, in bytes, of data to send.
4027  * @param iparam IO parameters.
4028  * @param rnode Destination of data (that is, a remote node).
4029  * @param cb Function call upon completion of sending data (may be NULL).
4030  * @param arg Argument to pass to IO completion function.
4031  *
4032  * @return Returns 0 on success, or a non-zero value on failure.
4033  *
4034  * @todo
4035  *  - Support specifiying relative offset.
4036  *  - Use a WQ other than 0.
4037  */
4038 ocs_hw_rtn_e
4039 ocs_hw_io_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
4040 		uint32_t len, ocs_hw_io_param_t *iparam, ocs_remote_node_t *rnode,
4041 		void *cb, void *arg)
4042 {
4043 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4044 	uint32_t	rpi;
4045 	uint8_t		send_wqe = TRUE;
4046 
4047 	CPUTRACE("");
4048 
4049 	if (!hw || !io || !rnode || !iparam) {
4050 		ocs_log_err(NULL, "bad parm hw=%p io=%p iparam=%p rnode=%p\n",
4051 			    hw, io, iparam, rnode);
4052 		return OCS_HW_RTN_ERROR;
4053 	}
4054 
4055 	if (hw->state != OCS_HW_STATE_ACTIVE) {
4056 		ocs_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
4057 		return OCS_HW_RTN_ERROR;
4058 	}
4059 
4060 	rpi = rnode->indicator;
4061 
4062 	if (hw->workaround.use_unregistered_rpi && (rpi == UINT32_MAX)) {
4063 		rpi = hw->workaround.unregistered_rid;
4064 		ocs_log_test(hw->os, "using unregistered RPI: %d\n", rpi);
4065 	}
4066 
4067 	/*
4068 	 * Save state needed during later stages
4069 	 */
4070 	io->rnode = rnode;
4071 	io->type  = type;
4072 	io->done  = cb;
4073 	io->arg   = arg;
4074 
4075 	/*
4076 	 * Format the work queue entry used to send the IO
4077 	 */
4078 	switch (type) {
4079 	case OCS_HW_IO_INITIATOR_READ:
4080 		/*
4081 		 * If use_dif_quarantine workaround is in effect, and dif_separates then mark the
4082 		 * initiator read IO for quarantine
4083 		 */
4084 		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4085 		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4086 			io->quarantine = TRUE;
4087 		}
4088 
4089 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4090 				iparam->fcp_ini.rsp);
4091 
4092 		if (sli_fcp_iread64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge, len,
4093 					io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rpi, rnode,
4094 					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4095 					iparam->fcp_ini.timeout)) {
4096 			ocs_log_err(hw->os, "IREAD WQE error\n");
4097 			rc = OCS_HW_RTN_ERROR;
4098 		}
4099 		break;
4100 	case OCS_HW_IO_INITIATOR_WRITE:
4101 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4102 				iparam->fcp_ini.rsp);
4103 
4104 		if (sli_fcp_iwrite64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4105 					 len, iparam->fcp_ini.first_burst,
4106 					 io->indicator, io->reqtag,
4107 					SLI4_CQ_DEFAULT, rpi, rnode,
4108 					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4109 					iparam->fcp_ini.timeout)) {
4110 			ocs_log_err(hw->os, "IWRITE WQE error\n");
4111 			rc = OCS_HW_RTN_ERROR;
4112 		}
4113 		break;
4114 	case OCS_HW_IO_INITIATOR_NODATA:
4115 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4116 				iparam->fcp_ini.rsp);
4117 
4118 		if (sli_fcp_icmnd64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
4119 					io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
4120 					rpi, rnode, iparam->fcp_ini.timeout)) {
4121 			ocs_log_err(hw->os, "ICMND WQE error\n");
4122 			rc = OCS_HW_RTN_ERROR;
4123 		}
4124 		break;
4125 	case OCS_HW_IO_TARGET_WRITE: {
4126 		uint16_t flags = iparam->fcp_tgt.flags;
4127 		fcp_xfer_rdy_iu_t *xfer = io->xfer_rdy.virt;
4128 
4129 		/*
4130 		 * Fill in the XFER_RDY for IF_TYPE 0 devices
4131 		 */
4132 		*((uint32_t *)xfer->fcp_data_ro) = ocs_htobe32(iparam->fcp_tgt.offset);
4133 		*((uint32_t *)xfer->fcp_burst_len) = ocs_htobe32(len);
4134 		*((uint32_t *)xfer->rsvd) = 0;
4135 
4136 		if (io->xbusy) {
4137 			flags |= SLI4_IO_CONTINUATION;
4138 		} else {
4139 			flags &= ~SLI4_IO_CONTINUATION;
4140 		}
4141 
4142 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4143 
4144 		/*
4145 		 * If use_dif_quarantine workaround is in effect, and this is a DIF enabled IO
4146 		 * then mark the target write IO for quarantine
4147 		 */
4148 		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4149 		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4150 			io->quarantine = TRUE;
4151 		}
4152 
4153 		/*
4154 		 * BZ 161832 Workaround:
4155 		 * Check for use_dif_sec_xri workaround.  Note, even though the first dataphase
4156 		 * doesn't really need a secondary XRI, we allocate one anyway, as this avoids the
4157 		 * potential for deadlock where all XRI's are allocated as primaries to IOs that
4158 		 * are on hw->sec_hio_wait_list.   If this secondary XRI is not for the first
4159 		 * data phase, it is marked for quarantine.
4160 		 */
4161 		if (hw->workaround.use_dif_sec_xri && (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4162 			/*
4163 			 * If we have allocated a chained SGL for skyhawk, then
4164 			 * we can re-use this for the sec_hio.
4165 			 */
4166 			if (io->ovfl_io != NULL) {
4167 				io->sec_hio = io->ovfl_io;
4168 				io->sec_hio->quarantine = TRUE;
4169 			} else {
4170 				io->sec_hio = ocs_hw_io_alloc(hw);
4171 			}
4172 			if (io->sec_hio == NULL) {
4173 				/* Failed to allocate, so save full request context and put
4174 				 * this IO on the wait list
4175 				 */
4176 				io->sec_iparam = *iparam;
4177 				io->sec_len = len;
4178 				ocs_lock(&hw->io_lock);
4179 					ocs_list_remove(&hw->io_inuse,  io);
4180 					ocs_list_add_tail(&hw->sec_hio_wait_list, io);
4181 					io->state = OCS_HW_IO_STATE_WAIT_SEC_HIO;
4182 					hw->sec_hio_wait_count++;
4183 				ocs_unlock(&hw->io_lock);
4184 				send_wqe = FALSE;
4185 				/* Done */
4186 				break;
4187 			}
4188 			/* We quarantine the secondary IO if this is the second or subsequent data phase */
4189 			if (io->xbusy) {
4190 				io->sec_hio->quarantine = TRUE;
4191 			}
4192 		}
4193 
4194 		/*
4195 		 * If not the first data phase, and io->sec_hio has been allocated, then issue
4196 		 * FCP_CONT_TRECEIVE64 WQE, otherwise use the usual FCP_TRECEIVE64 WQE
4197 		 */
4198 		if (io->xbusy && (io->sec_hio != NULL)) {
4199 			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4200 						   iparam->fcp_tgt.offset, len, io->indicator, io->sec_hio->indicator,
4201 						   io->reqtag, SLI4_CQ_DEFAULT,
4202 						   iparam->fcp_tgt.ox_id, rpi, rnode,
4203 						   flags,
4204 						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4205 						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4206 				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4207 				rc = OCS_HW_RTN_ERROR;
4208 			}
4209 		} else {
4210 			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4211 						   iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4212 						   SLI4_CQ_DEFAULT,
4213 						   iparam->fcp_tgt.ox_id, rpi, rnode,
4214 						   flags,
4215 						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4216 						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4217 				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4218 				rc = OCS_HW_RTN_ERROR;
4219 			}
4220 		}
4221 		break;
4222 	}
4223 	case OCS_HW_IO_TARGET_READ: {
4224 		uint16_t flags = iparam->fcp_tgt.flags;
4225 
4226 		if (io->xbusy) {
4227 			flags |= SLI4_IO_CONTINUATION;
4228 		} else {
4229 			flags &= ~SLI4_IO_CONTINUATION;
4230 		}
4231 
4232 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4233 		if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4234 					iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4235 					SLI4_CQ_DEFAULT,
4236 					iparam->fcp_tgt.ox_id, rpi, rnode,
4237 					flags,
4238 					iparam->fcp_tgt.dif_oper,
4239 					iparam->fcp_tgt.blk_size,
4240 					iparam->fcp_tgt.cs_ctl,
4241 					iparam->fcp_tgt.app_id)) {
4242 			ocs_log_err(hw->os, "TSEND WQE error\n");
4243 			rc = OCS_HW_RTN_ERROR;
4244 		} else if (hw->workaround.retain_tsend_io_length) {
4245 			io->length = len;
4246 		}
4247 		break;
4248 	}
4249 	case OCS_HW_IO_TARGET_RSP: {
4250 		uint16_t flags = iparam->fcp_tgt.flags;
4251 
4252 		if (io->xbusy) {
4253 			flags |= SLI4_IO_CONTINUATION;
4254 		} else {
4255 			flags &= ~SLI4_IO_CONTINUATION;
4256 		}
4257 
4258 		/* post a new auto xfer ready buffer */
4259 		if (hw->auto_xfer_rdy_enabled && io->is_port_owned) {
4260 			if ((io->auto_xfer_rdy_dnrx = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1))) {
4261 				flags |= SLI4_IO_DNRX;
4262 			}
4263 		}
4264 
4265 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4266 		if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size,
4267 					&io->def_sgl,
4268 					len,
4269 					io->indicator, io->reqtag,
4270 					SLI4_CQ_DEFAULT,
4271 					iparam->fcp_tgt.ox_id,
4272 					rpi, rnode,
4273 					flags, iparam->fcp_tgt.cs_ctl,
4274 					io->is_port_owned,
4275 					iparam->fcp_tgt.app_id)) {
4276 			ocs_log_err(hw->os, "TRSP WQE error\n");
4277 			rc = OCS_HW_RTN_ERROR;
4278 		}
4279 
4280 		break;
4281 	}
4282 	default:
4283 		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4284 		rc = OCS_HW_RTN_ERROR;
4285 	}
4286 
4287 	if (send_wqe && (OCS_HW_RTN_SUCCESS == rc)) {
4288 		if (io->wq == NULL) {
4289 			io->wq = ocs_hw_queue_next_wq(hw, io);
4290 			ocs_hw_assert(io->wq != NULL);
4291 		}
4292 
4293 		io->xbusy = TRUE;
4294 
4295 		/*
4296 		 * Add IO to active io wqe list before submitting, in case the
4297 		 * wcqe processing preempts this thread.
4298 		 */
4299 		OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
4300 		OCS_STAT(io->wq->use_count++);
4301 		ocs_hw_add_io_timed_wqe(hw, io);
4302 		rc = hw_wq_write(io->wq, &io->wqe);
4303 		if (rc >= 0) {
4304 			/* non-negative return is success */
4305 			rc = 0;
4306 		} else {
4307 			/* failed to write wqe, remove from active wqe list */
4308 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4309 			io->xbusy = FALSE;
4310 			ocs_hw_remove_io_timed_wqe(hw, io);
4311 		}
4312 	}
4313 
4314 	return rc;
4315 }
4316 
4317 /**
4318  * @brief Send a raw frame
4319  *
4320  * @par Description
4321  * Using the SEND_FRAME_WQE, a frame consisting of header and payload is sent.
4322  *
4323  * @param hw Pointer to HW object.
4324  * @param hdr Pointer to a little endian formatted FC header.
4325  * @param sof Value to use as the frame SOF.
4326  * @param eof Value to use as the frame EOF.
4327  * @param payload Pointer to payload DMA buffer.
4328  * @param ctx Pointer to caller provided send frame context.
4329  * @param callback Callback function.
4330  * @param arg Callback function argument.
4331  *
4332  * @return Returns 0 on success, or a negative error code value on failure.
4333  */
4334 ocs_hw_rtn_e
4335 ocs_hw_send_frame(ocs_hw_t *hw, fc_header_le_t *hdr, uint8_t sof, uint8_t eof, ocs_dma_t *payload,
4336 		   ocs_hw_send_frame_context_t *ctx, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
4337 {
4338 	int32_t rc;
4339 	ocs_hw_wqe_t *wqe;
4340 	uint32_t xri;
4341 	hw_wq_t *wq;
4342 
4343 	wqe = &ctx->wqe;
4344 
4345 	/* populate the callback object */
4346 	ctx->hw = hw;
4347 
4348 	/* Fetch and populate request tag */
4349 	ctx->wqcb = ocs_hw_reqtag_alloc(hw, callback, arg);
4350 	if (ctx->wqcb == NULL) {
4351 		ocs_log_err(hw->os, "can't allocate request tag\n");
4352 		return OCS_HW_RTN_NO_RESOURCES;
4353 	}
4354 
4355 	/* Choose a work queue, first look for a class[1] wq, otherwise just use wq[0] */
4356 	wq = ocs_varray_iter_next(hw->wq_class_array[1]);
4357 	if (wq == NULL) {
4358 		wq = hw->hw_wq[0];
4359 	}
4360 
4361 	/* Set XRI and RX_ID in the header based on which WQ, and which send_frame_io we are using */
4362 	xri = wq->send_frame_io->indicator;
4363 
4364 	/* Build the send frame WQE */
4365 	rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, hw->sli.config.wqe_size, sof, eof, (uint32_t*) hdr, payload,
4366 				payload->len, OCS_HW_SEND_FRAME_TIMEOUT, xri, ctx->wqcb->instance_index);
4367 	if (rc) {
4368 		ocs_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
4369 		return OCS_HW_RTN_ERROR;
4370 	}
4371 
4372 	/* Write to WQ */
4373 	rc = hw_wq_write(wq, wqe);
4374 	if (rc) {
4375 		ocs_log_err(hw->os, "hw_wq_write failed: %d\n", rc);
4376 		return OCS_HW_RTN_ERROR;
4377 	}
4378 
4379 	OCS_STAT(wq->use_count++);
4380 
4381 	return OCS_HW_RTN_SUCCESS;
4382 }
4383 
4384 ocs_hw_rtn_e
4385 ocs_hw_io_register_sgl(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *sgl, uint32_t sgl_count)
4386 {
4387 	if (sli_get_sgl_preregister(&hw->sli)) {
4388 		ocs_log_err(hw->os, "can't use temporary SGL with pre-registered SGLs\n");
4389 		return OCS_HW_RTN_ERROR;
4390 	}
4391 	io->ovfl_sgl = sgl;
4392 	io->ovfl_sgl_count = sgl_count;
4393 	io->ovfl_io = NULL;
4394 
4395 	return OCS_HW_RTN_SUCCESS;
4396 }
4397 
4398 static void
4399 ocs_hw_io_restore_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4400 {
4401 	/* Restore the default */
4402 	io->sgl = &io->def_sgl;
4403 	io->sgl_count = io->def_sgl_count;
4404 
4405 	/*
4406 	 * For skyhawk, we need to free the IO allocated for the chained
4407 	 * SGL. For all devices, clear the overflow fields on the IO.
4408 	 *
4409 	 * Note: For DIF IOs, we may be using the same XRI for the sec_hio and
4410 	 *       the chained SGLs. If so, then we clear the ovfl_io field
4411 	 *       when the sec_hio is freed.
4412 	 */
4413 	if (io->ovfl_io != NULL) {
4414 		ocs_hw_io_free(hw, io->ovfl_io);
4415 		io->ovfl_io = NULL;
4416 	}
4417 
4418 	/* Clear the overflow SGL */
4419 	io->ovfl_sgl = NULL;
4420 	io->ovfl_sgl_count = 0;
4421 	io->ovfl_lsp = NULL;
4422 }
4423 
4424 /**
4425  * @ingroup io
4426  * @brief Initialize the scatter gather list entries of an IO.
4427  *
4428  * @param hw Hardware context.
4429  * @param io Previously-allocated HW IO object.
4430  * @param type Type of IO (target read, target response, and so on).
4431  *
4432  * @return Returns 0 on success, or a non-zero value on failure.
4433  */
4434 ocs_hw_rtn_e
4435 ocs_hw_io_init_sges(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_io_type_e type)
4436 {
4437 	sli4_sge_t	*data = NULL;
4438 	uint32_t	i = 0;
4439 	uint32_t	skips = 0;
4440 
4441 	if (!hw || !io) {
4442 		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p\n",
4443 			    hw, io);
4444 		return OCS_HW_RTN_ERROR;
4445 	}
4446 
4447 	/* Clear / reset the scatter-gather list */
4448 	io->sgl = &io->def_sgl;
4449 	io->sgl_count = io->def_sgl_count;
4450 	io->first_data_sge = 0;
4451 
4452 	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
4453 	io->n_sge = 0;
4454 	io->sge_offset = 0;
4455 
4456 	io->type = type;
4457 
4458 	data = io->sgl->virt;
4459 
4460 	/*
4461 	 * Some IO types have underlying hardware requirements on the order
4462 	 * of SGEs. Process all special entries here.
4463 	 */
4464 	switch (type) {
4465 	case OCS_HW_IO_INITIATOR_READ:
4466 	case OCS_HW_IO_INITIATOR_WRITE:
4467 	case OCS_HW_IO_INITIATOR_NODATA:
4468 		/*
4469 		 * No skips, 2 special for initiator I/Os
4470 		 * The addresses and length are written later
4471 		 */
4472 		/* setup command pointer */
4473 		data->sge_type = SLI4_SGE_TYPE_DATA;
4474 		data++;
4475 
4476 		/* setup response pointer */
4477 		data->sge_type = SLI4_SGE_TYPE_DATA;
4478 
4479 		if (OCS_HW_IO_INITIATOR_NODATA == type) {
4480 			data->last = TRUE;
4481 		}
4482 		data++;
4483 
4484 		io->n_sge = 2;
4485 		break;
4486 	case OCS_HW_IO_TARGET_WRITE:
4487 #define OCS_TARGET_WRITE_SKIPS	2
4488 		skips = OCS_TARGET_WRITE_SKIPS;
4489 
4490 		/* populate host resident XFER_RDY buffer */
4491 		data->sge_type = SLI4_SGE_TYPE_DATA;
4492 		data->buffer_address_high = ocs_addr32_hi(io->xfer_rdy.phys);
4493 		data->buffer_address_low  = ocs_addr32_lo(io->xfer_rdy.phys);
4494 		data->buffer_length = io->xfer_rdy.size;
4495 		data++;
4496 
4497 		skips--;
4498 
4499 		io->n_sge = 1;
4500 		break;
4501 	case OCS_HW_IO_TARGET_READ:
4502 		/*
4503 		 * For FCP_TSEND64, the first 2 entries are SKIP SGE's
4504 		 */
4505 #define OCS_TARGET_READ_SKIPS	2
4506 		skips = OCS_TARGET_READ_SKIPS;
4507 		break;
4508 	case OCS_HW_IO_TARGET_RSP:
4509 		/*
4510 		 * No skips, etc. for FCP_TRSP64
4511 		 */
4512 		break;
4513 	default:
4514 		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4515 		return OCS_HW_RTN_ERROR;
4516 	}
4517 
4518 	/*
4519 	 * Write skip entries
4520 	 */
4521 	for (i = 0; i < skips; i++) {
4522 		data->sge_type = SLI4_SGE_TYPE_SKIP;
4523 		data++;
4524 	}
4525 
4526 	io->n_sge += skips;
4527 
4528 	/*
4529 	 * Set last
4530 	 */
4531 	data->last = TRUE;
4532 
4533 	return OCS_HW_RTN_SUCCESS;
4534 }
4535 
4536 /**
4537  * @ingroup io
4538  * @brief Add a T10 PI seed scatter gather list entry.
4539  *
4540  * @param hw Hardware context.
4541  * @param io Previously-allocated HW IO object.
4542  * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
4543  *
4544  * @return Returns 0 on success, or a non-zero value on failure.
4545  */
4546 ocs_hw_rtn_e
4547 ocs_hw_io_add_seed_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_dif_info_t *dif_info)
4548 {
4549 	sli4_sge_t	*data = NULL;
4550 	sli4_diseed_sge_t *dif_seed;
4551 
4552 	/* If no dif_info, or dif_oper is disabled, then just return success */
4553 	if ((dif_info == NULL) || (dif_info->dif_oper == OCS_HW_DIF_OPER_DISABLED)) {
4554 		return OCS_HW_RTN_SUCCESS;
4555 	}
4556 
4557 	if (!hw || !io) {
4558 		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p dif_info=%p\n",
4559 			    hw, io, dif_info);
4560 		return OCS_HW_RTN_ERROR;
4561 	}
4562 
4563 	data = io->sgl->virt;
4564 	data += io->n_sge;
4565 
4566 	/* If we are doing T10 DIF add the DIF Seed SGE */
4567 	ocs_memset(data, 0, sizeof(sli4_diseed_sge_t));
4568 	dif_seed = (sli4_diseed_sge_t *)data;
4569 	dif_seed->ref_tag_cmp = dif_info->ref_tag_cmp;
4570 	dif_seed->ref_tag_repl = dif_info->ref_tag_repl;
4571 	dif_seed->app_tag_repl = dif_info->app_tag_repl;
4572 	dif_seed->repl_app_tag = dif_info->repl_app_tag;
4573 	if (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) {
4574 		dif_seed->atrt = dif_info->disable_app_ref_ffff;
4575 		dif_seed->at = dif_info->disable_app_ffff;
4576 	}
4577 	dif_seed->sge_type = SLI4_SGE_TYPE_DISEED;
4578 	/* Workaround for SKH (BZ157233) */
4579 	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4580 		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) && dif_info->dif_separate) {
4581 		dif_seed->sge_type = SLI4_SGE_TYPE_SKIP;
4582 	}
4583 
4584 	dif_seed->app_tag_cmp = dif_info->app_tag_cmp;
4585 	dif_seed->dif_blk_size = dif_info->blk_size;
4586 	dif_seed->auto_incr_ref_tag = dif_info->auto_incr_ref_tag;
4587 	dif_seed->check_app_tag = dif_info->check_app_tag;
4588 	dif_seed->check_ref_tag = dif_info->check_ref_tag;
4589 	dif_seed->check_crc = dif_info->check_guard;
4590 	dif_seed->new_ref_tag = dif_info->repl_ref_tag;
4591 
4592 	switch(dif_info->dif_oper) {
4593 	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
4594 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4595 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4596 		break;
4597 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
4598 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4599 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4600 		break;
4601 	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM:
4602 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4603 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4604 		break;
4605 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF:
4606 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4607 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4608 		break;
4609 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
4610 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4611 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4612 		break;
4613 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM:
4614 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4615 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4616 		break;
4617 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
4618 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4619 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4620 		break;
4621 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
4622 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4623 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4624 		break;
4625 	case OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW:
4626 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4627 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4628 		break;
4629 	default:
4630 		ocs_log_err(hw->os, "unsupported DIF operation %#x\n",
4631 			    dif_info->dif_oper);
4632 		return OCS_HW_RTN_ERROR;
4633 	}
4634 
4635 	/*
4636 	 * Set last, clear previous last
4637 	 */
4638 	data->last = TRUE;
4639 	if (io->n_sge) {
4640 		data[-1].last = FALSE;
4641 	}
4642 
4643 	io->n_sge++;
4644 
4645 	return OCS_HW_RTN_SUCCESS;
4646 }
4647 
4648 static ocs_hw_rtn_e
4649 ocs_hw_io_overflow_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4650 {
4651 	sli4_lsp_sge_t *lsp;
4652 
4653 	/* fail if we're already pointing to the overflow SGL */
4654 	if (io->sgl == io->ovfl_sgl) {
4655 		return OCS_HW_RTN_ERROR;
4656 	}
4657 
4658 	/*
4659 	 * For skyhawk, we can use another SGL to extend the SGL list. The
4660 	 * Chained entry must not be in the first 4 entries.
4661 	 *
4662 	 * Note: For DIF enabled IOs, we will use the ovfl_io for the sec_hio.
4663 	 */
4664 	if (sli_get_sgl_preregister(&hw->sli) &&
4665 	    io->def_sgl_count > 4 &&
4666 	    io->ovfl_io == NULL &&
4667 	    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4668 		(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
4669 		io->ovfl_io = ocs_hw_io_alloc(hw);
4670 		if (io->ovfl_io != NULL) {
4671 			/*
4672 			 * Note: We can't call ocs_hw_io_register_sgl() here
4673 			 * because it checks that SGLs are not pre-registered
4674 			 * and for shyhawk, preregistered SGLs are required.
4675 			 */
4676 			io->ovfl_sgl = &io->ovfl_io->def_sgl;
4677 			io->ovfl_sgl_count = io->ovfl_io->def_sgl_count;
4678 		}
4679 	}
4680 
4681 	/* fail if we don't have an overflow SGL registered */
4682 	if (io->ovfl_io == NULL || io->ovfl_sgl == NULL) {
4683 		return OCS_HW_RTN_ERROR;
4684 	}
4685 
4686 	/*
4687 	 * Overflow, we need to put a link SGE in the last location of the current SGL, after
4688 	 * copying the the last SGE to the overflow SGL
4689 	 */
4690 
4691 	((sli4_sge_t*)io->ovfl_sgl->virt)[0] = ((sli4_sge_t*)io->sgl->virt)[io->n_sge - 1];
4692 
4693 	lsp = &((sli4_lsp_sge_t*)io->sgl->virt)[io->n_sge - 1];
4694 	ocs_memset(lsp, 0, sizeof(*lsp));
4695 
4696 	if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4697 	    (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
4698 		sli_skh_chain_sge_build(&hw->sli,
4699 					(sli4_sge_t*)lsp,
4700 					io->ovfl_io->indicator,
4701 					0, /* frag_num */
4702 					0); /* offset */
4703 	} else {
4704 		lsp->buffer_address_high = ocs_addr32_hi(io->ovfl_sgl->phys);
4705 		lsp->buffer_address_low  = ocs_addr32_lo(io->ovfl_sgl->phys);
4706 		lsp->sge_type = SLI4_SGE_TYPE_LSP;
4707 		lsp->last = 0;
4708 		io->ovfl_lsp = lsp;
4709 		io->ovfl_lsp->segment_length = sizeof(sli4_sge_t);
4710 	}
4711 
4712 	/* Update the current SGL pointer, and n_sgl */
4713 	io->sgl = io->ovfl_sgl;
4714 	io->sgl_count = io->ovfl_sgl_count;
4715 	io->n_sge = 1;
4716 
4717 	return OCS_HW_RTN_SUCCESS;
4718 }
4719 
4720 /**
4721  * @ingroup io
4722  * @brief Add a scatter gather list entry to an IO.
4723  *
4724  * @param hw Hardware context.
4725  * @param io Previously-allocated HW IO object.
4726  * @param addr Physical address.
4727  * @param length Length of memory pointed to by @c addr.
4728  *
4729  * @return Returns 0 on success, or a non-zero value on failure.
4730  */
4731 ocs_hw_rtn_e
4732 ocs_hw_io_add_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr, uint32_t length)
4733 {
4734 	sli4_sge_t	*data = NULL;
4735 
4736 	if (!hw || !io || !addr || !length) {
4737 		ocs_log_err(hw ? hw->os : NULL,
4738 			    "bad parameter hw=%p io=%p addr=%lx length=%u\n",
4739 			    hw, io, addr, length);
4740 		return OCS_HW_RTN_ERROR;
4741 	}
4742 
4743 	if ((length != 0) && (io->n_sge + 1) > io->sgl_count) {
4744 		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_SUCCESS) {
4745 			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4746 			return OCS_HW_RTN_ERROR;
4747 		}
4748 	}
4749 
4750 	if (length > sli_get_max_sge(&hw->sli)) {
4751 		ocs_log_err(hw->os, "length of SGE %d bigger than allowed %d\n",
4752 			    length, sli_get_max_sge(&hw->sli));
4753 		return OCS_HW_RTN_ERROR;
4754 	}
4755 
4756 	data = io->sgl->virt;
4757 	data += io->n_sge;
4758 
4759 	data->sge_type = SLI4_SGE_TYPE_DATA;
4760 	data->buffer_address_high = ocs_addr32_hi(addr);
4761 	data->buffer_address_low  = ocs_addr32_lo(addr);
4762 	data->buffer_length = length;
4763 	data->data_offset = io->sge_offset;
4764 	/*
4765 	 * Always assume this is the last entry and mark as such.
4766 	 * If this is not the first entry unset the "last SGE"
4767 	 * indication for the previous entry
4768 	 */
4769 	data->last = TRUE;
4770 	if (io->n_sge) {
4771 		data[-1].last = FALSE;
4772 	}
4773 
4774 	/* Set first_data_bde if not previously set */
4775 	if (io->first_data_sge == 0) {
4776 		io->first_data_sge = io->n_sge;
4777 	}
4778 
4779 	io->sge_offset += length;
4780 	io->n_sge++;
4781 
4782 	/* Update the linked segment length (only executed after overflow has begun) */
4783 	if (io->ovfl_lsp != NULL) {
4784 		io->ovfl_lsp->segment_length = io->n_sge * sizeof(sli4_sge_t);
4785 	}
4786 
4787 	return OCS_HW_RTN_SUCCESS;
4788 }
4789 
4790 /**
4791  * @ingroup io
4792  * @brief Add a T10 DIF scatter gather list entry to an IO.
4793  *
4794  * @param hw Hardware context.
4795  * @param io Previously-allocated HW IO object.
4796  * @param addr DIF physical address.
4797  *
4798  * @return Returns 0 on success, or a non-zero value on failure.
4799  */
4800 ocs_hw_rtn_e
4801 ocs_hw_io_add_dif_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr)
4802 {
4803 	sli4_dif_sge_t	*data = NULL;
4804 
4805 	if (!hw || !io || !addr) {
4806 		ocs_log_err(hw ? hw->os : NULL,
4807 			    "bad parameter hw=%p io=%p addr=%lx\n",
4808 			    hw, io, addr);
4809 		return OCS_HW_RTN_ERROR;
4810 	}
4811 
4812 	if ((io->n_sge + 1) > hw->config.n_sgl) {
4813 		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_ERROR) {
4814 			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4815 			return OCS_HW_RTN_ERROR;
4816 		}
4817 	}
4818 
4819 	data = io->sgl->virt;
4820 	data += io->n_sge;
4821 
4822 	data->sge_type = SLI4_SGE_TYPE_DIF;
4823 	/* Workaround for SKH (BZ157233) */
4824 	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4825 		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type)) {
4826 		data->sge_type = SLI4_SGE_TYPE_SKIP;
4827 	}
4828 
4829 	data->buffer_address_high = ocs_addr32_hi(addr);
4830 	data->buffer_address_low  = ocs_addr32_lo(addr);
4831 
4832 	/*
4833 	 * Always assume this is the last entry and mark as such.
4834 	 * If this is not the first entry unset the "last SGE"
4835 	 * indication for the previous entry
4836 	 */
4837 	data->last = TRUE;
4838 	if (io->n_sge) {
4839 		data[-1].last = FALSE;
4840 	}
4841 
4842 	io->n_sge++;
4843 
4844 	return OCS_HW_RTN_SUCCESS;
4845 }
4846 
4847 /**
4848  * @ingroup io
4849  * @brief Abort a previously-started IO.
4850  *
4851  * @param hw Hardware context.
4852  * @param io_to_abort The IO to abort.
4853  * @param send_abts Boolean to have the hardware automatically
4854  * generate an ABTS.
4855  * @param cb Function call upon completion of the abort (may be NULL).
4856  * @param arg Argument to pass to abort completion function.
4857  *
4858  * @return Returns 0 on success, or a non-zero value on failure.
4859  */
4860 ocs_hw_rtn_e
4861 ocs_hw_io_abort(ocs_hw_t *hw, ocs_hw_io_t *io_to_abort, uint32_t send_abts, void *cb, void *arg)
4862 {
4863 	sli4_abort_type_e atype = SLI_ABORT_MAX;
4864 	uint32_t	id = 0, mask = 0;
4865 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4866 	hw_wq_callback_t *wqcb;
4867 
4868 	if (!hw || !io_to_abort) {
4869 		ocs_log_err(hw ? hw->os : NULL,
4870 			    "bad parameter hw=%p io=%p\n",
4871 			    hw, io_to_abort);
4872 		return OCS_HW_RTN_ERROR;
4873 	}
4874 
4875 	if (hw->state != OCS_HW_STATE_ACTIVE) {
4876 		ocs_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
4877 			    hw->state);
4878 		return OCS_HW_RTN_ERROR;
4879 	}
4880 
4881 	/* take a reference on IO being aborted */
4882 	if (ocs_ref_get_unless_zero(&io_to_abort->ref) == 0) {
4883 		/* command no longer active */
4884 		ocs_log_test(hw ? hw->os : NULL,
4885 				"io not active xri=0x%x tag=0x%x\n",
4886 				io_to_abort->indicator, io_to_abort->reqtag);
4887 		return OCS_HW_RTN_IO_NOT_ACTIVE;
4888 	}
4889 
4890 	/* non-port owned XRI checks */
4891 	/* Must have a valid WQ reference */
4892 	if (io_to_abort->wq == NULL) {
4893 		ocs_log_test(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
4894 				io_to_abort->indicator);
4895 		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4896 		return OCS_HW_RTN_IO_NOT_ACTIVE;
4897 	}
4898 
4899 	/* Validation checks complete; now check to see if already being aborted */
4900 	ocs_lock(&hw->io_abort_lock);
4901 		if (io_to_abort->abort_in_progress) {
4902 			ocs_unlock(&hw->io_abort_lock);
4903 			ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4904 			ocs_log_debug(hw ? hw->os : NULL,
4905 				"io already being aborted xri=0x%x tag=0x%x\n",
4906 				io_to_abort->indicator, io_to_abort->reqtag);
4907 			return OCS_HW_RTN_IO_ABORT_IN_PROGRESS;
4908 		}
4909 
4910 		/*
4911 		 * This IO is not already being aborted. Set flag so we won't try to
4912 		 * abort it again. After all, we only have one abort_done callback.
4913 		 */
4914 		io_to_abort->abort_in_progress = 1;
4915 	ocs_unlock(&hw->io_abort_lock);
4916 
4917 	/*
4918 	 * If we got here, the possibilities are:
4919 	 * - host owned xri
4920 	 *	- io_to_abort->wq_index != UINT32_MAX
4921 	 *		- submit ABORT_WQE to same WQ
4922 	 * - port owned xri:
4923 	 *	- rxri: io_to_abort->wq_index == UINT32_MAX
4924 	 *		- submit ABORT_WQE to any WQ
4925 	 *	- non-rxri
4926 	 *		- io_to_abort->index != UINT32_MAX
4927 	 *			- submit ABORT_WQE to same WQ
4928 	 *		- io_to_abort->index == UINT32_MAX
4929 	 *			- submit ABORT_WQE to any WQ
4930 	 */
4931 	io_to_abort->abort_done = cb;
4932 	io_to_abort->abort_arg  = arg;
4933 
4934 	atype = SLI_ABORT_XRI;
4935 	id = io_to_abort->indicator;
4936 
4937 	/* Allocate a request tag for the abort portion of this IO */
4938 	wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_abort, io_to_abort);
4939 	if (wqcb == NULL) {
4940 		ocs_log_err(hw->os, "can't allocate request tag\n");
4941 		return OCS_HW_RTN_NO_RESOURCES;
4942 	}
4943 	io_to_abort->abort_reqtag = wqcb->instance_index;
4944 
4945 	/*
4946 	 * If the wqe is on the pending list, then set this wqe to be
4947 	 * aborted when the IO's wqe is removed from the list.
4948 	 */
4949 	if (io_to_abort->wq != NULL) {
4950 		sli_queue_lock(io_to_abort->wq->queue);
4951 			if (ocs_list_on_list(&io_to_abort->wqe.link)) {
4952 				io_to_abort->wqe.abort_wqe_submit_needed = 1;
4953 				io_to_abort->wqe.send_abts = send_abts;
4954 				io_to_abort->wqe.id = id;
4955 				io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
4956 				sli_queue_unlock(io_to_abort->wq->queue);
4957 				return 0;
4958 		}
4959 		sli_queue_unlock(io_to_abort->wq->queue);
4960 	}
4961 
4962 	if (sli_abort_wqe(&hw->sli, io_to_abort->wqe.wqebuf, hw->sli.config.wqe_size, atype, send_abts, id, mask,
4963 			  io_to_abort->abort_reqtag, SLI4_CQ_DEFAULT)) {
4964 		ocs_log_err(hw->os, "ABORT WQE error\n");
4965 		io_to_abort->abort_reqtag = UINT32_MAX;
4966 		ocs_hw_reqtag_free(hw, wqcb);
4967 		rc = OCS_HW_RTN_ERROR;
4968 	}
4969 
4970 	if (OCS_HW_RTN_SUCCESS == rc) {
4971 		if (io_to_abort->wq == NULL) {
4972 			io_to_abort->wq = ocs_hw_queue_next_wq(hw, io_to_abort);
4973 			ocs_hw_assert(io_to_abort->wq != NULL);
4974 		}
4975 		/* ABORT_WQE does not actually utilize an XRI on the Port,
4976 		 * therefore, keep xbusy as-is to track the exchange's state,
4977 		 * not the ABORT_WQE's state
4978 		 */
4979 		rc = hw_wq_write(io_to_abort->wq, &io_to_abort->wqe);
4980 		if (rc > 0) {
4981 			/* non-negative return is success */
4982 			rc = 0;
4983 			/* can't abort an abort so skip adding to timed wqe list */
4984 		}
4985 	}
4986 
4987 	if (OCS_HW_RTN_SUCCESS != rc) {
4988 		ocs_lock(&hw->io_abort_lock);
4989 			io_to_abort->abort_in_progress = 0;
4990 		ocs_unlock(&hw->io_abort_lock);
4991 		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4992 	}
4993 	return rc;
4994 }
4995 
4996 /**
4997  * @ingroup io
4998  * @brief Return the OX_ID/RX_ID of the IO.
4999  *
5000  * @param hw Hardware context.
5001  * @param io HW IO object.
5002  *
5003  * @return Returns X_ID on success, or -1 on failure.
5004  */
5005 int32_t
5006 ocs_hw_io_get_xid(ocs_hw_t *hw, ocs_hw_io_t *io)
5007 {
5008 	if (!hw || !io) {
5009 		ocs_log_err(hw ? hw->os : NULL,
5010 			    "bad parameter hw=%p io=%p\n", hw, io);
5011 		return -1;
5012 	}
5013 
5014 	return io->indicator;
5015 }
5016 
5017 typedef struct ocs_hw_fw_write_cb_arg {
5018 	ocs_hw_fw_cb_t cb;
5019 	void *arg;
5020 } ocs_hw_fw_write_cb_arg_t;
5021 
5022 typedef struct ocs_hw_sfp_cb_arg {
5023 	ocs_hw_sfp_cb_t cb;
5024 	void *arg;
5025 	ocs_dma_t payload;
5026 } ocs_hw_sfp_cb_arg_t;
5027 
5028 typedef struct ocs_hw_temp_cb_arg {
5029 	ocs_hw_temp_cb_t cb;
5030 	void *arg;
5031 } ocs_hw_temp_cb_arg_t;
5032 
5033 typedef struct ocs_hw_link_stat_cb_arg {
5034 	ocs_hw_link_stat_cb_t cb;
5035 	void *arg;
5036 } ocs_hw_link_stat_cb_arg_t;
5037 
5038 typedef struct ocs_hw_host_stat_cb_arg {
5039 	ocs_hw_host_stat_cb_t cb;
5040 	void *arg;
5041 } ocs_hw_host_stat_cb_arg_t;
5042 
5043 typedef struct ocs_hw_dump_get_cb_arg {
5044 	ocs_hw_dump_get_cb_t cb;
5045 	void *arg;
5046 	void *mbox_cmd;
5047 } ocs_hw_dump_get_cb_arg_t;
5048 
5049 typedef struct ocs_hw_dump_clear_cb_arg {
5050 	ocs_hw_dump_clear_cb_t cb;
5051 	void *arg;
5052 	void *mbox_cmd;
5053 } ocs_hw_dump_clear_cb_arg_t;
5054 
5055 /**
5056  * @brief Write a portion of a firmware image to the device.
5057  *
5058  * @par Description
5059  * Calls the correct firmware write function based on the device type.
5060  *
5061  * @param hw Hardware context.
5062  * @param dma DMA structure containing the firmware image chunk.
5063  * @param size Size of the firmware image chunk.
5064  * @param offset Offset, in bytes, from the beginning of the firmware image.
5065  * @param last True if this is the last chunk of the image.
5066  * Causes the image to be committed to flash.
5067  * @param cb Pointer to a callback function that is called when the command completes.
5068  * The callback function prototype is
5069  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5070  * @param arg Pointer to be passed to the callback function.
5071  *
5072  * @return Returns 0 on success, or a non-zero value on failure.
5073  */
5074 ocs_hw_rtn_e
5075 ocs_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)
5076 {
5077 	if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
5078 		return ocs_hw_firmware_write_lancer(hw, dma, size, offset, last, cb, arg);
5079 	} else {
5080 		/* Write firmware_write for BE3/Skyhawk not supported */
5081 		return -1;
5082 	}
5083 }
5084 
5085 /**
5086  * @brief Write a portion of a firmware image to the Emulex XE201 ASIC (Lancer).
5087  *
5088  * @par Description
5089  * Creates a SLI_CONFIG mailbox command, fills it with the correct values to write a
5090  * firmware image chunk, and then sends the command with ocs_hw_command(). On completion,
5091  * the callback function ocs_hw_fw_write_cb() gets called to free the mailbox
5092  * and to signal the caller that the write has completed.
5093  *
5094  * @param hw Hardware context.
5095  * @param dma DMA structure containing the firmware image chunk.
5096  * @param size Size of the firmware image chunk.
5097  * @param offset Offset, in bytes, from the beginning of the firmware image.
5098  * @param last True if this is the last chunk of the image. Causes the image to be committed to flash.
5099  * @param cb Pointer to a callback function that is called when the command completes.
5100  * The callback function prototype is
5101  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5102  * @param arg Pointer to be passed to the callback function.
5103  *
5104  * @return Returns 0 on success, or a non-zero value on failure.
5105  */
5106 ocs_hw_rtn_e
5107 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)
5108 {
5109 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5110 	uint8_t *mbxdata;
5111 	ocs_hw_fw_write_cb_arg_t *cb_arg;
5112 	int noc=0;	/* No Commit bit - set to 1 for testing */
5113 
5114 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
5115 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
5116 		return OCS_HW_RTN_ERROR;
5117 	}
5118 
5119 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5120 	if (mbxdata == NULL) {
5121 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5122 		return OCS_HW_RTN_NO_MEMORY;
5123 	}
5124 
5125 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_fw_write_cb_arg_t), OCS_M_NOWAIT);
5126 	if (cb_arg == NULL) {
5127 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5128 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5129 		return OCS_HW_RTN_NO_MEMORY;
5130 	}
5131 
5132 	cb_arg->cb = cb;
5133 	cb_arg->arg = arg;
5134 
5135 	if (sli_cmd_common_write_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE, noc, last,
5136 			size, offset, "/prg/", dma)) {
5137 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_fw_write, cb_arg);
5138 	}
5139 
5140 	if (rc != OCS_HW_RTN_SUCCESS) {
5141 		ocs_log_test(hw->os, "COMMON_WRITE_OBJECT failed\n");
5142 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5143 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5144 	}
5145 
5146 	return rc;
5147 
5148 }
5149 
5150 /**
5151  * @brief Called when the WRITE OBJECT command completes.
5152  *
5153  * @par Description
5154  * Get the number of bytes actually written out of the response, free the mailbox
5155  * that was malloc'd by ocs_hw_firmware_write(),
5156  * then call the callback and pass the status and bytes written.
5157  *
5158  * @param hw Hardware context.
5159  * @param status Status field from the mbox completion.
5160  * @param mqe Mailbox response structure.
5161  * @param arg Pointer to a callback function that signals the caller that the command is done.
5162  * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_written)</tt>.
5163  *
5164  * @return Returns 0.
5165  */
5166 static int32_t
5167 ocs_hw_cb_fw_write(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5168 {
5169 
5170 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
5171 	sli4_res_common_write_object_t* wr_obj_rsp = (sli4_res_common_write_object_t*) &(mbox_rsp->payload.embed);
5172 	ocs_hw_fw_write_cb_arg_t *cb_arg = arg;
5173 	uint32_t bytes_written;
5174 	uint16_t mbox_status;
5175 	uint32_t change_status;
5176 
5177 	bytes_written = wr_obj_rsp->actual_write_length;
5178 	mbox_status = mbox_rsp->hdr.status;
5179 	change_status = wr_obj_rsp->change_status;
5180 
5181 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5182 
5183 	if (cb_arg) {
5184 		if (cb_arg->cb) {
5185 			if ((status == 0) && mbox_status) {
5186 				status = mbox_status;
5187 			}
5188 			cb_arg->cb(status, bytes_written, change_status, cb_arg->arg);
5189 		}
5190 
5191 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5192 	}
5193 
5194 	return 0;
5195 
5196 }
5197 
5198 /**
5199  * @brief Called when the READ_TRANSCEIVER_DATA command completes.
5200  *
5201  * @par Description
5202  * Get the number of bytes read out of the response, free the mailbox that was malloc'd
5203  * by ocs_hw_get_sfp(), then call the callback and pass the status and bytes written.
5204  *
5205  * @param hw Hardware context.
5206  * @param status Status field from the mbox completion.
5207  * @param mqe Mailbox response structure.
5208  * @param arg Pointer to a callback function that signals the caller that the command is done.
5209  * The callback function prototype is
5210  * <tt>void cb(int32_t status, uint32_t bytes_written, uint32_t *data, void *arg)</tt>.
5211  *
5212  * @return Returns 0.
5213  */
5214 static int32_t
5215 ocs_hw_cb_sfp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5216 {
5217 
5218 	ocs_hw_sfp_cb_arg_t *cb_arg = arg;
5219 	ocs_dma_t *payload = NULL;
5220 	sli4_res_common_read_transceiver_data_t* mbox_rsp = NULL;
5221 	uint32_t bytes_written;
5222 
5223 	if (cb_arg) {
5224 		payload = &(cb_arg->payload);
5225 		if (cb_arg->cb) {
5226 			mbox_rsp = (sli4_res_common_read_transceiver_data_t*) payload->virt;
5227 			bytes_written = mbox_rsp->hdr.response_length;
5228 			if ((status == 0) && mbox_rsp->hdr.status) {
5229 				status = mbox_rsp->hdr.status;
5230 			}
5231 			cb_arg->cb(hw->os, status, bytes_written, mbox_rsp->page_data, cb_arg->arg);
5232 		}
5233 
5234 		ocs_dma_free(hw->os, &cb_arg->payload);
5235 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5236 	}
5237 
5238 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5239 	return 0;
5240 }
5241 
5242 /**
5243  * @ingroup io
5244  * @brief Function to retrieve the SFP information.
5245  *
5246  * @param hw Hardware context.
5247  * @param page The page of SFP data to retrieve (0xa0 or 0xa2).
5248  * @param cb Function call upon completion of sending the data (may be NULL).
5249  * @param arg Argument to pass to IO completion function.
5250  *
5251  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5252  */
5253 ocs_hw_rtn_e
5254 ocs_hw_get_sfp(ocs_hw_t *hw, uint16_t page, ocs_hw_sfp_cb_t cb, void *arg)
5255 {
5256 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5257 	ocs_hw_sfp_cb_arg_t *cb_arg;
5258 	uint8_t *mbxdata;
5259 
5260 	/* mbxdata holds the header of the command */
5261 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5262 	if (mbxdata == NULL) {
5263 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5264 		return OCS_HW_RTN_NO_MEMORY;
5265 	}
5266 
5267 	/* cb_arg holds the data that will be passed to the callback on completion */
5268 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_sfp_cb_arg_t), OCS_M_NOWAIT);
5269 	if (cb_arg == NULL) {
5270 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5271 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5272 		return OCS_HW_RTN_NO_MEMORY;
5273 	}
5274 
5275 	cb_arg->cb = cb;
5276 	cb_arg->arg = arg;
5277 
5278 	/* payload holds the non-embedded portion */
5279 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_read_transceiver_data_t),
5280 			  OCS_MIN_DMA_ALIGNMENT)) {
5281 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
5282 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5283 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5284 		return OCS_HW_RTN_NO_MEMORY;
5285 	}
5286 
5287 	/* Send the HW command */
5288 	if (sli_cmd_common_read_transceiver_data(&hw->sli, mbxdata, SLI4_BMBX_SIZE, page,
5289 	    &cb_arg->payload)) {
5290 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_sfp, cb_arg);
5291 	}
5292 
5293 	if (rc != OCS_HW_RTN_SUCCESS) {
5294 		ocs_log_test(hw->os, "READ_TRANSCEIVER_DATA failed with status %d\n",
5295 				rc);
5296 		ocs_dma_free(hw->os, &cb_arg->payload);
5297 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5298 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5299 	}
5300 
5301 	return rc;
5302 }
5303 
5304 /**
5305  * @brief Function to retrieve the temperature information.
5306  *
5307  * @param hw Hardware context.
5308  * @param cb Function call upon completion of sending the data (may be NULL).
5309  * @param arg Argument to pass to IO completion function.
5310  *
5311  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5312  */
5313 ocs_hw_rtn_e
5314 ocs_hw_get_temperature(ocs_hw_t *hw, ocs_hw_temp_cb_t cb, void *arg)
5315 {
5316 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5317 	ocs_hw_temp_cb_arg_t *cb_arg;
5318 	uint8_t *mbxdata;
5319 
5320 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5321 	if (mbxdata == NULL) {
5322 		ocs_log_err(hw->os, "failed to malloc mbox");
5323 		return OCS_HW_RTN_NO_MEMORY;
5324 	}
5325 
5326 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_temp_cb_arg_t), OCS_M_NOWAIT);
5327 	if (cb_arg == NULL) {
5328 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5329 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5330 		return OCS_HW_RTN_NO_MEMORY;
5331 	}
5332 
5333 	cb_arg->cb = cb;
5334 	cb_arg->arg = arg;
5335 
5336 	if (sli_cmd_dump_type4(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5337 				SLI4_WKI_TAG_SAT_TEM)) {
5338 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_temp, cb_arg);
5339 	}
5340 
5341 	if (rc != OCS_HW_RTN_SUCCESS) {
5342 		ocs_log_test(hw->os, "DUMP_TYPE4 failed\n");
5343 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5344 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5345 	}
5346 
5347 	return rc;
5348 }
5349 
5350 /**
5351  * @brief Called when the DUMP command completes.
5352  *
5353  * @par Description
5354  * Get the temperature data out of the response, free the mailbox that was malloc'd
5355  * by ocs_hw_get_temperature(), then call the callback and pass the status and data.
5356  *
5357  * @param hw Hardware context.
5358  * @param status Status field from the mbox completion.
5359  * @param mqe Mailbox response structure.
5360  * @param arg Pointer to a callback function that signals the caller that the command is done.
5361  * The callback function prototype is defined by ocs_hw_temp_cb_t.
5362  *
5363  * @return Returns 0.
5364  */
5365 static int32_t
5366 ocs_hw_cb_temp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5367 {
5368 
5369 	sli4_cmd_dump4_t* mbox_rsp = (sli4_cmd_dump4_t*) mqe;
5370 	ocs_hw_temp_cb_arg_t *cb_arg = arg;
5371 	uint32_t curr_temp = mbox_rsp->resp_data[0]; /* word 5 */
5372 	uint32_t crit_temp_thrshld = mbox_rsp->resp_data[1]; /* word 6*/
5373 	uint32_t warn_temp_thrshld = mbox_rsp->resp_data[2]; /* word 7 */
5374 	uint32_t norm_temp_thrshld = mbox_rsp->resp_data[3]; /* word 8 */
5375 	uint32_t fan_off_thrshld = mbox_rsp->resp_data[4];   /* word 9 */
5376 	uint32_t fan_on_thrshld = mbox_rsp->resp_data[5];    /* word 10 */
5377 
5378 	if (cb_arg) {
5379 		if (cb_arg->cb) {
5380 			if ((status == 0) && mbox_rsp->hdr.status) {
5381 				status = mbox_rsp->hdr.status;
5382 			}
5383 			cb_arg->cb(status,
5384 				   curr_temp,
5385 				   crit_temp_thrshld,
5386 				   warn_temp_thrshld,
5387 				   norm_temp_thrshld,
5388 				   fan_off_thrshld,
5389 				   fan_on_thrshld,
5390 				   cb_arg->arg);
5391 		}
5392 
5393 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5394 	}
5395 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5396 
5397 	return 0;
5398 }
5399 
5400 /**
5401  * @brief Function to retrieve the link statistics.
5402  *
5403  * @param hw Hardware context.
5404  * @param req_ext_counters If TRUE, then the extended counters will be requested.
5405  * @param clear_overflow_flags If TRUE, then overflow flags will be cleared.
5406  * @param clear_all_counters If TRUE, the counters will be cleared.
5407  * @param cb Function call upon completion of sending the data (may be NULL).
5408  * @param arg Argument to pass to IO completion function.
5409  *
5410  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5411  */
5412 ocs_hw_rtn_e
5413 ocs_hw_get_link_stats(ocs_hw_t *hw,
5414 			uint8_t req_ext_counters,
5415 			uint8_t clear_overflow_flags,
5416 			uint8_t clear_all_counters,
5417 			ocs_hw_link_stat_cb_t cb,
5418 			void *arg)
5419 {
5420 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5421 	ocs_hw_link_stat_cb_arg_t *cb_arg;
5422 	uint8_t *mbxdata;
5423 
5424 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5425 	if (mbxdata == NULL) {
5426 		ocs_log_err(hw->os, "failed to malloc mbox");
5427 		return OCS_HW_RTN_NO_MEMORY;
5428 	}
5429 
5430 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_link_stat_cb_arg_t), OCS_M_NOWAIT);
5431 	if (cb_arg == NULL) {
5432 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5433 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5434 		return OCS_HW_RTN_NO_MEMORY;
5435 	}
5436 
5437 	cb_arg->cb = cb;
5438 	cb_arg->arg = arg;
5439 
5440 	if (sli_cmd_read_link_stats(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5441 				    req_ext_counters,
5442 				    clear_overflow_flags,
5443 				    clear_all_counters)) {
5444 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_link_stat, cb_arg);
5445 	}
5446 
5447 	if (rc != OCS_HW_RTN_SUCCESS) {
5448 		ocs_log_test(hw->os, "READ_LINK_STATS failed\n");
5449 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5450 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5451 	}
5452 
5453 	return rc;
5454 }
5455 
5456 /**
5457  * @brief Called when the READ_LINK_STAT command completes.
5458  *
5459  * @par Description
5460  * Get the counters out of the response, free the mailbox that was malloc'd
5461  * by ocs_hw_get_link_stats(), then call the callback and pass the status and data.
5462  *
5463  * @param hw Hardware context.
5464  * @param status Status field from the mbox completion.
5465  * @param mqe Mailbox response structure.
5466  * @param arg Pointer to a callback function that signals the caller that the command is done.
5467  * The callback function prototype is defined by ocs_hw_link_stat_cb_t.
5468  *
5469  * @return Returns 0.
5470  */
5471 static int32_t
5472 ocs_hw_cb_link_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5473 {
5474 
5475 	sli4_cmd_read_link_stats_t* mbox_rsp = (sli4_cmd_read_link_stats_t*) mqe;
5476 	ocs_hw_link_stat_cb_arg_t *cb_arg = arg;
5477 	ocs_hw_link_stat_counts_t counts[OCS_HW_LINK_STAT_MAX];
5478 	uint32_t num_counters = (mbox_rsp->gec ? 20 : 13);
5479 
5480 	ocs_memset(counts, 0, sizeof(ocs_hw_link_stat_counts_t) *
5481 		   OCS_HW_LINK_STAT_MAX);
5482 
5483 	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].overflow = mbox_rsp->w02of;
5484 	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].overflow = mbox_rsp->w03of;
5485 	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].overflow = mbox_rsp->w04of;
5486 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].overflow = mbox_rsp->w05of;
5487 	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].overflow = mbox_rsp->w06of;
5488 	counts[OCS_HW_LINK_STAT_CRC_COUNT].overflow = mbox_rsp->w07of;
5489 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].overflow = mbox_rsp->w08of;
5490 	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].overflow = mbox_rsp->w09of;
5491 	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].overflow = mbox_rsp->w10of;
5492 	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].overflow = mbox_rsp->w11of;
5493 	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].overflow = mbox_rsp->w12of;
5494 	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].overflow = mbox_rsp->w13of;
5495 	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].overflow = mbox_rsp->w14of;
5496 	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].overflow = mbox_rsp->w15of;
5497 	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].overflow = mbox_rsp->w16of;
5498 	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].overflow = mbox_rsp->w17of;
5499 	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].overflow = mbox_rsp->w18of;
5500 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].overflow = mbox_rsp->w19of;
5501 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].overflow = mbox_rsp->w20of;
5502 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].overflow = mbox_rsp->w21of;
5503 
5504 	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = mbox_rsp->link_failure_error_count;
5505 	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = mbox_rsp->loss_of_sync_error_count;
5506 	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = mbox_rsp->loss_of_signal_error_count;
5507 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = mbox_rsp->primitive_sequence_error_count;
5508 	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = mbox_rsp->invalid_transmission_word_error_count;
5509 	counts[OCS_HW_LINK_STAT_CRC_COUNT].counter = mbox_rsp->crc_error_count;
5510 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = mbox_rsp->primitive_sequence_event_timeout_count;
5511 	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = mbox_rsp->elastic_buffer_overrun_error_count;
5512 	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = mbox_rsp->arbitration_fc_al_timout_count;
5513 	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = mbox_rsp->advertised_receive_bufftor_to_buffer_credit;
5514 	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = mbox_rsp->current_receive_buffer_to_buffer_credit;
5515 	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = mbox_rsp->advertised_transmit_buffer_to_buffer_credit;
5516 	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = mbox_rsp->current_transmit_buffer_to_buffer_credit;
5517 	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].counter = mbox_rsp->received_eofa_count;
5518 	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = mbox_rsp->received_eofdti_count;
5519 	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = mbox_rsp->received_eofni_count;
5520 	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].counter = mbox_rsp->received_soff_count;
5521 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = mbox_rsp->received_dropped_no_aer_count;
5522 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = mbox_rsp->received_dropped_no_available_rpi_resources_count;
5523 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = mbox_rsp->received_dropped_no_available_xri_resources_count;
5524 
5525 	if (cb_arg) {
5526 		if (cb_arg->cb) {
5527 			if ((status == 0) && mbox_rsp->hdr.status) {
5528 				status = mbox_rsp->hdr.status;
5529 			}
5530 			cb_arg->cb(status,
5531 				   num_counters,
5532 				   counts,
5533 				   cb_arg->arg);
5534 		}
5535 
5536 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5537 	}
5538 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5539 
5540 	return 0;
5541 }
5542 
5543 /**
5544  * @brief Function to retrieve the link and host statistics.
5545  *
5546  * @param hw Hardware context.
5547  * @param cc clear counters, if TRUE all counters will be cleared.
5548  * @param cb Function call upon completion of receiving the data.
5549  * @param arg Argument to pass to pointer fc hosts statistics structure.
5550  *
5551  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5552  */
5553 ocs_hw_rtn_e
5554 ocs_hw_get_host_stats(ocs_hw_t *hw, uint8_t cc, ocs_hw_host_stat_cb_t cb, void *arg)
5555 {
5556 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5557 	ocs_hw_host_stat_cb_arg_t *cb_arg;
5558 	uint8_t *mbxdata;
5559 
5560 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO);
5561 	if (mbxdata == NULL) {
5562 		ocs_log_err(hw->os, "failed to malloc mbox");
5563 		return OCS_HW_RTN_NO_MEMORY;
5564 	}
5565 
5566 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_host_stat_cb_arg_t), 0);
5567 	if (cb_arg == NULL) {
5568 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5569 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5570 		return OCS_HW_RTN_NO_MEMORY;
5571 	 }
5572 
5573 	 cb_arg->cb = cb;
5574 	 cb_arg->arg = arg;
5575 
5576 	 /* Send the HW command to get the host stats */
5577 	if (sli_cmd_read_status(&hw->sli, mbxdata, SLI4_BMBX_SIZE, cc)) {
5578 		 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_host_stat, cb_arg);
5579 	}
5580 
5581 	if (rc != OCS_HW_RTN_SUCCESS) {
5582 		ocs_log_test(hw->os, "READ_HOST_STATS failed\n");
5583 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5584 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5585 	}
5586 
5587 	return rc;
5588 }
5589 
5590 /**
5591  * @brief Called when the READ_STATUS command completes.
5592  *
5593  * @par Description
5594  * Get the counters out of the response, free the mailbox that was malloc'd
5595  * by ocs_hw_get_host_stats(), then call the callback and pass
5596  * the status and data.
5597  *
5598  * @param hw Hardware context.
5599  * @param status Status field from the mbox completion.
5600  * @param mqe Mailbox response structure.
5601  * @param arg Pointer to a callback function that signals the caller that the command is done.
5602  * The callback function prototype is defined by
5603  * ocs_hw_host_stat_cb_t.
5604  *
5605  * @return Returns 0.
5606  */
5607 static int32_t
5608 ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5609 {
5610 
5611 	sli4_cmd_read_status_t* mbox_rsp = (sli4_cmd_read_status_t*) mqe;
5612 	ocs_hw_host_stat_cb_arg_t *cb_arg = arg;
5613 	ocs_hw_host_stat_counts_t counts[OCS_HW_HOST_STAT_MAX];
5614 	uint32_t num_counters = OCS_HW_HOST_STAT_MAX;
5615 
5616 	ocs_memset(counts, 0, sizeof(ocs_hw_host_stat_counts_t) *
5617 		   OCS_HW_HOST_STAT_MAX);
5618 
5619 	counts[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter = mbox_rsp->transmit_kbyte_count;
5620 	counts[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter = mbox_rsp->receive_kbyte_count;
5621 	counts[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter = mbox_rsp->transmit_frame_count;
5622 	counts[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter = mbox_rsp->receive_frame_count;
5623 	counts[OCS_HW_HOST_STAT_TX_SEQ_COUNT].counter = mbox_rsp->transmit_sequence_count;
5624 	counts[OCS_HW_HOST_STAT_RX_SEQ_COUNT].counter = mbox_rsp->receive_sequence_count;
5625 	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = mbox_rsp->total_exchanges_originator;
5626 	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = mbox_rsp->total_exchanges_responder;
5627 	counts[OCS_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = mbox_rsp->receive_p_bsy_count;
5628 	counts[OCS_HW_HOST_STAT_RX_F_BSY_COUNT].counter = mbox_rsp->receive_f_bsy_count;
5629 	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_rq_buffer_count;
5630 	counts[OCS_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = mbox_rsp->empty_rq_timeout_count;
5631 	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_xri_count;
5632 	counts[OCS_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = mbox_rsp->empty_xri_pool_count;
5633 
5634 	if (cb_arg) {
5635 		if (cb_arg->cb) {
5636 			if ((status == 0) && mbox_rsp->hdr.status) {
5637 				status = mbox_rsp->hdr.status;
5638 			}
5639 			cb_arg->cb(status,
5640 				   num_counters,
5641 				   counts,
5642 				   cb_arg->arg);
5643 		}
5644 
5645 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5646 	}
5647 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5648 
5649 	return 0;
5650 }
5651 
5652 /**
5653  * @brief HW link configuration enum to the CLP string value mapping.
5654  *
5655  * This structure provides a mapping from the ocs_hw_linkcfg_e
5656  * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5657  * control) to the CLP string that is used
5658  * in the DMTF_CLP_CMD mailbox command.
5659  */
5660 typedef struct ocs_hw_linkcfg_map_s {
5661 	ocs_hw_linkcfg_e linkcfg;
5662 	const char *clp_str;
5663 } ocs_hw_linkcfg_map_t;
5664 
5665 /**
5666  * @brief Mapping from the HW linkcfg enum to the CLP command value
5667  * string.
5668  */
5669 static ocs_hw_linkcfg_map_t linkcfg_map[] = {
5670 	{OCS_HW_LINKCFG_4X10G, "ELX_4x10G"},
5671 	{OCS_HW_LINKCFG_1X40G, "ELX_1x40G"},
5672 	{OCS_HW_LINKCFG_2X16G, "ELX_2x16G"},
5673 	{OCS_HW_LINKCFG_4X8G, "ELX_4x8G"},
5674 	{OCS_HW_LINKCFG_4X1G, "ELX_4x1G"},
5675 	{OCS_HW_LINKCFG_2X10G, "ELX_2x10G"},
5676 	{OCS_HW_LINKCFG_2X10G_2X8G, "ELX_2x10G_2x8G"}};
5677 
5678 /**
5679  * @brief HW link configuration enum to Skyhawk link config ID mapping.
5680  *
5681  * This structure provides a mapping from the ocs_hw_linkcfg_e
5682  * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5683  * control) to the link config ID numbers used by Skyhawk
5684  */
5685 typedef struct ocs_hw_skyhawk_linkcfg_map_s {
5686 	ocs_hw_linkcfg_e linkcfg;
5687 	uint32_t	config_id;
5688 } ocs_hw_skyhawk_linkcfg_map_t;
5689 
5690 /**
5691  * @brief Mapping from the HW linkcfg enum to the Skyhawk link config IDs
5692  */
5693 static ocs_hw_skyhawk_linkcfg_map_t skyhawk_linkcfg_map[] = {
5694 	{OCS_HW_LINKCFG_4X10G, 0x0a},
5695 	{OCS_HW_LINKCFG_1X40G, 0x09},
5696 };
5697 
5698 /**
5699  * @brief Helper function for getting the HW linkcfg enum from the CLP
5700  * string value
5701  *
5702  * @param clp_str CLP string value from OEMELX_LinkConfig.
5703  *
5704  * @return Returns the HW linkcfg enum corresponding to clp_str.
5705  */
5706 static ocs_hw_linkcfg_e
5707 ocs_hw_linkcfg_from_clp(const char *clp_str)
5708 {
5709 	uint32_t i;
5710 	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5711 		if (ocs_strncmp(linkcfg_map[i].clp_str, clp_str, ocs_strlen(clp_str)) == 0) {
5712 			return linkcfg_map[i].linkcfg;
5713 		}
5714 	}
5715 	return OCS_HW_LINKCFG_NA;
5716 }
5717 
5718 /**
5719  * @brief Helper function for getting the CLP string value from the HW
5720  * linkcfg enum.
5721  *
5722  * @param linkcfg HW linkcfg enum.
5723  *
5724  * @return Returns the OEMELX_LinkConfig CLP string value corresponding to
5725  * given linkcfg.
5726  */
5727 static const char *
5728 ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5729 {
5730 	uint32_t i;
5731 	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5732 		if (linkcfg_map[i].linkcfg == linkcfg) {
5733 			return linkcfg_map[i].clp_str;
5734 		}
5735 	}
5736 	return NULL;
5737 }
5738 
5739 /**
5740  * @brief Helper function for getting a Skyhawk link config ID from the HW
5741  * linkcfg enum.
5742  *
5743  * @param linkcfg HW linkcfg enum.
5744  *
5745  * @return Returns the Skyhawk link config ID corresponding to
5746  * given linkcfg.
5747  */
5748 static uint32_t
5749 ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5750 {
5751 	uint32_t i;
5752 	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5753 		if (skyhawk_linkcfg_map[i].linkcfg == linkcfg) {
5754 			return skyhawk_linkcfg_map[i].config_id;
5755 		}
5756 	}
5757 	return 0;
5758 }
5759 
5760 /**
5761  * @brief Helper function for getting the HW linkcfg enum from a
5762  * Skyhawk config ID.
5763  *
5764  * @param config_id Skyhawk link config ID.
5765  *
5766  * @return Returns the HW linkcfg enum corresponding to config_id.
5767  */
5768 static ocs_hw_linkcfg_e
5769 ocs_hw_linkcfg_from_config_id(const uint32_t config_id)
5770 {
5771 	uint32_t i;
5772 	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5773 		if (skyhawk_linkcfg_map[i].config_id == config_id) {
5774 			return skyhawk_linkcfg_map[i].linkcfg;
5775 		}
5776 	}
5777 	return OCS_HW_LINKCFG_NA;
5778 }
5779 
5780 /**
5781  * @brief Link configuration callback argument.
5782  */
5783 typedef struct ocs_hw_linkcfg_cb_arg_s {
5784 	ocs_hw_port_control_cb_t cb;
5785 	void *arg;
5786 	uint32_t opts;
5787 	int32_t status;
5788 	ocs_dma_t dma_cmd;
5789 	ocs_dma_t dma_resp;
5790 	uint32_t result_len;
5791 } ocs_hw_linkcfg_cb_arg_t;
5792 
5793 /**
5794  * @brief Set link configuration.
5795  *
5796  * @param hw Hardware context.
5797  * @param value Link configuration enum to which the link configuration is
5798  * set.
5799  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5800  * @param cb Callback function to invoke following mbx command.
5801  * @param arg Callback argument.
5802  *
5803  * @return Returns OCS_HW_RTN_SUCCESS on success.
5804  */
5805 static ocs_hw_rtn_e
5806 ocs_hw_set_linkcfg(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5807 {
5808 	if (!sli_link_is_configurable(&hw->sli)) {
5809 		ocs_log_debug(hw->os, "Function not supported\n");
5810 		return OCS_HW_RTN_ERROR;
5811 	}
5812 
5813 	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
5814 		return ocs_hw_set_linkcfg_lancer(hw, value, opts, cb, arg);
5815 	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
5816 		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
5817 		return ocs_hw_set_linkcfg_skyhawk(hw, value, opts, cb, arg);
5818 	} else {
5819 		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
5820 		return OCS_HW_RTN_ERROR;
5821 	}
5822 }
5823 
5824 /**
5825  * @brief Set link configuration for Lancer
5826  *
5827  * @param hw Hardware context.
5828  * @param value Link configuration enum to which the link configuration is
5829  * set.
5830  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5831  * @param cb Callback function to invoke following mbx command.
5832  * @param arg Callback argument.
5833  *
5834  * @return Returns OCS_HW_RTN_SUCCESS on success.
5835  */
5836 static ocs_hw_rtn_e
5837 ocs_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)
5838 {
5839 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
5840 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5841 	const char *value_str = NULL;
5842 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5843 
5844 	/* translate ocs_hw_linkcfg_e to CLP string */
5845 	value_str = ocs_hw_clp_from_linkcfg(value);
5846 
5847 	/* allocate memory for callback argument */
5848 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
5849 	if (cb_arg == NULL) {
5850 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5851 		return OCS_HW_RTN_NO_MEMORY;
5852 	}
5853 
5854 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_LinkConfig=%s", value_str);
5855 	/* allocate DMA for command  */
5856 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
5857 		ocs_log_err(hw->os, "malloc failed\n");
5858 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5859 		return OCS_HW_RTN_NO_MEMORY;
5860 	}
5861 	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
5862 	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
5863 
5864 	/* allocate DMA for response */
5865 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
5866 		ocs_log_err(hw->os, "malloc failed\n");
5867 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5868 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5869 		return OCS_HW_RTN_NO_MEMORY;
5870 	}
5871 	cb_arg->cb = cb;
5872 	cb_arg->arg = arg;
5873 	cb_arg->opts = opts;
5874 
5875 	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
5876 					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
5877 
5878 	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
5879 		/* if failed, or polling, free memory here; if success and not
5880 		 * polling, will free in callback function
5881 		 */
5882 		if (rc) {
5883 			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
5884 					(char *)cb_arg->dma_cmd.virt);
5885 		}
5886 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5887 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
5888 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5889 	}
5890 	return rc;
5891 }
5892 
5893 /**
5894  * @brief Callback for ocs_hw_set_linkcfg_skyhawk
5895  *
5896  * @param hw Hardware context.
5897  * @param status Status from the RECONFIG_GET_LINK_INFO command.
5898  * @param mqe Mailbox response structure.
5899  * @param arg Pointer to a callback argument.
5900  *
5901  * @return none
5902  */
5903 static void
5904 ocs_hw_set_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5905 {
5906 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
5907 
5908 	if (status) {
5909 		ocs_log_test(hw->os, "SET_RECONFIG_LINK_ID failed, status=%d\n", status);
5910 	}
5911 
5912 	/* invoke callback */
5913 	if (cb_arg->cb) {
5914 		cb_arg->cb(status, 0, cb_arg->arg);
5915 	}
5916 
5917 	/* if polling, will free memory in calling function */
5918 	if (cb_arg->opts != OCS_CMD_POLL) {
5919 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5920 	}
5921 }
5922 
5923 /**
5924  * @brief Set link configuration for a Skyhawk
5925  *
5926  * @param hw Hardware context.
5927  * @param value Link configuration enum to which the link configuration is
5928  * set.
5929  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5930  * @param cb Callback function to invoke following mbx command.
5931  * @param arg Callback argument.
5932  *
5933  * @return Returns OCS_HW_RTN_SUCCESS on success.
5934  */
5935 static ocs_hw_rtn_e
5936 ocs_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)
5937 {
5938 	uint8_t *mbxdata;
5939 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5940 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5941 	uint32_t config_id;
5942 
5943 	config_id = ocs_hw_config_id_from_linkcfg(value);
5944 
5945 	if (config_id == 0) {
5946 		ocs_log_test(hw->os, "Link config %d not supported by Skyhawk\n", value);
5947 		return OCS_HW_RTN_ERROR;
5948 	}
5949 
5950 	/* mbxdata holds the header of the command */
5951 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5952 	if (mbxdata == NULL) {
5953 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5954 		return OCS_HW_RTN_NO_MEMORY;
5955 	}
5956 
5957 	/* cb_arg holds the data that will be passed to the callback on completion */
5958 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
5959 	if (cb_arg == NULL) {
5960 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5961 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5962 		return OCS_HW_RTN_NO_MEMORY;
5963 	}
5964 
5965 	cb_arg->cb = cb;
5966 	cb_arg->arg = arg;
5967 
5968 	if (sli_cmd_common_set_reconfig_link_id(&hw->sli, mbxdata, SLI4_BMBX_SIZE, NULL, 0, config_id)) {
5969 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_set_active_link_config_cb, cb_arg);
5970 	}
5971 
5972 	if (rc != OCS_HW_RTN_SUCCESS) {
5973 		ocs_log_err(hw->os, "SET_RECONFIG_LINK_ID failed\n");
5974 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5975 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
5976 	} else if (opts == OCS_CMD_POLL) {
5977 		/* if we're polling we have to call the callback here. */
5978 		ocs_hw_set_active_link_config_cb(hw, 0, mbxdata, cb_arg);
5979 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5980 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
5981 	} else {
5982 		/* We weren't poling, so the callback got called */
5983 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5984 	}
5985 
5986 	return rc;
5987 }
5988 
5989 /**
5990  * @brief Get link configuration.
5991  *
5992  * @param hw Hardware context.
5993  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5994  * @param cb Callback function to invoke following mbx command.
5995  * @param arg Callback argument.
5996  *
5997  * @return Returns OCS_HW_RTN_SUCCESS on success.
5998  */
5999 static ocs_hw_rtn_e
6000 ocs_hw_get_linkcfg(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6001 {
6002 	if (!sli_link_is_configurable(&hw->sli)) {
6003 		ocs_log_debug(hw->os, "Function not supported\n");
6004 		return OCS_HW_RTN_ERROR;
6005 	}
6006 
6007 	if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
6008 	    (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))){
6009 		return ocs_hw_get_linkcfg_lancer(hw, opts, cb, arg);
6010 	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
6011 		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
6012 		return ocs_hw_get_linkcfg_skyhawk(hw, opts, cb, arg);
6013 	} else {
6014 		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
6015 		return OCS_HW_RTN_ERROR;
6016 	}
6017 }
6018 
6019 /**
6020  * @brief Get link configuration for a Lancer
6021  *
6022  * @param hw Hardware context.
6023  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6024  * @param cb Callback function to invoke following mbx command.
6025  * @param arg Callback argument.
6026  *
6027  * @return Returns OCS_HW_RTN_SUCCESS on success.
6028  */
6029 static ocs_hw_rtn_e
6030 ocs_hw_get_linkcfg_lancer(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6031 {
6032 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6033 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6034 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6035 
6036 	/* allocate memory for callback argument */
6037 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6038 	if (cb_arg == NULL) {
6039 		ocs_log_err(hw->os, "failed to malloc cb_arg");
6040 		return OCS_HW_RTN_NO_MEMORY;
6041 	}
6042 
6043 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "show / OEMELX_LinkConfig");
6044 
6045 	/* allocate DMA for command  */
6046 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6047 		ocs_log_err(hw->os, "malloc failed\n");
6048 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6049 		return OCS_HW_RTN_NO_MEMORY;
6050 	}
6051 
6052 	/* copy CLP command to DMA command */
6053 	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6054 	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
6055 
6056 	/* allocate DMA for response */
6057 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6058 		ocs_log_err(hw->os, "malloc failed\n");
6059 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6060 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6061 		return OCS_HW_RTN_NO_MEMORY;
6062 	}
6063 	cb_arg->cb = cb;
6064 	cb_arg->arg = arg;
6065 	cb_arg->opts = opts;
6066 
6067 	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
6068 					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
6069 
6070 	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6071 		/* if failed or polling, free memory here; if not polling and success,
6072 		 * will free in callback function
6073 		 */
6074 		if (rc) {
6075 			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
6076 					(char *)cb_arg->dma_cmd.virt);
6077 		}
6078 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6079 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6080 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6081 	}
6082 	return rc;
6083 }
6084 
6085 /**
6086  * @brief Get the link configuration callback.
6087  *
6088  * @param hw Hardware context.
6089  * @param status Status from the RECONFIG_GET_LINK_INFO command.
6090  * @param mqe Mailbox response structure.
6091  * @param arg Pointer to a callback argument.
6092  *
6093  * @return none
6094  */
6095 static void
6096 ocs_hw_get_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6097 {
6098 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6099 	sli4_res_common_get_reconfig_link_info_t *rsp = cb_arg->dma_cmd.virt;
6100 	ocs_hw_linkcfg_e value = OCS_HW_LINKCFG_NA;
6101 
6102 	if (status) {
6103 		ocs_log_test(hw->os, "GET_RECONFIG_LINK_INFO failed, status=%d\n", status);
6104 	} else {
6105 		/* Call was successful */
6106 		value = ocs_hw_linkcfg_from_config_id(rsp->active_link_config_id);
6107 	}
6108 
6109 	/* invoke callback */
6110 	if (cb_arg->cb) {
6111 		cb_arg->cb(status, value, cb_arg->arg);
6112 	}
6113 
6114 	/* if polling, will free memory in calling function */
6115 	if (cb_arg->opts != OCS_CMD_POLL) {
6116 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6117 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6118 	}
6119 }
6120 
6121 /**
6122  * @brief Get link configuration for a Skyhawk.
6123  *
6124  * @param hw Hardware context.
6125  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6126  * @param cb Callback function to invoke following mbx command.
6127  * @param arg Callback argument.
6128  *
6129  * @return Returns OCS_HW_RTN_SUCCESS on success.
6130  */
6131 static ocs_hw_rtn_e
6132 ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6133 {
6134 	uint8_t *mbxdata;
6135 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6136 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6137 
6138 	/* mbxdata holds the header of the command */
6139 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6140 	if (mbxdata == NULL) {
6141 		ocs_log_err(hw->os, "failed to malloc mbox\n");
6142 		return OCS_HW_RTN_NO_MEMORY;
6143 	}
6144 
6145 	/* cb_arg holds the data that will be passed to the callback on completion */
6146 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
6147 	if (cb_arg == NULL) {
6148 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
6149 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6150 		return OCS_HW_RTN_NO_MEMORY;
6151 	}
6152 
6153 	cb_arg->cb = cb;
6154 	cb_arg->arg = arg;
6155 	cb_arg->opts = opts;
6156 
6157 	/* dma_mem holds the non-embedded portion */
6158 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, sizeof(sli4_res_common_get_reconfig_link_info_t), 4)) {
6159 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
6160 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6161 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6162 		return OCS_HW_RTN_NO_MEMORY;
6163 	}
6164 
6165 	if (sli_cmd_common_get_reconfig_link_info(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->dma_cmd)) {
6166 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_get_active_link_config_cb, cb_arg);
6167 	}
6168 
6169 	if (rc != OCS_HW_RTN_SUCCESS) {
6170 		ocs_log_err(hw->os, "GET_RECONFIG_LINK_INFO failed\n");
6171 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6172 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6173 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6174 	} else if (opts == OCS_CMD_POLL) {
6175 		/* if we're polling we have to call the callback here. */
6176 		ocs_hw_get_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6177 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6178 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6179 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6180 	} else {
6181 		/* We weren't poling, so the callback got called */
6182 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6183 	}
6184 
6185 	return rc;
6186 }
6187 
6188 /**
6189  * @brief Sets the DIF seed value.
6190  *
6191  * @param hw Hardware context.
6192  *
6193  * @return Returns OCS_HW_RTN_SUCCESS on success.
6194  */
6195 static ocs_hw_rtn_e
6196 ocs_hw_set_dif_seed(ocs_hw_t *hw)
6197 {
6198 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6199 	uint8_t buf[SLI4_BMBX_SIZE];
6200 	sli4_req_common_set_features_dif_seed_t seed_param;
6201 
6202 	ocs_memset(&seed_param, 0, sizeof(seed_param));
6203 	seed_param.seed = hw->config.dif_seed;
6204 
6205 	/* send set_features command */
6206 	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6207 					SLI4_SET_FEATURES_DIF_SEED,
6208 					4,
6209 					(uint32_t*)&seed_param)) {
6210 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6211 		if (rc) {
6212 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6213 		} else {
6214 			ocs_log_debug(hw->os, "DIF seed set to 0x%x\n",
6215 					hw->config.dif_seed);
6216 		}
6217 	} else {
6218 		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6219 		rc = OCS_HW_RTN_ERROR;
6220 	}
6221 	return rc;
6222 }
6223 
6224 /**
6225  * @brief Sets the DIF mode value.
6226  *
6227  * @param hw Hardware context.
6228  *
6229  * @return Returns OCS_HW_RTN_SUCCESS on success.
6230  */
6231 static ocs_hw_rtn_e
6232 ocs_hw_set_dif_mode(ocs_hw_t *hw)
6233 {
6234 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6235 	uint8_t buf[SLI4_BMBX_SIZE];
6236 	sli4_req_common_set_features_t10_pi_mem_model_t mode_param;
6237 
6238 	ocs_memset(&mode_param, 0, sizeof(mode_param));
6239 	mode_param.tmm = (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? 0 : 1);
6240 
6241 	/* send set_features command */
6242 	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6243 					SLI4_SET_FEATURES_DIF_MEMORY_MODE,
6244 					sizeof(mode_param),
6245 					(uint32_t*)&mode_param)) {
6246 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6247 		if (rc) {
6248 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6249 		} else {
6250 			ocs_log_test(hw->os, "DIF mode set to %s\n",
6251 				(hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? "inline" : "separate"));
6252 		}
6253 	} else {
6254 		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6255 		rc = OCS_HW_RTN_ERROR;
6256 	}
6257 	return rc;
6258 }
6259 
6260 static void
6261 ocs_hw_watchdog_timer_cb(void *arg)
6262 {
6263 	ocs_hw_t *hw = (ocs_hw_t *)arg;
6264 
6265 	ocs_hw_config_watchdog_timer(hw);
6266 	return;
6267 }
6268 
6269 static void
6270 ocs_hw_cb_cfg_watchdog(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6271 {
6272 	uint16_t timeout = hw->watchdog_timeout;
6273 
6274 	if (status != 0) {
6275 		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", status);
6276 	} else {
6277 		if(timeout != 0) {
6278 			/* keeping callback 500ms before timeout to keep heartbeat alive */
6279 			ocs_setup_timer(hw->os, &hw->watchdog_timer, ocs_hw_watchdog_timer_cb, hw, (timeout*1000 - 500) );
6280 		}else {
6281 			ocs_del_timer(&hw->watchdog_timer);
6282 		}
6283 	}
6284 
6285 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6286 	return;
6287 }
6288 
6289 /**
6290  * @brief Set configuration parameters for watchdog timer feature.
6291  *
6292  * @param hw Hardware context.
6293  * @param timeout Timeout for watchdog timer in seconds
6294  *
6295  * @return Returns OCS_HW_RTN_SUCCESS on success.
6296  */
6297 static ocs_hw_rtn_e
6298 ocs_hw_config_watchdog_timer(ocs_hw_t *hw)
6299 {
6300 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6301 	uint8_t *buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
6302 
6303 	if (!buf) {
6304 		ocs_log_err(hw->os, "no buffer for command\n");
6305 		return OCS_HW_RTN_NO_MEMORY;
6306 	}
6307 
6308 	sli4_cmd_lowlevel_set_watchdog(&hw->sli, buf, SLI4_BMBX_SIZE, hw->watchdog_timeout);
6309 	rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_cfg_watchdog, NULL);
6310 	if (rc) {
6311 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
6312 		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", rc);
6313 	}
6314 	return rc;
6315 }
6316 
6317 /**
6318  * @brief Set configuration parameters for auto-generate xfer_rdy T10 PI feature.
6319  *
6320  * @param hw Hardware context.
6321  * @param buf Pointer to a mailbox buffer area.
6322  *
6323  * @return Returns OCS_HW_RTN_SUCCESS on success.
6324  */
6325 static ocs_hw_rtn_e
6326 ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf)
6327 {
6328 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6329 	sli4_req_common_set_features_xfer_rdy_t10pi_t param;
6330 
6331 	ocs_memset(&param, 0, sizeof(param));
6332 	param.rtc = (hw->config.auto_xfer_rdy_ref_tag_is_lba ? 0 : 1);
6333 	param.atv = (hw->config.auto_xfer_rdy_app_tag_valid ? 1 : 0);
6334 	param.tmm = ((hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE) ? 0 : 1);
6335 	param.app_tag = hw->config.auto_xfer_rdy_app_tag_value;
6336 	param.blk_size = hw->config.auto_xfer_rdy_blk_size_chip;
6337 
6338 	switch (hw->config.auto_xfer_rdy_p_type) {
6339 	case 1:
6340 		param.p_type = 0;
6341 		break;
6342 	case 3:
6343 		param.p_type = 2;
6344 		break;
6345 	default:
6346 		ocs_log_err(hw->os, "unsupported p_type %d\n",
6347 			hw->config.auto_xfer_rdy_p_type);
6348 		return OCS_HW_RTN_ERROR;
6349 	}
6350 
6351 	/* build the set_features command */
6352 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6353 				    SLI4_SET_FEATURES_SET_CONFIG_AUTO_XFER_RDY_T10PI,
6354 				    sizeof(param),
6355 				    &param);
6356 
6357 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6358 	if (rc) {
6359 		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6360 	} else {
6361 		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",
6362 				param.rtc, param.atv, param.p_type,
6363 				param.app_tag, param.blk_size);
6364 	}
6365 
6366 	return rc;
6367 }
6368 
6369 /**
6370  * @brief enable sli port health check
6371  *
6372  * @param hw Hardware context.
6373  * @param buf Pointer to a mailbox buffer area.
6374  * @param query current status of the health check feature enabled/disabled
6375  * @param enable if 1: enable 0: disable
6376  * @param buf Pointer to a mailbox buffer area.
6377  *
6378  * @return Returns OCS_HW_RTN_SUCCESS on success.
6379  */
6380 static ocs_hw_rtn_e
6381 ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable)
6382 {
6383 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6384 	uint8_t buf[SLI4_BMBX_SIZE];
6385 	sli4_req_common_set_features_health_check_t param;
6386 
6387 	ocs_memset(&param, 0, sizeof(param));
6388 	param.hck = enable;
6389 	param.qry = query;
6390 
6391 	/* build the set_features command */
6392 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6393 				    SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK,
6394 				    sizeof(param),
6395 				    &param);
6396 
6397 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6398 	if (rc) {
6399 		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6400 	} else {
6401 		ocs_log_test(hw->os, "SLI Port Health Check is enabled \n");
6402 	}
6403 
6404 	return rc;
6405 }
6406 
6407 /**
6408  * @brief Set FTD transfer hint feature
6409  *
6410  * @param hw Hardware context.
6411  * @param fdt_xfer_hint size in bytes where read requests are segmented.
6412  *
6413  * @return Returns OCS_HW_RTN_SUCCESS on success.
6414  */
6415 static ocs_hw_rtn_e
6416 ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint)
6417 {
6418 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6419 	uint8_t buf[SLI4_BMBX_SIZE];
6420 	sli4_req_common_set_features_set_fdt_xfer_hint_t param;
6421 
6422 	ocs_memset(&param, 0, sizeof(param));
6423 	param.fdt_xfer_hint = fdt_xfer_hint;
6424 	/* build the set_features command */
6425 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6426 				    SLI4_SET_FEATURES_SET_FTD_XFER_HINT,
6427 				    sizeof(param),
6428 				    &param);
6429 
6430 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6431 	if (rc) {
6432 		ocs_log_warn(hw->os, "set FDT hint %d failed: %d\n", fdt_xfer_hint, rc);
6433 	} else {
6434 		ocs_log_debug(hw->os, "Set FTD transfer hint to %d\n", param.fdt_xfer_hint);
6435 	}
6436 
6437 	return rc;
6438 }
6439 
6440 /**
6441  * @brief Get the link configuration callback.
6442  *
6443  * @param hw Hardware context.
6444  * @param status Status from the DMTF CLP command.
6445  * @param result_len Length, in bytes, of the DMTF CLP result.
6446  * @param arg Pointer to a callback argument.
6447  *
6448  * @return Returns OCS_HW_RTN_SUCCESS on success.
6449  */
6450 static void
6451 ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg)
6452 {
6453 	int32_t rval;
6454 	char retdata_str[64];
6455 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6456 	ocs_hw_linkcfg_e linkcfg = OCS_HW_LINKCFG_NA;
6457 
6458 	if (status) {
6459 		ocs_log_test(hw->os, "CLP cmd failed, status=%d\n", status);
6460 	} else {
6461 		/* parse CLP response to get return data */
6462 		rval = ocs_hw_clp_resp_get_value(hw, "retdata", retdata_str,
6463 						  sizeof(retdata_str),
6464 						  cb_arg->dma_resp.virt,
6465 						  result_len);
6466 
6467 		if (rval <= 0) {
6468 			ocs_log_err(hw->os, "failed to get retdata %d\n", result_len);
6469 		} else {
6470 			/* translate string into hw enum */
6471 			linkcfg = ocs_hw_linkcfg_from_clp(retdata_str);
6472 		}
6473 	}
6474 
6475 	/* invoke callback */
6476 	if (cb_arg->cb) {
6477 		cb_arg->cb(status, linkcfg, cb_arg->arg);
6478 	}
6479 
6480 	/* if polling, will free memory in calling function */
6481 	if (cb_arg->opts != OCS_CMD_POLL) {
6482 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6483 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6484 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6485 	}
6486 }
6487 
6488 /**
6489  * @brief Set the Lancer dump location
6490  * @par Description
6491  * This function tells a Lancer chip to use a specific DMA
6492  * buffer as a dump location rather than the internal flash.
6493  *
6494  * @param hw Hardware context.
6495  * @param num_buffers The number of DMA buffers to hold the dump (1..n).
6496  * @param dump_buffers DMA buffers to hold the dump.
6497  *
6498  * @return Returns OCS_HW_RTN_SUCCESS on success.
6499  */
6500 ocs_hw_rtn_e
6501 ocs_hw_set_dump_location(ocs_hw_t *hw, uint32_t num_buffers, ocs_dma_t *dump_buffers, uint8_t fdb)
6502 {
6503 	uint8_t bus, dev, func;
6504 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6505 	uint8_t	buf[SLI4_BMBX_SIZE];
6506 
6507 	/*
6508 	 * Make sure the FW is new enough to support this command. If the FW
6509 	 * is too old, the FW will UE.
6510 	 */
6511 	if (hw->workaround.disable_dump_loc) {
6512 		ocs_log_test(hw->os, "FW version is too old for this feature\n");
6513 		return OCS_HW_RTN_ERROR;
6514 	}
6515 
6516 	/* This command is only valid for physical port 0 */
6517 	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
6518 	if (fdb == 0 && func != 0) {
6519 		ocs_log_test(hw->os, "function only valid for pci function 0, %d passed\n",
6520 			     func);
6521 		return OCS_HW_RTN_ERROR;
6522 	}
6523 
6524 	/*
6525 	 * If a single buffer is used, then it may be passed as is to the chip. For multiple buffers,
6526 	 * We must allocate a SGL list and then pass the address of the list to the chip.
6527 	 */
6528 	if (num_buffers > 1) {
6529 		uint32_t sge_size = num_buffers * sizeof(sli4_sge_t);
6530 		sli4_sge_t *sge;
6531 		uint32_t i;
6532 
6533 		if (hw->dump_sges.size < sge_size) {
6534 			ocs_dma_free(hw->os, &hw->dump_sges);
6535 			if (ocs_dma_alloc(hw->os, &hw->dump_sges, sge_size, OCS_MIN_DMA_ALIGNMENT)) {
6536 				ocs_log_err(hw->os, "SGE DMA allocation failed\n");
6537 				return OCS_HW_RTN_NO_MEMORY;
6538 			}
6539 		}
6540 		/* build the SGE list */
6541 		ocs_memset(hw->dump_sges.virt, 0, hw->dump_sges.size);
6542 		hw->dump_sges.len = sge_size;
6543 		sge = hw->dump_sges.virt;
6544 		for (i = 0; i < num_buffers; i++) {
6545 			sge[i].buffer_address_high = ocs_addr32_hi(dump_buffers[i].phys);
6546 			sge[i].buffer_address_low = ocs_addr32_lo(dump_buffers[i].phys);
6547 			sge[i].last = (i == num_buffers - 1 ? 1 : 0);
6548 			sge[i].buffer_length = dump_buffers[i].size;
6549 		}
6550 		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6551 						      SLI4_BMBX_SIZE, FALSE, TRUE,
6552 						      &hw->dump_sges, fdb);
6553 	} else {
6554 		dump_buffers->len = dump_buffers->size;
6555 		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6556 						      SLI4_BMBX_SIZE, FALSE, FALSE,
6557 						      dump_buffers, fdb);
6558 	}
6559 
6560 	if (rc) {
6561 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL,
6562 				     NULL, NULL);
6563 		if (rc) {
6564 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n",
6565 				rc);
6566 		}
6567 	} else {
6568 		ocs_log_err(hw->os,
6569 			"sli_cmd_common_set_dump_location failed\n");
6570 		rc = OCS_HW_RTN_ERROR;
6571 	}
6572 
6573 	return rc;
6574 }
6575 
6576 /**
6577  * @brief Set the Ethernet license.
6578  *
6579  * @par Description
6580  * This function sends the appropriate mailbox command (DMTF
6581  * CLP) to set the Ethernet license to the given license value.
6582  * Since it is used during the time of ocs_hw_init(), the mailbox
6583  * command is sent via polling (the BMBX route).
6584  *
6585  * @param hw Hardware context.
6586  * @param license 32-bit license value.
6587  *
6588  * @return Returns OCS_HW_RTN_SUCCESS on success.
6589  */
6590 static ocs_hw_rtn_e
6591 ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license)
6592 {
6593 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6594 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6595 	ocs_dma_t dma_cmd;
6596 	ocs_dma_t dma_resp;
6597 
6598 	/* only for lancer right now */
6599 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6600 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6601 		return OCS_HW_RTN_ERROR;
6602 	}
6603 
6604 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_Ethernet_License=%X", license);
6605 	/* allocate DMA for command  */
6606 	if (ocs_dma_alloc(hw->os, &dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6607 		ocs_log_err(hw->os, "malloc failed\n");
6608 		return OCS_HW_RTN_NO_MEMORY;
6609 	}
6610 	ocs_memset(dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6611 	ocs_memcpy(dma_cmd.virt, cmd, ocs_strlen(cmd));
6612 
6613 	/* allocate DMA for response */
6614 	if (ocs_dma_alloc(hw->os, &dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6615 		ocs_log_err(hw->os, "malloc failed\n");
6616 		ocs_dma_free(hw->os, &dma_cmd);
6617 		return OCS_HW_RTN_NO_MEMORY;
6618 	}
6619 
6620 	/* send DMTF CLP command mbx and poll */
6621 	if (ocs_hw_exec_dmtf_clp_cmd(hw, &dma_cmd, &dma_resp, OCS_CMD_POLL, NULL, NULL)) {
6622 		ocs_log_err(hw->os, "CLP cmd=\"%s\" failed\n", (char *)dma_cmd.virt);
6623 		rc = OCS_HW_RTN_ERROR;
6624 	}
6625 
6626 	ocs_dma_free(hw->os, &dma_cmd);
6627 	ocs_dma_free(hw->os, &dma_resp);
6628 	return rc;
6629 }
6630 
6631 /**
6632  * @brief Callback argument structure for the DMTF CLP commands.
6633  */
6634 typedef struct ocs_hw_clp_cb_arg_s {
6635 	ocs_hw_dmtf_clp_cb_t cb;
6636 	ocs_dma_t *dma_resp;
6637 	int32_t status;
6638 	uint32_t opts;
6639 	void *arg;
6640 } ocs_hw_clp_cb_arg_t;
6641 
6642 /**
6643  * @brief Execute the DMTF CLP command.
6644  *
6645  * @param hw Hardware context.
6646  * @param dma_cmd DMA buffer containing the CLP command.
6647  * @param dma_resp DMA buffer that will contain the response (if successful).
6648  * @param opts Mailbox command options (such as OCS_CMD_NOWAIT and POLL).
6649  * @param cb Callback function.
6650  * @param arg Callback argument.
6651  *
6652  * @return Returns the number of bytes written to the response
6653  * buffer on success, or a negative value if failed.
6654  */
6655 static ocs_hw_rtn_e
6656 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)
6657 {
6658 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6659 	ocs_hw_clp_cb_arg_t *cb_arg;
6660 	uint8_t *mbxdata;
6661 
6662 	/* allocate DMA for mailbox */
6663 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6664 	if (mbxdata == NULL) {
6665 		ocs_log_err(hw->os, "failed to malloc mbox\n");
6666 		return OCS_HW_RTN_NO_MEMORY;
6667 	}
6668 
6669 	/* allocate memory for callback argument */
6670 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6671 	if (cb_arg == NULL) {
6672 		ocs_log_err(hw->os, "failed to malloc cb_arg");
6673 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6674 		return OCS_HW_RTN_NO_MEMORY;
6675 	}
6676 
6677 	cb_arg->cb = cb;
6678 	cb_arg->arg = arg;
6679 	cb_arg->dma_resp = dma_resp;
6680 	cb_arg->opts = opts;
6681 
6682 	/* Send the HW command */
6683 	if (sli_cmd_dmtf_exec_clp_cmd(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
6684 				      dma_cmd, dma_resp)) {
6685 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_dmtf_clp_cb, cb_arg);
6686 
6687 		if (opts == OCS_CMD_POLL && rc == OCS_HW_RTN_SUCCESS) {
6688 			/* if we're polling, copy response and invoke callback to
6689 			 * parse result */
6690 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
6691 			ocs_hw_dmtf_clp_cb(hw, 0, mbxdata, cb_arg);
6692 
6693 			/* set rc to resulting or "parsed" status */
6694 			rc = cb_arg->status;
6695 		}
6696 
6697 		/* if failed, or polling, free memory here */
6698 		if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6699 			if (rc != OCS_HW_RTN_SUCCESS) {
6700 				ocs_log_test(hw->os, "ocs_hw_command failed\n");
6701 			}
6702 			ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6703 			ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6704 		}
6705 	} else {
6706 		ocs_log_test(hw->os, "sli_cmd_dmtf_exec_clp_cmd failed\n");
6707 		rc = OCS_HW_RTN_ERROR;
6708 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6709 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6710 	}
6711 
6712 	return rc;
6713 }
6714 
6715 /**
6716  * @brief Called when the DMTF CLP command completes.
6717  *
6718  * @param hw Hardware context.
6719  * @param status Status field from the mbox completion.
6720  * @param mqe Mailbox response structure.
6721  * @param arg Pointer to a callback argument.
6722  *
6723  * @return None.
6724  *
6725  */
6726 static void
6727 ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6728 {
6729 	int32_t cb_status = 0;
6730 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6731 	sli4_res_dmtf_exec_clp_cmd_t *clp_rsp = (sli4_res_dmtf_exec_clp_cmd_t *) mbox_rsp->payload.embed;
6732 	ocs_hw_clp_cb_arg_t *cb_arg = arg;
6733 	uint32_t result_len = 0;
6734 	int32_t stat_len;
6735 	char stat_str[8];
6736 
6737 	/* there are several status codes here, check them all and condense
6738 	 * into a single callback status
6739 	 */
6740 	if (status || mbox_rsp->hdr.status || clp_rsp->clp_status) {
6741 		ocs_log_debug(hw->os, "status=x%x/x%x/x%x  addl=x%x clp=x%x detail=x%x\n",
6742 			status,
6743 			mbox_rsp->hdr.status,
6744 			clp_rsp->hdr.status,
6745 			clp_rsp->hdr.additional_status,
6746 			clp_rsp->clp_status,
6747 			clp_rsp->clp_detailed_status);
6748 		if (status) {
6749 			cb_status = status;
6750 		} else if (mbox_rsp->hdr.status) {
6751 			cb_status = mbox_rsp->hdr.status;
6752 		} else {
6753 			cb_status = clp_rsp->clp_status;
6754 		}
6755 	} else {
6756 		result_len = clp_rsp->resp_length;
6757 	}
6758 
6759 	if (cb_status) {
6760 		goto ocs_hw_cb_dmtf_clp_done;
6761 	}
6762 
6763 	if ((result_len == 0) || (cb_arg->dma_resp->size < result_len)) {
6764 		ocs_log_test(hw->os, "Invalid response length: resp_len=%zu result len=%d\n",
6765 			     cb_arg->dma_resp->size, result_len);
6766 		cb_status = -1;
6767 		goto ocs_hw_cb_dmtf_clp_done;
6768 	}
6769 
6770 	/* parse CLP response to get status */
6771 	stat_len = ocs_hw_clp_resp_get_value(hw, "status", stat_str,
6772 					      sizeof(stat_str),
6773 					      cb_arg->dma_resp->virt,
6774 					      result_len);
6775 
6776 	if (stat_len <= 0) {
6777 		ocs_log_test(hw->os, "failed to get status %d\n", stat_len);
6778 		cb_status = -1;
6779 		goto ocs_hw_cb_dmtf_clp_done;
6780 	}
6781 
6782 	if (ocs_strcmp(stat_str, "0") != 0) {
6783 		ocs_log_test(hw->os, "CLP status indicates failure=%s\n", stat_str);
6784 		cb_status = -1;
6785 		goto ocs_hw_cb_dmtf_clp_done;
6786 	}
6787 
6788 ocs_hw_cb_dmtf_clp_done:
6789 
6790 	/* save status in cb_arg for callers with NULL cb's + polling */
6791 	cb_arg->status = cb_status;
6792 	if (cb_arg->cb) {
6793 		cb_arg->cb(hw, cb_status, result_len, cb_arg->arg);
6794 	}
6795 	/* if polling, caller will free memory */
6796 	if (cb_arg->opts != OCS_CMD_POLL) {
6797 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6798 		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6799 	}
6800 }
6801 
6802 /**
6803  * @brief Parse the CLP result and get the value corresponding to the given
6804  * keyword.
6805  *
6806  * @param hw Hardware context.
6807  * @param keyword CLP keyword for which the value is returned.
6808  * @param value Location to which the resulting value is copied.
6809  * @param value_len Length of the value parameter.
6810  * @param resp Pointer to the response buffer that is searched
6811  * for the keyword and value.
6812  * @param resp_len Length of response buffer passed in.
6813  *
6814  * @return Returns the number of bytes written to the value
6815  * buffer on success, or a negative vaue on failure.
6816  */
6817 static int32_t
6818 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)
6819 {
6820 	char *start = NULL;
6821 	char *end = NULL;
6822 
6823 	/* look for specified keyword in string */
6824 	start = ocs_strstr(resp, keyword);
6825 	if (start == NULL) {
6826 		ocs_log_test(hw->os, "could not find keyword=%s in CLP response\n",
6827 			     keyword);
6828 		return -1;
6829 	}
6830 
6831 	/* now look for '=' and go one past */
6832 	start = ocs_strchr(start, '=');
6833 	if (start == NULL) {
6834 		ocs_log_test(hw->os, "could not find \'=\' in CLP response for keyword=%s\n",
6835 			     keyword);
6836 		return -1;
6837 	}
6838 	start++;
6839 
6840 	/* \r\n terminates value */
6841 	end = ocs_strstr(start, "\r\n");
6842 	if (end == NULL) {
6843 		ocs_log_test(hw->os, "could not find \\r\\n for keyword=%s in CLP response\n",
6844 			     keyword);
6845 		return -1;
6846 	}
6847 
6848 	/* make sure given result array is big enough */
6849 	if ((end - start + 1) > value_len) {
6850 		ocs_log_test(hw->os, "value len=%d not large enough for actual=%ld\n",
6851 			     value_len, (end-start));
6852 		return -1;
6853 	}
6854 
6855 	ocs_strncpy(value, start, (end - start));
6856 	value[end-start] = '\0';
6857 	return (end-start+1);
6858 }
6859 
6860 /**
6861  * @brief Cause chip to enter an unrecoverable error state.
6862  *
6863  * @par Description
6864  * Cause chip to enter an unrecoverable error state. This is
6865  * used when detecting unexpected FW behavior so that the FW can be
6866  * hwted from the driver as soon as the error is detected.
6867  *
6868  * @param hw Hardware context.
6869  * @param dump Generate dump as part of reset.
6870  *
6871  * @return Returns 0 on success, or a non-zero value on failure.
6872  *
6873  */
6874 ocs_hw_rtn_e
6875 ocs_hw_raise_ue(ocs_hw_t *hw, uint8_t dump)
6876 {
6877 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6878 
6879 	if (sli_raise_ue(&hw->sli, dump) != 0) {
6880 		rc = OCS_HW_RTN_ERROR;
6881 	} else {
6882 		if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
6883 			hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
6884 		}
6885 	}
6886 
6887 	return rc;
6888 }
6889 
6890 /**
6891  * @brief Called when the OBJECT_GET command completes.
6892  *
6893  * @par Description
6894  * Get the number of bytes actually written out of the response, free the mailbox
6895  * that was malloc'd by ocs_hw_dump_get(), then call the callback
6896  * and pass the status and bytes read.
6897  *
6898  * @param hw Hardware context.
6899  * @param status Status field from the mbox completion.
6900  * @param mqe Mailbox response structure.
6901  * @param arg Pointer to a callback function that signals the caller that the command is done.
6902  * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_read)</tt>.
6903  *
6904  * @return Returns 0.
6905  */
6906 static int32_t
6907 ocs_hw_cb_dump_get(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6908 {
6909 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6910 	sli4_res_common_read_object_t* rd_obj_rsp = (sli4_res_common_read_object_t*) mbox_rsp->payload.embed;
6911 	ocs_hw_dump_get_cb_arg_t *cb_arg = arg;
6912 	uint32_t bytes_read;
6913 	uint8_t eof;
6914 
6915 	bytes_read = rd_obj_rsp->actual_read_length;
6916 	eof = rd_obj_rsp->eof;
6917 
6918 	if (cb_arg) {
6919 		if (cb_arg->cb) {
6920 			if ((status == 0) && mbox_rsp->hdr.status) {
6921 				status = mbox_rsp->hdr.status;
6922 			}
6923 			cb_arg->cb(status, bytes_read, eof, cb_arg->arg);
6924 		}
6925 
6926 		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
6927 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
6928 	}
6929 
6930 	return 0;
6931 }
6932 
6933 /**
6934  * @brief Read a dump image to the host.
6935  *
6936  * @par Description
6937  * Creates a SLI_CONFIG mailbox command, fills in the correct values to read a
6938  * dump image chunk, then sends the command with the ocs_hw_command(). On completion,
6939  * the callback function ocs_hw_cb_dump_get() gets called to free the mailbox
6940  * and signal the caller that the read has completed.
6941  *
6942  * @param hw Hardware context.
6943  * @param dma DMA structure to transfer the dump chunk into.
6944  * @param size Size of the dump chunk.
6945  * @param offset Offset, in bytes, from the beginning of the dump.
6946  * @param cb Pointer to a callback function that is called when the command completes.
6947  * The callback function prototype is
6948  * <tt>void cb(int32_t status, uint32_t bytes_read, uint8_t eof, void *arg)</tt>.
6949  * @param arg Pointer to be passed to the callback function.
6950  *
6951  * @return Returns 0 on success, or a non-zero value on failure.
6952  */
6953 ocs_hw_rtn_e
6954 ocs_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)
6955 {
6956 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6957 	uint8_t *mbxdata;
6958 	ocs_hw_dump_get_cb_arg_t *cb_arg;
6959 	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
6960 
6961 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6962 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6963 		return OCS_HW_RTN_ERROR;
6964 	}
6965 
6966 	if (1 != sli_dump_is_present(&hw->sli)) {
6967 		ocs_log_test(hw->os, "No dump is present\n");
6968 		return OCS_HW_RTN_ERROR;
6969 	}
6970 
6971 	if (1 == sli_reset_required(&hw->sli)) {
6972 		ocs_log_test(hw->os, "device reset required\n");
6973 		return OCS_HW_RTN_ERROR;
6974 	}
6975 
6976 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6977 	if (mbxdata == NULL) {
6978 		ocs_log_err(hw->os, "failed to malloc mbox\n");
6979 		return OCS_HW_RTN_NO_MEMORY;
6980 	}
6981 
6982 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_get_cb_arg_t), OCS_M_NOWAIT);
6983 	if (cb_arg == NULL) {
6984 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
6985 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6986 		return OCS_HW_RTN_NO_MEMORY;
6987 	}
6988 
6989 	cb_arg->cb = cb;
6990 	cb_arg->arg = arg;
6991 	cb_arg->mbox_cmd = mbxdata;
6992 
6993 	if (sli_cmd_common_read_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
6994 			size, offset, "/dbg/dump.bin", dma)) {
6995 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_get, cb_arg);
6996 		if (rc == 0 && opts == OCS_CMD_POLL) {
6997 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
6998 			rc = ocs_hw_cb_dump_get(hw, 0, mbxdata, cb_arg);
6999 		}
7000 	}
7001 
7002 	if (rc != OCS_HW_RTN_SUCCESS) {
7003 		ocs_log_test(hw->os, "COMMON_READ_OBJECT failed\n");
7004 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7005 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
7006 	}
7007 
7008 	return rc;
7009 }
7010 
7011 /**
7012  * @brief Called when the OBJECT_DELETE command completes.
7013  *
7014  * @par Description
7015  * Free the mailbox that was malloc'd
7016  * by ocs_hw_dump_clear(), then call the callback and pass the status.
7017  *
7018  * @param hw Hardware context.
7019  * @param status Status field from the mbox completion.
7020  * @param mqe Mailbox response structure.
7021  * @param arg Pointer to a callback function that signals the caller that the command is done.
7022  * The callback function prototype is <tt>void cb(int32_t status, void *arg)</tt>.
7023  *
7024  * @return Returns 0.
7025  */
7026 static int32_t
7027 ocs_hw_cb_dump_clear(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
7028 {
7029 	ocs_hw_dump_clear_cb_arg_t *cb_arg = arg;
7030 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7031 
7032 	if (cb_arg) {
7033 		if (cb_arg->cb) {
7034 			if ((status == 0) && mbox_rsp->hdr.status) {
7035 				status = mbox_rsp->hdr.status;
7036 			}
7037 			cb_arg->cb(status, cb_arg->arg);
7038 		}
7039 
7040 		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
7041 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7042 	}
7043 
7044 	return 0;
7045 }
7046 
7047 /**
7048  * @brief Clear a dump image from the device.
7049  *
7050  * @par Description
7051  * Creates a SLI_CONFIG mailbox command, fills it with the correct values to clear
7052  * the dump, then sends the command with ocs_hw_command(). On completion,
7053  * the callback function ocs_hw_cb_dump_clear() gets called to free the mailbox
7054  * and to signal the caller that the write has completed.
7055  *
7056  * @param hw Hardware context.
7057  * @param cb Pointer to a callback function that is called when the command completes.
7058  * The callback function prototype is
7059  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
7060  * @param arg Pointer to be passed to the callback function.
7061  *
7062  * @return Returns 0 on success, or a non-zero value on failure.
7063  */
7064 ocs_hw_rtn_e
7065 ocs_hw_dump_clear(ocs_hw_t *hw, ocs_hw_dump_clear_cb_t cb, void *arg)
7066 {
7067 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7068 	uint8_t *mbxdata;
7069 	ocs_hw_dump_clear_cb_arg_t *cb_arg;
7070 	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
7071 
7072 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
7073 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
7074 		return OCS_HW_RTN_ERROR;
7075 	}
7076 
7077 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7078 	if (mbxdata == NULL) {
7079 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7080 		return OCS_HW_RTN_NO_MEMORY;
7081 	}
7082 
7083 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_clear_cb_arg_t), OCS_M_NOWAIT);
7084 	if (cb_arg == NULL) {
7085 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7086 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7087 		return OCS_HW_RTN_NO_MEMORY;
7088 	}
7089 
7090 	cb_arg->cb = cb;
7091 	cb_arg->arg = arg;
7092 	cb_arg->mbox_cmd = mbxdata;
7093 
7094 	if (sli_cmd_common_delete_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7095 			"/dbg/dump.bin")) {
7096 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_clear, cb_arg);
7097 		if (rc == 0 && opts == OCS_CMD_POLL) {
7098 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7099 			rc = ocs_hw_cb_dump_clear(hw, 0, mbxdata, cb_arg);
7100 		}
7101 	}
7102 
7103 	if (rc != OCS_HW_RTN_SUCCESS) {
7104 		ocs_log_test(hw->os, "COMMON_DELETE_OBJECT failed\n");
7105 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7106 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7107 	}
7108 
7109 	return rc;
7110 }
7111 
7112 typedef struct ocs_hw_get_port_protocol_cb_arg_s {
7113 	ocs_get_port_protocol_cb_t cb;
7114 	void *arg;
7115 	uint32_t pci_func;
7116 	ocs_dma_t payload;
7117 } ocs_hw_get_port_protocol_cb_arg_t;
7118 
7119 /**
7120  * @brief Called for the completion of get_port_profile for a
7121  *        user request.
7122  *
7123  * @param hw Hardware context.
7124  * @param status The status from the MQE.
7125  * @param mqe Pointer to mailbox command buffer.
7126  * @param arg Pointer to a callback argument.
7127  *
7128  * @return Returns 0 on success, or a non-zero value on failure.
7129  */
7130 static int32_t
7131 ocs_hw_get_port_protocol_cb(ocs_hw_t *hw, int32_t status,
7132 			    uint8_t *mqe, void *arg)
7133 {
7134 	ocs_hw_get_port_protocol_cb_arg_t *cb_arg = arg;
7135 	ocs_dma_t *payload = &(cb_arg->payload);
7136 	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7137 	ocs_hw_port_protocol_e port_protocol;
7138 	int num_descriptors;
7139 	sli4_resource_descriptor_v1_t *desc_p;
7140 	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7141 	int i;
7142 
7143 	port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7144 
7145 	num_descriptors = response->desc_count;
7146 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7147 	for (i=0; i<num_descriptors; i++) {
7148 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7149 			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7150 			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7151 				switch(pcie_desc_p->pf_type) {
7152 				case 0x02:
7153 					port_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
7154 					break;
7155 				case 0x04:
7156 					port_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
7157 					break;
7158 				case 0x10:
7159 					port_protocol = OCS_HW_PORT_PROTOCOL_FC;
7160 					break;
7161 				default:
7162 					port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7163 					break;
7164 				}
7165 			}
7166 		}
7167 
7168 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7169 	}
7170 
7171 	if (cb_arg->cb) {
7172 		cb_arg->cb(status, port_protocol, cb_arg->arg);
7173 	}
7174 
7175 	ocs_dma_free(hw->os, &cb_arg->payload);
7176 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7177 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7178 
7179 	return 0;
7180 }
7181 
7182 /**
7183  * @ingroup io
7184  * @brief  Get the current port protocol.
7185  * @par Description
7186  * Issues a SLI4 COMMON_GET_PROFILE_CONFIG mailbox.  When the
7187  * command completes the provided mgmt callback function is
7188  * called.
7189  *
7190  * @param hw Hardware context.
7191  * @param pci_func PCI function to query for current protocol.
7192  * @param cb Callback function to be called when the command completes.
7193  * @param ul_arg An argument that is passed to the callback function.
7194  *
7195  * @return
7196  * - OCS_HW_RTN_SUCCESS on success.
7197  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7198  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7199  *   context.
7200  * - OCS_HW_RTN_ERROR on any other error.
7201  */
7202 ocs_hw_rtn_e
7203 ocs_hw_get_port_protocol(ocs_hw_t *hw, uint32_t pci_func,
7204 	ocs_get_port_protocol_cb_t cb, void* ul_arg)
7205 {
7206 	uint8_t *mbxdata;
7207 	ocs_hw_get_port_protocol_cb_arg_t *cb_arg;
7208 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7209 
7210 	/* Only supported on Skyhawk */
7211 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7212 		return OCS_HW_RTN_ERROR;
7213 	}
7214 
7215 	/* mbxdata holds the header of the command */
7216 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7217 	if (mbxdata == NULL) {
7218 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7219 		return OCS_HW_RTN_NO_MEMORY;
7220 	}
7221 
7222 	/* cb_arg holds the data that will be passed to the callback on completion */
7223 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7224 	if (cb_arg == NULL) {
7225 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7226 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7227 		return OCS_HW_RTN_NO_MEMORY;
7228 	}
7229 
7230 	cb_arg->cb = cb;
7231 	cb_arg->arg = ul_arg;
7232 	cb_arg->pci_func = pci_func;
7233 
7234 	/* dma_mem holds the non-embedded portion */
7235 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7236 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7237 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7238 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7239 		return OCS_HW_RTN_NO_MEMORY;
7240 	}
7241 
7242 	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7243 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_port_protocol_cb, cb_arg);
7244 	}
7245 
7246 	if (rc != OCS_HW_RTN_SUCCESS) {
7247 		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7248 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7249 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7250 		ocs_dma_free(hw->os, &cb_arg->payload);
7251 	}
7252 
7253 	return rc;
7254 
7255 }
7256 
7257 typedef struct ocs_hw_set_port_protocol_cb_arg_s {
7258 	ocs_set_port_protocol_cb_t cb;
7259 	void *arg;
7260 	ocs_dma_t payload;
7261 	uint32_t new_protocol;
7262 	uint32_t pci_func;
7263 } ocs_hw_set_port_protocol_cb_arg_t;
7264 
7265 /**
7266  * @brief Called for the completion of set_port_profile for a
7267  *        user request.
7268  *
7269  * @par Description
7270  * This is the second of two callbacks for the set_port_protocol
7271  * function. The set operation is a read-modify-write. This
7272  * callback is called when the write (SET_PROFILE_CONFIG)
7273  * completes.
7274  *
7275  * @param hw Hardware context.
7276  * @param status The status from the MQE.
7277  * @param mqe Pointer to mailbox command buffer.
7278  * @param arg Pointer to a callback argument.
7279  *
7280  * @return 0 on success, non-zero otherwise
7281  */
7282 static int32_t
7283 ocs_hw_set_port_protocol_cb2(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7284 {
7285 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7286 
7287 	if (cb_arg->cb) {
7288 		cb_arg->cb( status, cb_arg->arg);
7289 	}
7290 
7291 	ocs_dma_free(hw->os, &(cb_arg->payload));
7292 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7293 	ocs_free(hw->os, arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7294 
7295 	return 0;
7296 }
7297 
7298 /**
7299  * @brief Called for the completion of set_port_profile for a
7300  *        user request.
7301  *
7302  * @par Description
7303  * This is the first of two callbacks for the set_port_protocol
7304  * function.  The set operation is a read-modify-write.  This
7305  * callback is called when the read completes
7306  * (GET_PROFILE_CONFG).  It will updated the resource
7307  * descriptors, then queue the write (SET_PROFILE_CONFIG).
7308  *
7309  * On entry there are three memory areas that were allocated by
7310  * ocs_hw_set_port_protocol.  If a failure is detected in this
7311  * function those need to be freed.  If this function succeeds
7312  * it allocates three more areas.
7313  *
7314  * @param hw Hardware context.
7315  * @param status The status from the MQE
7316  * @param mqe Pointer to mailbox command buffer.
7317  * @param arg Pointer to a callback argument.
7318  *
7319  * @return Returns 0 on success, or a non-zero value otherwise.
7320  */
7321 static int32_t
7322 ocs_hw_set_port_protocol_cb1(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7323 {
7324 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7325 	ocs_dma_t *payload = &(cb_arg->payload);
7326 	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7327 	int num_descriptors;
7328 	sli4_resource_descriptor_v1_t *desc_p;
7329 	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7330 	int i;
7331 	ocs_hw_set_port_protocol_cb_arg_t *new_cb_arg;
7332 	ocs_hw_port_protocol_e new_protocol;
7333 	uint8_t *dst;
7334 	sli4_isap_resouce_descriptor_v1_t *isap_desc_p;
7335 	uint8_t *mbxdata;
7336 	int pci_descriptor_count;
7337 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7338 	int num_fcoe_ports = 0;
7339 	int num_iscsi_ports = 0;
7340 
7341 	new_protocol = (ocs_hw_port_protocol_e)cb_arg->new_protocol;
7342 
7343 	num_descriptors = response->desc_count;
7344 
7345 	/* Count PCI descriptors */
7346 	pci_descriptor_count = 0;
7347 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7348 	for (i=0; i<num_descriptors; i++) {
7349 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7350 			++pci_descriptor_count;
7351 		}
7352 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7353 	}
7354 
7355 	/* mbxdata holds the header of the command */
7356 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7357 	if (mbxdata == NULL) {
7358 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7359 		return OCS_HW_RTN_NO_MEMORY;
7360 	}
7361 
7362 	/* cb_arg holds the data that will be passed to the callback on completion */
7363 	new_cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7364 	if (new_cb_arg == NULL) {
7365 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7366 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7367 		return OCS_HW_RTN_NO_MEMORY;
7368 	}
7369 
7370 	new_cb_arg->cb = cb_arg->cb;
7371 	new_cb_arg->arg = cb_arg->arg;
7372 
7373 	/* Allocate memory for the descriptors we're going to send.  This is
7374 	 * one for each PCI descriptor plus one ISAP descriptor. */
7375 	if (ocs_dma_alloc(hw->os, &new_cb_arg->payload, sizeof(sli4_req_common_set_profile_config_t) +
7376 			  (pci_descriptor_count * sizeof(sli4_pcie_resource_descriptor_v1_t)) +
7377 			  sizeof(sli4_isap_resouce_descriptor_v1_t), 4)) {
7378 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7379 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7380 		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7381 		return OCS_HW_RTN_NO_MEMORY;
7382 	}
7383 
7384 	sli_cmd_common_set_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7385 						   &new_cb_arg->payload,
7386 						   0, pci_descriptor_count+1, 1);
7387 
7388 	/* Point dst to the first descriptor entry in the SET_PROFILE_CONFIG command */
7389 	dst = (uint8_t *)&(((sli4_req_common_set_profile_config_t *) new_cb_arg->payload.virt)->desc);
7390 
7391 	/* Loop over all descriptors.  If the descriptor is a PCIe descriptor, copy it
7392 	 * to the SET_PROFILE_CONFIG command to be written back.  If it's the descriptor
7393 	 * that we're trying to change also set its pf_type.
7394 	 */
7395 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7396 	for (i=0; i<num_descriptors; i++) {
7397 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7398 			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7399 			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7400 				/* This is the PCIe descriptor for this OCS instance.
7401 				 * Update it with the new pf_type */
7402 				switch(new_protocol) {
7403 				case OCS_HW_PORT_PROTOCOL_FC:
7404 					pcie_desc_p->pf_type = SLI4_PROTOCOL_FC;
7405 					break;
7406 				case OCS_HW_PORT_PROTOCOL_FCOE:
7407 					pcie_desc_p->pf_type = SLI4_PROTOCOL_FCOE;
7408 					break;
7409 				case OCS_HW_PORT_PROTOCOL_ISCSI:
7410 					pcie_desc_p->pf_type = SLI4_PROTOCOL_ISCSI;
7411 					break;
7412 				default:
7413 					pcie_desc_p->pf_type = SLI4_PROTOCOL_DEFAULT;
7414 					break;
7415 				}
7416 			}
7417 
7418 			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_FCOE) {
7419 				++num_fcoe_ports;
7420 			}
7421 			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_ISCSI) {
7422 				++num_iscsi_ports;
7423 			}
7424 			ocs_memcpy(dst, pcie_desc_p, sizeof(sli4_pcie_resource_descriptor_v1_t));
7425 			dst += sizeof(sli4_pcie_resource_descriptor_v1_t);
7426 		}
7427 
7428 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7429 	}
7430 
7431 	/* Create an ISAP resource descriptor */
7432 	isap_desc_p = (sli4_isap_resouce_descriptor_v1_t*)dst;
7433 	isap_desc_p->descriptor_type = SLI4_RESOURCE_DESCRIPTOR_TYPE_ISAP;
7434 	isap_desc_p->descriptor_length = sizeof(sli4_isap_resouce_descriptor_v1_t);
7435 	if (num_iscsi_ports > 0) {
7436 		isap_desc_p->iscsi_tgt = 1;
7437 		isap_desc_p->iscsi_ini = 1;
7438 		isap_desc_p->iscsi_dif = 1;
7439 	}
7440 	if (num_fcoe_ports > 0) {
7441 		isap_desc_p->fcoe_tgt = 1;
7442 		isap_desc_p->fcoe_ini = 1;
7443 		isap_desc_p->fcoe_dif = 1;
7444 	}
7445 
7446 	/* At this point we're done with the memory allocated by ocs_port_set_protocol */
7447 	ocs_dma_free(hw->os, &cb_arg->payload);
7448 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7449 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7450 
7451 	/* Send a SET_PROFILE_CONFIG mailbox command with the new descriptors */
7452 	rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb2, new_cb_arg);
7453 	if (rc) {
7454 		ocs_log_err(hw->os, "Error posting COMMON_SET_PROFILE_CONFIG\n");
7455 		/* Call the upper level callback to report a failure */
7456 		if (new_cb_arg->cb) {
7457 			new_cb_arg->cb( rc, new_cb_arg->arg);
7458 		}
7459 
7460 		/* Free the memory allocated by this function */
7461 		ocs_dma_free(hw->os, &new_cb_arg->payload);
7462 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7463 		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7464 	}
7465 
7466 	return rc;
7467 }
7468 
7469 /**
7470  * @ingroup io
7471  * @brief  Set the port protocol.
7472  * @par Description
7473  * Setting the port protocol is a read-modify-write operation.
7474  * This function submits a GET_PROFILE_CONFIG command to read
7475  * the current settings.  The callback function will modify the
7476  * settings and issue the write.
7477  *
7478  * On successful completion this function will have allocated
7479  * two regular memory areas and one dma area which will need to
7480  * get freed later in the callbacks.
7481  *
7482  * @param hw Hardware context.
7483  * @param new_protocol New protocol to use.
7484  * @param pci_func PCI function to configure.
7485  * @param cb Callback function to be called when the command completes.
7486  * @param ul_arg An argument that is passed to the callback function.
7487  *
7488  * @return
7489  * - OCS_HW_RTN_SUCCESS on success.
7490  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7491  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7492  *   context.
7493  * - OCS_HW_RTN_ERROR on any other error.
7494  */
7495 ocs_hw_rtn_e
7496 ocs_hw_set_port_protocol(ocs_hw_t *hw, ocs_hw_port_protocol_e new_protocol,
7497 		uint32_t pci_func, ocs_set_port_protocol_cb_t cb, void *ul_arg)
7498 {
7499 	uint8_t *mbxdata;
7500 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg;
7501 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7502 
7503 	/* Only supported on Skyhawk */
7504 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7505 		return OCS_HW_RTN_ERROR;
7506 	}
7507 
7508 	/* mbxdata holds the header of the command */
7509 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7510 	if (mbxdata == NULL) {
7511 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7512 		return OCS_HW_RTN_NO_MEMORY;
7513 	}
7514 
7515 	/* cb_arg holds the data that will be passed to the callback on completion */
7516 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7517 	if (cb_arg == NULL) {
7518 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7519 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7520 		return OCS_HW_RTN_NO_MEMORY;
7521 	}
7522 
7523 	cb_arg->cb = cb;
7524 	cb_arg->arg = ul_arg;
7525 	cb_arg->new_protocol = new_protocol;
7526 	cb_arg->pci_func = pci_func;
7527 
7528 	/* dma_mem holds the non-embedded portion */
7529 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7530 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7531 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7532 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7533 		return OCS_HW_RTN_NO_MEMORY;
7534 	}
7535 
7536 	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7537 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb1, cb_arg);
7538 	}
7539 
7540 	if (rc != OCS_HW_RTN_SUCCESS) {
7541 		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7542 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7543 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7544 		ocs_dma_free(hw->os, &cb_arg->payload);
7545 	}
7546 
7547 	return rc;
7548 }
7549 
7550 typedef struct ocs_hw_get_profile_list_cb_arg_s {
7551 	ocs_get_profile_list_cb_t cb;
7552 	void *arg;
7553 	ocs_dma_t payload;
7554 } ocs_hw_get_profile_list_cb_arg_t;
7555 
7556 /**
7557  * @brief Called for the completion of get_profile_list for a
7558  *        user request.
7559  * @par Description
7560  * This function is called when the COMMMON_GET_PROFILE_LIST
7561  * mailbox completes.  The response will be in
7562  * ctx->non_embedded_mem.virt.  This function parses the
7563  * response and creates a ocs_hw_profile_list, then calls the
7564  * mgmt_cb callback function and passes that list to it.
7565  *
7566  * @param hw Hardware context.
7567  * @param status The status from the MQE
7568  * @param mqe Pointer to mailbox command buffer.
7569  * @param arg Pointer to a callback argument.
7570  *
7571  * @return Returns 0 on success, or a non-zero value on failure.
7572  */
7573 static int32_t
7574 ocs_hw_get_profile_list_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7575 {
7576 	ocs_hw_profile_list_t *list;
7577 	ocs_hw_get_profile_list_cb_arg_t *cb_arg = arg;
7578 	ocs_dma_t *payload = &(cb_arg->payload);
7579 	sli4_res_common_get_profile_list_t *response = (sli4_res_common_get_profile_list_t *)payload->virt;
7580 	int i;
7581 	int num_descriptors;
7582 
7583 	list = ocs_malloc(hw->os, sizeof(ocs_hw_profile_list_t), OCS_M_ZERO);
7584 	if (list == NULL) {
7585 		ocs_log_err(hw->os, "failed to malloc list\n");
7586 		return OCS_HW_RTN_NO_MEMORY;
7587 	}
7588 
7589 	list->num_descriptors = response->profile_descriptor_count;
7590 
7591 	num_descriptors = list->num_descriptors;
7592 	if (num_descriptors > OCS_HW_MAX_PROFILES) {
7593 		num_descriptors = OCS_HW_MAX_PROFILES;
7594 	}
7595 
7596 	for (i=0; i<num_descriptors; i++) {
7597 		list->descriptors[i].profile_id = response->profile_descriptor[i].profile_id;
7598 		list->descriptors[i].profile_index = response->profile_descriptor[i].profile_index;
7599 		ocs_strcpy(list->descriptors[i].profile_description, (char *)response->profile_descriptor[i].profile_description);
7600 	}
7601 
7602 	if (cb_arg->cb) {
7603 		cb_arg->cb(status, list, cb_arg->arg);
7604 	} else {
7605 		ocs_free(hw->os, list, sizeof(*list));
7606 	}
7607 
7608 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7609 	ocs_dma_free(hw->os, &cb_arg->payload);
7610 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7611 
7612 	return 0;
7613 }
7614 
7615 /**
7616  * @ingroup io
7617  * @brief  Get a list of available profiles.
7618  * @par Description
7619  * Issues a SLI-4 COMMON_GET_PROFILE_LIST mailbox.  When the
7620  * command completes the provided mgmt callback function is
7621  * called.
7622  *
7623  * @param hw Hardware context.
7624  * @param cb Callback function to be called when the
7625  *      	  command completes.
7626  * @param ul_arg An argument that is passed to the callback
7627  *      	 function.
7628  *
7629  * @return
7630  * - OCS_HW_RTN_SUCCESS on success.
7631  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7632  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7633  *   context.
7634  * - OCS_HW_RTN_ERROR on any other error.
7635  */
7636 ocs_hw_rtn_e
7637 ocs_hw_get_profile_list(ocs_hw_t *hw, ocs_get_profile_list_cb_t cb, void* ul_arg)
7638 {
7639 	uint8_t *mbxdata;
7640 	ocs_hw_get_profile_list_cb_arg_t *cb_arg;
7641 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7642 
7643 	/* Only supported on Skyhawk */
7644 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7645 		return OCS_HW_RTN_ERROR;
7646 	}
7647 
7648 	/* mbxdata holds the header of the command */
7649 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7650 	if (mbxdata == NULL) {
7651 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7652 		return OCS_HW_RTN_NO_MEMORY;
7653 	}
7654 
7655 	/* cb_arg holds the data that will be passed to the callback on completion */
7656 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_profile_list_cb_arg_t), OCS_M_NOWAIT);
7657 	if (cb_arg == NULL) {
7658 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7659 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7660 		return OCS_HW_RTN_NO_MEMORY;
7661 	}
7662 
7663 	cb_arg->cb = cb;
7664 	cb_arg->arg = ul_arg;
7665 
7666 	/* dma_mem holds the non-embedded portion */
7667 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_get_profile_list_t), 4)) {
7668 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7669 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7670 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7671 		return OCS_HW_RTN_NO_MEMORY;
7672 	}
7673 
7674 	if (sli_cmd_common_get_profile_list(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, &cb_arg->payload)) {
7675 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_profile_list_cb, cb_arg);
7676 	}
7677 
7678 	if (rc != OCS_HW_RTN_SUCCESS) {
7679 		ocs_log_test(hw->os, "GET_PROFILE_LIST failed\n");
7680 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7681 		ocs_dma_free(hw->os, &cb_arg->payload);
7682 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7683 	}
7684 
7685 	return rc;
7686 }
7687 
7688 typedef struct ocs_hw_get_active_profile_cb_arg_s {
7689 	ocs_get_active_profile_cb_t cb;
7690 	void *arg;
7691 } ocs_hw_get_active_profile_cb_arg_t;
7692 
7693 /**
7694  * @brief Called for the completion of get_active_profile for a
7695  *        user request.
7696  *
7697  * @param hw Hardware context.
7698  * @param status The status from the MQE
7699  * @param mqe Pointer to mailbox command buffer.
7700  * @param arg Pointer to a callback argument.
7701  *
7702  * @return Returns 0 on success, or a non-zero value on failure.
7703  */
7704 static int32_t
7705 ocs_hw_get_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7706 {
7707 	ocs_hw_get_active_profile_cb_arg_t *cb_arg = arg;
7708 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7709 	sli4_res_common_get_active_profile_t* response = (sli4_res_common_get_active_profile_t*) mbox_rsp->payload.embed;
7710 	uint32_t active_profile;
7711 
7712 	active_profile = response->active_profile_id;
7713 
7714 	if (cb_arg->cb) {
7715 		cb_arg->cb(status, active_profile, cb_arg->arg);
7716 	}
7717 
7718 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7719 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7720 
7721 	return 0;
7722 }
7723 
7724 /**
7725  * @ingroup io
7726  * @brief  Get the currently active profile.
7727  * @par Description
7728  * Issues a SLI-4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
7729  * command completes the provided mgmt callback function is
7730  * called.
7731  *
7732  * @param hw Hardware context.
7733  * @param cb Callback function to be called when the
7734  *	     command completes.
7735  * @param ul_arg An argument that is passed to the callback
7736  *      	 function.
7737  *
7738  * @return
7739  * - OCS_HW_RTN_SUCCESS on success.
7740  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7741  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7742  *   context.
7743  * - OCS_HW_RTN_ERROR on any other error.
7744  */
7745 int32_t
7746 ocs_hw_get_active_profile(ocs_hw_t *hw, ocs_get_active_profile_cb_t cb, void* ul_arg)
7747 {
7748 	uint8_t *mbxdata;
7749 	ocs_hw_get_active_profile_cb_arg_t *cb_arg;
7750 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7751 
7752 	/* Only supported on Skyhawk */
7753 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7754 		return OCS_HW_RTN_ERROR;
7755 	}
7756 
7757 	/* mbxdata holds the header of the command */
7758 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7759 	if (mbxdata == NULL) {
7760 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7761 		return OCS_HW_RTN_NO_MEMORY;
7762 	}
7763 
7764 	/* cb_arg holds the data that will be passed to the callback on completion */
7765 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_active_profile_cb_arg_t), OCS_M_NOWAIT);
7766 	if (cb_arg == NULL) {
7767 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7768 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7769 		return OCS_HW_RTN_NO_MEMORY;
7770 	}
7771 
7772 	cb_arg->cb = cb;
7773 	cb_arg->arg = ul_arg;
7774 
7775 	if (sli_cmd_common_get_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7776 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_active_profile_cb, cb_arg);
7777 	}
7778 
7779 	if (rc != OCS_HW_RTN_SUCCESS) {
7780 		ocs_log_test(hw->os, "GET_ACTIVE_PROFILE failed\n");
7781 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7782 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7783 	}
7784 
7785 	return rc;
7786 }
7787 
7788 typedef struct ocs_hw_get_nvparms_cb_arg_s {
7789 	ocs_get_nvparms_cb_t cb;
7790 	void *arg;
7791 } ocs_hw_get_nvparms_cb_arg_t;
7792 
7793 /**
7794  * @brief Called for the completion of get_nvparms for a
7795  *        user request.
7796  *
7797  * @param hw Hardware context.
7798  * @param status The status from the MQE.
7799  * @param mqe Pointer to mailbox command buffer.
7800  * @param arg Pointer to a callback argument.
7801  *
7802  * @return 0 on success, non-zero otherwise
7803  */
7804 static int32_t
7805 ocs_hw_get_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7806 {
7807 	ocs_hw_get_nvparms_cb_arg_t *cb_arg = arg;
7808 	sli4_cmd_read_nvparms_t* mbox_rsp = (sli4_cmd_read_nvparms_t*) mqe;
7809 
7810 	if (cb_arg->cb) {
7811 		cb_arg->cb(status, mbox_rsp->wwpn, mbox_rsp->wwnn, mbox_rsp->hard_alpa,
7812 				mbox_rsp->preferred_d_id, cb_arg->arg);
7813 	}
7814 
7815 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7816 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7817 
7818 	return 0;
7819 }
7820 
7821 /**
7822  * @ingroup io
7823  * @brief  Read non-volatile parms.
7824  * @par Description
7825  * Issues a SLI-4 READ_NVPARMS mailbox. When the
7826  * command completes the provided mgmt callback function is
7827  * called.
7828  *
7829  * @param hw Hardware context.
7830  * @param cb Callback function to be called when the
7831  *	  command completes.
7832  * @param ul_arg An argument that is passed to the callback
7833  *	  function.
7834  *
7835  * @return
7836  * - OCS_HW_RTN_SUCCESS on success.
7837  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7838  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7839  *   context.
7840  * - OCS_HW_RTN_ERROR on any other error.
7841  */
7842 int32_t
7843 ocs_hw_get_nvparms(ocs_hw_t *hw, ocs_get_nvparms_cb_t cb, void* ul_arg)
7844 {
7845 	uint8_t *mbxdata;
7846 	ocs_hw_get_nvparms_cb_arg_t *cb_arg;
7847 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7848 
7849 	/* mbxdata holds the header of the command */
7850 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7851 	if (mbxdata == NULL) {
7852 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7853 		return OCS_HW_RTN_NO_MEMORY;
7854 	}
7855 
7856 	/* cb_arg holds the data that will be passed to the callback on completion */
7857 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_nvparms_cb_arg_t), OCS_M_NOWAIT);
7858 	if (cb_arg == NULL) {
7859 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7860 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7861 		return OCS_HW_RTN_NO_MEMORY;
7862 	}
7863 
7864 	cb_arg->cb = cb;
7865 	cb_arg->arg = ul_arg;
7866 
7867 	if (sli_cmd_read_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7868 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_nvparms_cb, cb_arg);
7869 	}
7870 
7871 	if (rc != OCS_HW_RTN_SUCCESS) {
7872 		ocs_log_test(hw->os, "READ_NVPARMS failed\n");
7873 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7874 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7875 	}
7876 
7877 	return rc;
7878 }
7879 
7880 typedef struct ocs_hw_set_nvparms_cb_arg_s {
7881 	ocs_set_nvparms_cb_t cb;
7882 	void *arg;
7883 } ocs_hw_set_nvparms_cb_arg_t;
7884 
7885 /**
7886  * @brief Called for the completion of set_nvparms for a
7887  *        user request.
7888  *
7889  * @param hw Hardware context.
7890  * @param status The status from the MQE.
7891  * @param mqe Pointer to mailbox command buffer.
7892  * @param arg Pointer to a callback argument.
7893  *
7894  * @return Returns 0 on success, or a non-zero value on failure.
7895  */
7896 static int32_t
7897 ocs_hw_set_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7898 {
7899 	ocs_hw_set_nvparms_cb_arg_t *cb_arg = arg;
7900 
7901 	if (cb_arg->cb) {
7902 		cb_arg->cb(status, cb_arg->arg);
7903 	}
7904 
7905 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7906 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7907 
7908 	return 0;
7909 }
7910 
7911 /**
7912  * @ingroup io
7913  * @brief  Write non-volatile parms.
7914  * @par Description
7915  * Issues a SLI-4 WRITE_NVPARMS mailbox. When the
7916  * command completes the provided mgmt callback function is
7917  * called.
7918  *
7919  * @param hw Hardware context.
7920  * @param cb Callback function to be called when the
7921  *	  command completes.
7922  * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
7923  * @param wwnn Port's WWNN in big-endian order, or NULL to use default.
7924  * @param hard_alpa A hard AL_PA address setting used during loop
7925  * initialization. If no hard AL_PA is required, set to 0.
7926  * @param preferred_d_id A preferred D_ID address setting
7927  * that may be overridden with the CONFIG_LINK mailbox command.
7928  * If there is no preference, set to 0.
7929  * @param ul_arg An argument that is passed to the callback
7930  *	  function.
7931  *
7932  * @return
7933  * - OCS_HW_RTN_SUCCESS on success.
7934  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7935  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7936  *   context.
7937  * - OCS_HW_RTN_ERROR on any other error.
7938  */
7939 int32_t
7940 ocs_hw_set_nvparms(ocs_hw_t *hw, ocs_set_nvparms_cb_t cb, uint8_t *wwpn,
7941 		uint8_t *wwnn, uint8_t hard_alpa, uint32_t preferred_d_id, void* ul_arg)
7942 {
7943 	uint8_t *mbxdata;
7944 	ocs_hw_set_nvparms_cb_arg_t *cb_arg;
7945 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7946 
7947 	/* mbxdata holds the header of the command */
7948 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7949 	if (mbxdata == NULL) {
7950 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7951 		return OCS_HW_RTN_NO_MEMORY;
7952 	}
7953 
7954 	/* cb_arg holds the data that will be passed to the callback on completion */
7955 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_nvparms_cb_arg_t), OCS_M_NOWAIT);
7956 	if (cb_arg == NULL) {
7957 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7958 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7959 		return OCS_HW_RTN_NO_MEMORY;
7960 	}
7961 
7962 	cb_arg->cb = cb;
7963 	cb_arg->arg = ul_arg;
7964 
7965 	if (sli_cmd_write_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE, wwpn, wwnn, hard_alpa, preferred_d_id)) {
7966 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_nvparms_cb, cb_arg);
7967 	}
7968 
7969 	if (rc != OCS_HW_RTN_SUCCESS) {
7970 		ocs_log_test(hw->os, "SET_NVPARMS failed\n");
7971 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7972 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7973 	}
7974 
7975 	return rc;
7976 }
7977 
7978 /**
7979  * @brief Called to obtain the count for the specified type.
7980  *
7981  * @param hw Hardware context.
7982  * @param io_count_type IO count type (inuse, free, wait_free).
7983  *
7984  * @return Returns the number of IOs on the specified list type.
7985  */
7986 uint32_t
7987 ocs_hw_io_get_count(ocs_hw_t *hw, ocs_hw_io_count_type_e io_count_type)
7988 {
7989 	ocs_hw_io_t *io = NULL;
7990 	uint32_t count = 0;
7991 
7992 	ocs_lock(&hw->io_lock);
7993 
7994 	switch (io_count_type) {
7995 	case OCS_HW_IO_INUSE_COUNT :
7996 		ocs_list_foreach(&hw->io_inuse, io) {
7997 			count++;
7998 		}
7999 		break;
8000 	case OCS_HW_IO_FREE_COUNT :
8001 		 ocs_list_foreach(&hw->io_free, io) {
8002 			 count++;
8003 		 }
8004 		 break;
8005 	case OCS_HW_IO_WAIT_FREE_COUNT :
8006 		 ocs_list_foreach(&hw->io_wait_free, io) {
8007 			 count++;
8008 		 }
8009 		 break;
8010 	case OCS_HW_IO_PORT_OWNED_COUNT:
8011 		 ocs_list_foreach(&hw->io_port_owned, io) {
8012 			 count++;
8013 		 }
8014 		 break;
8015 	case OCS_HW_IO_N_TOTAL_IO_COUNT :
8016 		count = hw->config.n_io;
8017 		break;
8018 	}
8019 
8020 	ocs_unlock(&hw->io_lock);
8021 
8022 	return count;
8023 }
8024 
8025 /**
8026  * @brief Called to obtain the count of produced RQs.
8027  *
8028  * @param hw Hardware context.
8029  *
8030  * @return Returns the number of RQs produced.
8031  */
8032 uint32_t
8033 ocs_hw_get_rqes_produced_count(ocs_hw_t *hw)
8034 {
8035 	uint32_t count = 0;
8036 	uint32_t i;
8037 	uint32_t j;
8038 
8039 	for (i = 0; i < hw->hw_rq_count; i++) {
8040 		hw_rq_t *rq = hw->hw_rq[i];
8041 		if (rq->rq_tracker != NULL) {
8042 			for (j = 0; j < rq->entry_count; j++) {
8043 				if (rq->rq_tracker[j] != NULL) {
8044 					count++;
8045 				}
8046 			}
8047 		}
8048 	}
8049 
8050 	return count;
8051 }
8052 
8053 typedef struct ocs_hw_set_active_profile_cb_arg_s {
8054 	ocs_set_active_profile_cb_t cb;
8055 	void *arg;
8056 } ocs_hw_set_active_profile_cb_arg_t;
8057 
8058 /**
8059  * @brief Called for the completion of set_active_profile for a
8060  *        user request.
8061  *
8062  * @param hw Hardware context.
8063  * @param status The status from the MQE
8064  * @param mqe Pointer to mailbox command buffer.
8065  * @param arg Pointer to a callback argument.
8066  *
8067  * @return Returns 0 on success, or a non-zero value on failure.
8068  */
8069 static int32_t
8070 ocs_hw_set_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
8071 {
8072 	ocs_hw_set_active_profile_cb_arg_t *cb_arg = arg;
8073 
8074 	if (cb_arg->cb) {
8075 		cb_arg->cb(status, cb_arg->arg);
8076 	}
8077 
8078 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
8079 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
8080 
8081 	return 0;
8082 }
8083 
8084 /**
8085  * @ingroup io
8086  * @brief  Set the currently active profile.
8087  * @par Description
8088  * Issues a SLI4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
8089  * command completes the provided mgmt callback function is
8090  * called.
8091  *
8092  * @param hw Hardware context.
8093  * @param profile_id Profile ID to activate.
8094  * @param cb Callback function to be called when the command completes.
8095  * @param ul_arg An argument that is passed to the callback function.
8096  *
8097  * @return
8098  * - OCS_HW_RTN_SUCCESS on success.
8099  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
8100  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
8101  *   context.
8102  * - OCS_HW_RTN_ERROR on any other error.
8103  */
8104 int32_t
8105 ocs_hw_set_active_profile(ocs_hw_t *hw, ocs_set_active_profile_cb_t cb, uint32_t profile_id, void* ul_arg)
8106 {
8107 	uint8_t *mbxdata;
8108 	ocs_hw_set_active_profile_cb_arg_t *cb_arg;
8109 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
8110 
8111 	/* Only supported on Skyhawk */
8112 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
8113 		return OCS_HW_RTN_ERROR;
8114 	}
8115 
8116 	/* mbxdata holds the header of the command */
8117 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
8118 	if (mbxdata == NULL) {
8119 		ocs_log_err(hw->os, "failed to malloc mbox\n");
8120 		return OCS_HW_RTN_NO_MEMORY;
8121 	}
8122 
8123 	/* cb_arg holds the data that will be passed to the callback on completion */
8124 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_active_profile_cb_arg_t), OCS_M_NOWAIT);
8125 	if (cb_arg == NULL) {
8126 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
8127 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8128 		return OCS_HW_RTN_NO_MEMORY;
8129 	}
8130 
8131 	cb_arg->cb = cb;
8132 	cb_arg->arg = ul_arg;
8133 
8134 	if (sli_cmd_common_set_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, profile_id)) {
8135 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_active_profile_cb, cb_arg);
8136 	}
8137 
8138 	if (rc != OCS_HW_RTN_SUCCESS) {
8139 		ocs_log_test(hw->os, "SET_ACTIVE_PROFILE failed\n");
8140 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8141 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_active_profile_cb_arg_t));
8142 	}
8143 
8144 	return rc;
8145 }
8146 
8147 /*
8148  * Private functions
8149  */
8150 
8151 /**
8152  * @brief Update the queue hash with the ID and index.
8153  *
8154  * @param hash Pointer to hash table.
8155  * @param id ID that was created.
8156  * @param index The index into the hash object.
8157  */
8158 static void
8159 ocs_hw_queue_hash_add(ocs_queue_hash_t *hash, uint16_t id, uint16_t index)
8160 {
8161 	uint32_t	hash_index = id & (OCS_HW_Q_HASH_SIZE - 1);
8162 
8163 	/*
8164 	 * Since the hash is always bigger than the number of queues, then we
8165 	 * never have to worry about an infinite loop.
8166 	 */
8167 	while(hash[hash_index].in_use) {
8168 		hash_index = (hash_index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8169 	}
8170 
8171 	/* not used, claim the entry */
8172 	hash[hash_index].id = id;
8173 	hash[hash_index].in_use = 1;
8174 	hash[hash_index].index = index;
8175 }
8176 
8177 /**
8178  * @brief Find index given queue ID.
8179  *
8180  * @param hash Pointer to hash table.
8181  * @param id ID to find.
8182  *
8183  * @return Returns the index into the HW cq array or -1 if not found.
8184  */
8185 int32_t
8186 ocs_hw_queue_hash_find(ocs_queue_hash_t *hash, uint16_t id)
8187 {
8188 	int32_t	rc = -1;
8189 	int32_t	index = id & (OCS_HW_Q_HASH_SIZE - 1);
8190 
8191 	/*
8192 	 * Since the hash is always bigger than the maximum number of Qs, then we
8193 	 * never have to worry about an infinite loop. We will always find an
8194 	 * unused entry.
8195 	 */
8196 	do {
8197 		if (hash[index].in_use &&
8198 		    hash[index].id == id) {
8199 			rc = hash[index].index;
8200 		} else {
8201 			index = (index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8202 		}
8203 	} while(rc == -1 && hash[index].in_use);
8204 
8205 	return rc;
8206 }
8207 
8208 static int32_t
8209 ocs_hw_domain_add(ocs_hw_t *hw, ocs_domain_t *domain)
8210 {
8211 	int32_t		rc = OCS_HW_RTN_ERROR;
8212 	uint16_t	fcfi = UINT16_MAX;
8213 
8214 	if ((hw == NULL) || (domain == NULL)) {
8215 		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8216 				hw, domain);
8217 		return OCS_HW_RTN_ERROR;
8218 	}
8219 
8220 	fcfi = domain->fcf_indicator;
8221 
8222 	if (fcfi < SLI4_MAX_FCFI) {
8223 		uint16_t	fcf_index = UINT16_MAX;
8224 
8225 		ocs_log_debug(hw->os, "adding domain %p @ %#x\n",
8226 				domain, fcfi);
8227 		hw->domains[fcfi] = domain;
8228 
8229 		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8230 		if (hw->workaround.override_fcfi) {
8231 			if (hw->first_domain_idx < 0) {
8232 				hw->first_domain_idx = fcfi;
8233 			}
8234 		}
8235 
8236 		fcf_index = domain->fcf;
8237 
8238 		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8239 			ocs_log_debug(hw->os, "adding map of FCF index %d to FCFI %d\n",
8240 				      fcf_index, fcfi);
8241 			hw->fcf_index_fcfi[fcf_index] = fcfi;
8242 			rc = OCS_HW_RTN_SUCCESS;
8243 		} else {
8244 			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8245 				     fcf_index, SLI4_MAX_FCF_INDEX);
8246 			hw->domains[fcfi] = NULL;
8247 		}
8248 	} else {
8249 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8250 				fcfi, SLI4_MAX_FCFI);
8251 	}
8252 
8253 	return rc;
8254 }
8255 
8256 static int32_t
8257 ocs_hw_domain_del(ocs_hw_t *hw, ocs_domain_t *domain)
8258 {
8259 	int32_t		rc = OCS_HW_RTN_ERROR;
8260 	uint16_t	fcfi = UINT16_MAX;
8261 
8262 	if ((hw == NULL) || (domain == NULL)) {
8263 		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8264 				hw, domain);
8265 		return OCS_HW_RTN_ERROR;
8266 	}
8267 
8268 	fcfi = domain->fcf_indicator;
8269 
8270 	if (fcfi < SLI4_MAX_FCFI) {
8271 		uint16_t	fcf_index = UINT16_MAX;
8272 
8273 		ocs_log_debug(hw->os, "deleting domain %p @ %#x\n",
8274 				domain, fcfi);
8275 
8276 		if (domain != hw->domains[fcfi]) {
8277 			ocs_log_test(hw->os, "provided domain %p does not match stored domain %p\n",
8278 				     domain, hw->domains[fcfi]);
8279 			return OCS_HW_RTN_ERROR;
8280 		}
8281 
8282 		hw->domains[fcfi] = NULL;
8283 
8284 		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8285 		if (hw->workaround.override_fcfi) {
8286 			if (hw->first_domain_idx == fcfi) {
8287 				hw->first_domain_idx = -1;
8288 			}
8289 		}
8290 
8291 		fcf_index = domain->fcf;
8292 
8293 		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8294 			if (hw->fcf_index_fcfi[fcf_index] == fcfi) {
8295 				hw->fcf_index_fcfi[fcf_index] = 0;
8296 				rc = OCS_HW_RTN_SUCCESS;
8297 			} else {
8298 				ocs_log_test(hw->os, "indexed FCFI %#x doesn't match provided %#x @ %d\n",
8299 					     hw->fcf_index_fcfi[fcf_index], fcfi, fcf_index);
8300 			}
8301 		} else {
8302 			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8303 				     fcf_index, SLI4_MAX_FCF_INDEX);
8304 		}
8305 	} else {
8306 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8307 				fcfi, SLI4_MAX_FCFI);
8308 	}
8309 
8310 	return rc;
8311 }
8312 
8313 ocs_domain_t *
8314 ocs_hw_domain_get(ocs_hw_t *hw, uint16_t fcfi)
8315 {
8316 
8317 	if (hw == NULL) {
8318 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8319 		return NULL;
8320 	}
8321 
8322 	if (fcfi < SLI4_MAX_FCFI) {
8323 		return hw->domains[fcfi];
8324 	} else {
8325 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8326 				fcfi, SLI4_MAX_FCFI);
8327 		return NULL;
8328 	}
8329 }
8330 
8331 static ocs_domain_t *
8332 ocs_hw_domain_get_indexed(ocs_hw_t *hw, uint16_t fcf_index)
8333 {
8334 
8335 	if (hw == NULL) {
8336 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8337 		return NULL;
8338 	}
8339 
8340 	if (fcf_index < SLI4_MAX_FCF_INDEX) {
8341 		return ocs_hw_domain_get(hw, hw->fcf_index_fcfi[fcf_index]);
8342 	} else {
8343 		ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8344 			     fcf_index, SLI4_MAX_FCF_INDEX);
8345 		return NULL;
8346 	}
8347 }
8348 
8349 /**
8350  * @brief Quaratine an IO by taking a reference count and adding it to the
8351  *        quarantine list. When the IO is popped from the list then the
8352  *        count is released and the IO MAY be freed depending on whether
8353  *        it is still referenced by the IO.
8354  *
8355  *        @n @b Note: BZ 160124 - If this is a target write or an initiator read using
8356  *        DIF, then we must add the XRI to a quarantine list until we receive
8357  *        4 more completions of this same type.
8358  *
8359  * @param hw Hardware context.
8360  * @param wq Pointer to the WQ associated with the IO object to quarantine.
8361  * @param io Pointer to the io object to quarantine.
8362  */
8363 static void
8364 ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io)
8365 {
8366 	ocs_quarantine_info_t *q_info = &wq->quarantine_info;
8367 	uint32_t	index;
8368 	ocs_hw_io_t	*free_io = NULL;
8369 
8370 	/* return if the QX bit was clear */
8371 	if (!io->quarantine) {
8372 		return;
8373 	}
8374 
8375 	/* increment the IO refcount to prevent it from being freed before the quarantine is over */
8376 	if (ocs_ref_get_unless_zero(&io->ref) == 0) {
8377 		/* command no longer active */
8378 		ocs_log_debug(hw ? hw->os : NULL,
8379 			      "io not active xri=0x%x tag=0x%x\n",
8380 			      io->indicator, io->reqtag);
8381 		return;
8382 	}
8383 
8384 	sli_queue_lock(wq->queue);
8385 		index = q_info->quarantine_index;
8386 		free_io = q_info->quarantine_ios[index];
8387 		q_info->quarantine_ios[index] = io;
8388 		q_info->quarantine_index = (index + 1) % OCS_HW_QUARANTINE_QUEUE_DEPTH;
8389 	sli_queue_unlock(wq->queue);
8390 
8391 	if (free_io != NULL) {
8392 		ocs_ref_put(&free_io->ref); /* ocs_ref_get(): same function */
8393 	}
8394 }
8395 
8396 /**
8397  * @brief Process entries on the given completion queue.
8398  *
8399  * @param hw Hardware context.
8400  * @param cq Pointer to the HW completion queue object.
8401  *
8402  * @return None.
8403  */
8404 void
8405 ocs_hw_cq_process(ocs_hw_t *hw, hw_cq_t *cq)
8406 {
8407 	uint8_t		cqe[sizeof(sli4_mcqe_t)];
8408 	uint16_t	rid = UINT16_MAX;
8409 	sli4_qentry_e	ctype;		/* completion type */
8410 	int32_t		status;
8411 	uint32_t	n_processed = 0;
8412 	time_t		tstart;
8413 	time_t		telapsed;
8414 
8415 	tstart = ocs_msectime();
8416 
8417 	while (!sli_queue_read(&hw->sli, cq->queue, cqe)) {
8418 		status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
8419 		/*
8420 		 * The sign of status is significant. If status is:
8421 		 * == 0 : call completed correctly and the CQE indicated success
8422 		 *  > 0 : call completed correctly and the CQE indicated an error
8423 		 *  < 0 : call failed and no information is available about the CQE
8424 		 */
8425 		if (status < 0) {
8426 			if (status == -2) {
8427 				/* Notification that an entry was consumed, but not completed */
8428 				continue;
8429 			}
8430 
8431 			break;
8432 		}
8433 
8434 		switch (ctype) {
8435 		case SLI_QENTRY_ASYNC:
8436 			CPUTRACE("async");
8437 			sli_cqe_async(&hw->sli, cqe);
8438 			break;
8439 		case SLI_QENTRY_MQ:
8440 			/*
8441 			 * Process MQ entry. Note there is no way to determine
8442 			 * the MQ_ID from the completion entry.
8443 			 */
8444 			CPUTRACE("mq");
8445 			ocs_hw_mq_process(hw, status, hw->mq);
8446 			break;
8447 		case SLI_QENTRY_OPT_WRITE_CMD:
8448 			ocs_hw_rqpair_process_auto_xfr_rdy_cmd(hw, cq, cqe);
8449 			break;
8450 		case SLI_QENTRY_OPT_WRITE_DATA:
8451 			ocs_hw_rqpair_process_auto_xfr_rdy_data(hw, cq, cqe);
8452 			break;
8453 		case SLI_QENTRY_WQ:
8454 			CPUTRACE("wq");
8455 			ocs_hw_wq_process(hw, cq, cqe, status, rid);
8456 			break;
8457 		case SLI_QENTRY_WQ_RELEASE: {
8458 			uint32_t wq_id = rid;
8459 			int32_t index = ocs_hw_queue_hash_find(hw->wq_hash, wq_id);
8460 
8461 			if (unlikely(index < 0)) {
8462 				ocs_log_err(hw->os, "unknown idx=%#x rid=%#x\n",
8463 					    index, rid);
8464 				break;
8465 			}
8466 
8467 			hw_wq_t *wq = hw->hw_wq[index];
8468 
8469 			/* Submit any HW IOs that are on the WQ pending list */
8470 			hw_wq_submit_pending(wq, wq->wqec_set_count);
8471 
8472 			break;
8473 		}
8474 
8475 		case SLI_QENTRY_RQ:
8476 			CPUTRACE("rq");
8477 			ocs_hw_rqpair_process_rq(hw, cq, cqe);
8478 			break;
8479 		case SLI_QENTRY_XABT: {
8480 			CPUTRACE("xabt");
8481 			ocs_hw_xabt_process(hw, cq, cqe, rid);
8482 			break;
8483 		}
8484 		default:
8485 			ocs_log_test(hw->os, "unhandled ctype=%#x rid=%#x\n", ctype, rid);
8486 			break;
8487 		}
8488 
8489 		n_processed++;
8490 		if (n_processed == cq->queue->proc_limit) {
8491 			break;
8492 		}
8493 
8494 		if (cq->queue->n_posted >= (cq->queue->posted_limit)) {
8495 			sli_queue_arm(&hw->sli, cq->queue, FALSE);
8496 		}
8497 	}
8498 
8499 	sli_queue_arm(&hw->sli, cq->queue, TRUE);
8500 
8501 	if (n_processed > cq->queue->max_num_processed) {
8502 		cq->queue->max_num_processed = n_processed;
8503 	}
8504 	telapsed = ocs_msectime() - tstart;
8505 	if (telapsed > cq->queue->max_process_time) {
8506 		cq->queue->max_process_time = telapsed;
8507 	}
8508 }
8509 
8510 /**
8511  * @brief Process WQ completion queue entries.
8512  *
8513  * @param hw Hardware context.
8514  * @param cq Pointer to the HW completion queue object.
8515  * @param cqe Pointer to WQ completion queue.
8516  * @param status Completion status.
8517  * @param rid Resource ID (IO tag).
8518  *
8519  * @return none
8520  */
8521 void
8522 ocs_hw_wq_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, int32_t status, uint16_t rid)
8523 {
8524 	hw_wq_callback_t *wqcb;
8525 
8526 	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_WQ, (void *)cqe, ((sli4_fc_wcqe_t *)cqe)->status, cq->queue->id,
8527 			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8528 
8529 	if(rid == OCS_HW_REQUE_XRI_REGTAG) {
8530 		if(status) {
8531 			ocs_log_err(hw->os, "reque xri failed, status = %d \n", status);
8532 		}
8533 		return;
8534 	}
8535 
8536 	wqcb = ocs_hw_reqtag_get_instance(hw, rid);
8537 	if (wqcb == NULL) {
8538 		ocs_log_err(hw->os, "invalid request tag: x%x\n", rid);
8539 		return;
8540 	}
8541 
8542 	if (wqcb->callback == NULL) {
8543 		ocs_log_err(hw->os, "wqcb callback is NULL\n");
8544 		return;
8545 	}
8546 
8547 	(*wqcb->callback)(wqcb->arg, cqe, status);
8548 }
8549 
8550 /**
8551  * @brief Process WQ completions for IO requests
8552  *
8553  * @param arg Generic callback argument
8554  * @param cqe Pointer to completion queue entry
8555  * @param status Completion status
8556  *
8557  * @par Description
8558  * @n @b Note:  Regarding io->reqtag, the reqtag is assigned once when HW IOs are initialized
8559  * in ocs_hw_setup_io(), and don't need to be returned to the hw->wq_reqtag_pool.
8560  *
8561  * @return None.
8562  */
8563 static void
8564 ocs_hw_wq_process_io(void *arg, uint8_t *cqe, int32_t status)
8565 {
8566 	ocs_hw_io_t *io = arg;
8567 	ocs_hw_t *hw = io->hw;
8568 	sli4_fc_wcqe_t *wcqe = (void *)cqe;
8569 	uint32_t	len = 0;
8570 	uint32_t ext = 0;
8571 	uint8_t out_of_order_axr_cmd = 0;
8572 	uint8_t out_of_order_axr_data = 0;
8573 	uint8_t lock_taken = 0;
8574 #if defined(OCS_DISC_SPIN_DELAY)
8575 	uint32_t delay = 0;
8576 	char prop_buf[32];
8577 #endif
8578 
8579 	/*
8580 	 * For the primary IO, this will also be used for the
8581 	 * response. So it is important to only set/clear this
8582 	 * flag on the first data phase of the IO because
8583 	 * subsequent phases will be done on the secondary XRI.
8584 	 */
8585 	if (io->quarantine && io->quarantine_first_phase) {
8586 		io->quarantine = (wcqe->qx == 1);
8587 		ocs_hw_io_quarantine(hw, io->wq, io);
8588 	}
8589 	io->quarantine_first_phase = FALSE;
8590 
8591 	/* BZ 161832 - free secondary HW IO */
8592 	if (io->sec_hio != NULL &&
8593 	    io->sec_hio->quarantine) {
8594 		/*
8595 		 * If the quarantine flag is set on the
8596 		 * IO, then set it on the secondary IO
8597 		 * based on the quarantine XRI (QX) bit
8598 		 * sent by the FW.
8599 		 */
8600 		io->sec_hio->quarantine = (wcqe->qx == 1);
8601 		/* use the primary io->wq because it is not set on the secondary IO. */
8602 		ocs_hw_io_quarantine(hw, io->wq, io->sec_hio);
8603 	}
8604 
8605 	ocs_hw_remove_io_timed_wqe(hw, io);
8606 
8607 	/* clear xbusy flag if WCQE[XB] is clear */
8608 	if (io->xbusy && wcqe->xb == 0) {
8609 		io->xbusy = FALSE;
8610 	}
8611 
8612 	/* get extended CQE status */
8613 	switch (io->type) {
8614 	case OCS_HW_BLS_ACC:
8615 	case OCS_HW_BLS_ACC_SID:
8616 		break;
8617 	case OCS_HW_ELS_REQ:
8618 		sli_fc_els_did(&hw->sli, cqe, &ext);
8619 		len = sli_fc_response_length(&hw->sli, cqe);
8620 		break;
8621 	case OCS_HW_ELS_RSP:
8622 	case OCS_HW_ELS_RSP_SID:
8623 	case OCS_HW_FC_CT_RSP:
8624 		break;
8625 	case OCS_HW_FC_CT:
8626 		len = sli_fc_response_length(&hw->sli, cqe);
8627 		break;
8628 	case OCS_HW_IO_TARGET_WRITE:
8629 		len = sli_fc_io_length(&hw->sli, cqe);
8630 #if defined(OCS_DISC_SPIN_DELAY)
8631 		if (ocs_get_property("disk_spin_delay", prop_buf, sizeof(prop_buf)) == 0) {
8632 			delay = ocs_strtoul(prop_buf, 0, 0);
8633 			ocs_udelay(delay);
8634 		}
8635 #endif
8636 		break;
8637 	case OCS_HW_IO_TARGET_READ:
8638 		len = sli_fc_io_length(&hw->sli, cqe);
8639 		/*
8640 		 * if_type == 2 seems to return 0 "total length placed" on
8641 		 * FCP_TSEND64_WQE completions. If this appears to happen,
8642 		 * use the CTIO data transfer length instead.
8643 		 */
8644 		if (hw->workaround.retain_tsend_io_length && !len && !status) {
8645 			len = io->length;
8646 		}
8647 
8648 		break;
8649 	case OCS_HW_IO_TARGET_RSP:
8650 		if(io->is_port_owned) {
8651 			ocs_lock(&io->axr_lock);
8652 			lock_taken = 1;
8653 			if(io->axr_buf->call_axr_cmd) {
8654 				out_of_order_axr_cmd = 1;
8655 			}
8656 			if(io->axr_buf->call_axr_data) {
8657 				out_of_order_axr_data = 1;
8658 			}
8659 		}
8660 		break;
8661 	case OCS_HW_IO_INITIATOR_READ:
8662 		len = sli_fc_io_length(&hw->sli, cqe);
8663 		break;
8664 	case OCS_HW_IO_INITIATOR_WRITE:
8665 		len = sli_fc_io_length(&hw->sli, cqe);
8666 		break;
8667 	case OCS_HW_IO_INITIATOR_NODATA:
8668 		break;
8669 	case OCS_HW_IO_DNRX_REQUEUE:
8670 		/* release the count for re-posting the buffer */
8671 		//ocs_hw_io_free(hw, io);
8672 		break;
8673 	default:
8674 		ocs_log_test(hw->os, "XXX unhandled io type %#x for XRI 0x%x\n",
8675 			     io->type, io->indicator);
8676 		break;
8677 	}
8678 	if (status) {
8679 		ext = sli_fc_ext_status(&hw->sli, cqe);
8680 		/* Emulate IAAB=0 for initiator WQEs only; i.e. automatically
8681 		 * abort exchange if an error occurred and exchange is still busy.
8682 		 */
8683 		if (hw->config.i_only_aab &&
8684 		    (ocs_hw_iotype_is_originator(io->type)) &&
8685 		    (ocs_hw_wcqe_abort_needed(status, ext, wcqe->xb))) {
8686 			ocs_hw_rtn_e rc;
8687 
8688 			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
8689 				      io->indicator, io->reqtag);
8690 			/*
8691 			 * Because the initiator will not issue another IO phase, then it is OK to to issue the
8692 			 * callback on the abort completion, but for consistency with the target, wait for the
8693 			 * XRI_ABORTED CQE to issue the IO callback.
8694 			 */
8695 			rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
8696 
8697 			if (rc == OCS_HW_RTN_SUCCESS) {
8698 				/* latch status to return after abort is complete */
8699 				io->status_saved = 1;
8700 				io->saved_status = status;
8701 				io->saved_ext = ext;
8702 				io->saved_len = len;
8703 				goto exit_ocs_hw_wq_process_io;
8704 			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8705 				/*
8706 				 * Already being aborted by someone else (ABTS
8707 				 * perhaps). Just fall through and return original
8708 				 * error.
8709 				 */
8710 				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8711 					      io->indicator, io->reqtag);
8712 
8713 			} else {
8714 				/* Failed to abort for some other reason, log error */
8715 				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8716 					     io->indicator, io->reqtag, rc);
8717 			}
8718 		}
8719 
8720 		/*
8721 		 * If we're not an originator IO, and XB is set, then issue abort for the IO from within the HW
8722 		 */
8723 		if ( (! ocs_hw_iotype_is_originator(io->type)) && wcqe->xb) {
8724 			ocs_hw_rtn_e rc;
8725 
8726 			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", io->indicator, io->reqtag);
8727 
8728 			/*
8729 			 * Because targets may send a response when the IO completes using the same XRI, we must
8730 			 * wait for the XRI_ABORTED CQE to issue the IO callback
8731 			 */
8732 			rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
8733 			if (rc == OCS_HW_RTN_SUCCESS) {
8734 				/* latch status to return after abort is complete */
8735 				io->status_saved = 1;
8736 				io->saved_status = status;
8737 				io->saved_ext = ext;
8738 				io->saved_len = len;
8739 				goto exit_ocs_hw_wq_process_io;
8740 			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8741 				/*
8742 				 * Already being aborted by someone else (ABTS
8743 				 * perhaps). Just fall through and return original
8744 				 * error.
8745 				 */
8746 				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8747 					      io->indicator, io->reqtag);
8748 
8749 			} else {
8750 				/* Failed to abort for some other reason, log error */
8751 				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8752 					     io->indicator, io->reqtag, rc);
8753 			}
8754 		}
8755 	}
8756 	/* BZ 161832 - free secondary HW IO */
8757 	if (io->sec_hio != NULL) {
8758 		ocs_hw_io_free(hw, io->sec_hio);
8759 		io->sec_hio = NULL;
8760 	}
8761 
8762 	if (io->done != NULL) {
8763 		ocs_hw_done_t  done = io->done;
8764 		void		*arg = io->arg;
8765 
8766 		io->done = NULL;
8767 
8768 		if (io->status_saved) {
8769 			/* use latched status if exists */
8770 			status = io->saved_status;
8771 			len = io->saved_len;
8772 			ext = io->saved_ext;
8773 			io->status_saved = 0;
8774 		}
8775 
8776 		/* Restore default SGL */
8777 		ocs_hw_io_restore_sgl(hw, io);
8778 		done(io, io->rnode, len, status, ext, arg);
8779 	}
8780 
8781 	if(out_of_order_axr_cmd) {
8782 		/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8783 		if (hw->config.bounce) {
8784 			fc_header_t *hdr = io->axr_buf->cmd_seq->header->dma.virt;
8785 			uint32_t s_id = fc_be24toh(hdr->s_id);
8786 			uint32_t d_id = fc_be24toh(hdr->d_id);
8787 			uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8788 			if (hw->callback.bounce != NULL) {
8789 				(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, io->axr_buf->cmd_seq, s_id, d_id, ox_id);
8790 			}
8791 		}else {
8792 			hw->callback.unsolicited(hw->args.unsolicited, io->axr_buf->cmd_seq);
8793 		}
8794 
8795 		if(out_of_order_axr_data) {
8796 			/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8797 			if (hw->config.bounce) {
8798 				fc_header_t *hdr = io->axr_buf->seq.header->dma.virt;
8799 				uint32_t s_id = fc_be24toh(hdr->s_id);
8800 				uint32_t d_id = fc_be24toh(hdr->d_id);
8801 				uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8802 				if (hw->callback.bounce != NULL) {
8803 					(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, &io->axr_buf->seq, s_id, d_id, ox_id);
8804 				}
8805 			}else {
8806 				hw->callback.unsolicited(hw->args.unsolicited, &io->axr_buf->seq);
8807 			}
8808 		}
8809 	}
8810 
8811 exit_ocs_hw_wq_process_io:
8812 	if(lock_taken) {
8813 		ocs_unlock(&io->axr_lock);
8814 	}
8815 }
8816 
8817 /**
8818  * @brief Process WQ completions for abort requests.
8819  *
8820  * @param arg Generic callback argument.
8821  * @param cqe Pointer to completion queue entry.
8822  * @param status Completion status.
8823  *
8824  * @return None.
8825  */
8826 static void
8827 ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status)
8828 {
8829 	ocs_hw_io_t *io = arg;
8830 	ocs_hw_t *hw = io->hw;
8831 	uint32_t ext = 0;
8832 	uint32_t len = 0;
8833 	hw_wq_callback_t *wqcb;
8834 
8835 	/*
8836 	 * For IOs that were aborted internally, we may need to issue the callback here depending
8837 	 * on whether a XRI_ABORTED CQE is expected ot not. If the status is Local Reject/No XRI, then
8838 	 * issue the callback now.
8839 	*/
8840 	ext = sli_fc_ext_status(&hw->sli, cqe);
8841 	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
8842 	    ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
8843 		io->done != NULL) {
8844 		ocs_hw_done_t  done = io->done;
8845 		void		*arg = io->arg;
8846 
8847 		io->done = NULL;
8848 
8849 		/*
8850 		 * Use latched status as this is always saved for an internal abort
8851 		 *
8852 		 * Note: We wont have both a done and abort_done function, so don't worry about
8853 		 *       clobbering the len, status and ext fields.
8854 		 */
8855 		status = io->saved_status;
8856 		len = io->saved_len;
8857 		ext = io->saved_ext;
8858 		io->status_saved = 0;
8859 		done(io, io->rnode, len, status, ext, arg);
8860 	}
8861 
8862 	if (io->abort_done != NULL) {
8863 		ocs_hw_done_t  done = io->abort_done;
8864 		void		*arg = io->abort_arg;
8865 
8866 		io->abort_done = NULL;
8867 
8868 		done(io, io->rnode, len, status, ext, arg);
8869 	}
8870 	ocs_lock(&hw->io_abort_lock);
8871 		/* clear abort bit to indicate abort is complete */
8872 		io->abort_in_progress = 0;
8873 	ocs_unlock(&hw->io_abort_lock);
8874 
8875 	/* Free the WQ callback */
8876 	ocs_hw_assert(io->abort_reqtag != UINT32_MAX);
8877 	wqcb = ocs_hw_reqtag_get_instance(hw, io->abort_reqtag);
8878 	ocs_hw_reqtag_free(hw, wqcb);
8879 
8880 	/*
8881 	 * Call ocs_hw_io_free() because this releases the WQ reservation as
8882 	 * well as doing the refcount put. Don't duplicate the code here.
8883 	 */
8884 	(void)ocs_hw_io_free(hw, io);
8885 }
8886 
8887 /**
8888  * @brief Process XABT completions
8889  *
8890  * @param hw Hardware context.
8891  * @param cq Pointer to the HW completion queue object.
8892  * @param cqe Pointer to WQ completion queue.
8893  * @param rid Resource ID (IO tag).
8894  *
8895  *
8896  * @return None.
8897  */
8898 void
8899 ocs_hw_xabt_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, uint16_t rid)
8900 {
8901 	/* search IOs wait free list */
8902 	ocs_hw_io_t *io = NULL;
8903 
8904 	io = ocs_hw_io_lookup(hw, rid);
8905 
8906 	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_XABT, (void *)cqe, 0, cq->queue->id,
8907 			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8908 	if (io == NULL) {
8909 		/* IO lookup failure should never happen */
8910 		ocs_log_err(hw->os, "Error: xabt io lookup failed rid=%#x\n", rid);
8911 		return;
8912 	}
8913 
8914 	if (!io->xbusy) {
8915 		ocs_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
8916 	} else {
8917 		/* mark IO as no longer busy */
8918 		io->xbusy = FALSE;
8919 	}
8920 
8921        if (io->is_port_owned) {
8922                ocs_lock(&hw->io_lock);
8923                /* Take reference so that below callback will not free io before reque */
8924                ocs_ref_get(&io->ref);
8925                ocs_unlock(&hw->io_lock);
8926        }
8927 
8928 	/* For IOs that were aborted internally, we need to issue any pending callback here. */
8929 	if (io->done != NULL) {
8930 		ocs_hw_done_t  done = io->done;
8931 		void		*arg = io->arg;
8932 
8933 		/* Use latched status as this is always saved for an internal abort */
8934 		int32_t status = io->saved_status;
8935 		uint32_t len = io->saved_len;
8936 		uint32_t ext = io->saved_ext;
8937 
8938 		io->done = NULL;
8939 		io->status_saved = 0;
8940 
8941 		done(io, io->rnode, len, status, ext, arg);
8942 	}
8943 
8944 	/* Check to see if this is a port owned XRI */
8945 	if (io->is_port_owned) {
8946 		ocs_lock(&hw->io_lock);
8947 		ocs_hw_reque_xri(hw, io);
8948 		ocs_unlock(&hw->io_lock);
8949 		/* Not hanlding reque xri completion, free io */
8950 		ocs_hw_io_free(hw, io);
8951 		return;
8952 	}
8953 
8954 	ocs_lock(&hw->io_lock);
8955 		if ((io->state == OCS_HW_IO_STATE_INUSE) || (io->state == OCS_HW_IO_STATE_WAIT_FREE)) {
8956 			/* if on wait_free list, caller has already freed IO;
8957 			 * remove from wait_free list and add to free list.
8958 			 * if on in-use list, already marked as no longer busy;
8959 			 * just leave there and wait for caller to free.
8960 			 */
8961 			if (io->state == OCS_HW_IO_STATE_WAIT_FREE) {
8962 				io->state = OCS_HW_IO_STATE_FREE;
8963 				ocs_list_remove(&hw->io_wait_free, io);
8964 				ocs_hw_io_free_move_correct_list(hw, io);
8965 			}
8966 		}
8967 	ocs_unlock(&hw->io_lock);
8968 }
8969 
8970 /**
8971  * @brief Adjust the number of WQs and CQs within the HW.
8972  *
8973  * @par Description
8974  * Calculates the number of WQs and associated CQs needed in the HW based on
8975  * the number of IOs. Calculates the starting CQ index for each WQ, RQ and
8976  * MQ.
8977  *
8978  * @param hw Hardware context allocated by the caller.
8979  */
8980 static void
8981 ocs_hw_adjust_wqs(ocs_hw_t *hw)
8982 {
8983 	uint32_t max_wq_num = sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ);
8984 	uint32_t max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ];
8985 	uint32_t max_cq_entries = hw->num_qentries[SLI_QTYPE_CQ];
8986 
8987 	/*
8988 	 * possibly adjust the the size of the WQs so that the CQ is twice as
8989 	 * big as the WQ to allow for 2 completions per IO. This allows us to
8990 	 * handle multi-phase as well as aborts.
8991 	 */
8992 	if (max_cq_entries < max_wq_entries * 2) {
8993 		max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ] = max_cq_entries / 2;
8994 	}
8995 
8996 	/*
8997 	 * Calculate the number of WQs to use base on the number of IOs.
8998 	 *
8999 	 * Note: We need to reserve room for aborts which must be sent down
9000 	 *       the same WQ as the IO. So we allocate enough WQ space to
9001 	 *       handle 2 times the number of IOs. Half of the space will be
9002 	 *       used for normal IOs and the other hwf is reserved for aborts.
9003 	 */
9004 	hw->config.n_wq = ((hw->config.n_io * 2) + (max_wq_entries - 1)) / max_wq_entries;
9005 
9006 	/*
9007 	 * For performance reasons, it is best to use use a minimum of 4 WQs
9008 	 * for BE3 and Skyhawk.
9009 	 */
9010 	if (hw->config.n_wq < 4 &&
9011 	    SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
9012 		hw->config.n_wq = 4;
9013 	}
9014 
9015 	/*
9016 	 * For dual-chute support, we need to have at least one WQ per chute.
9017 	 */
9018 	if (hw->config.n_wq < 2 &&
9019 	    ocs_hw_get_num_chutes(hw) > 1) {
9020 		hw->config.n_wq = 2;
9021 	}
9022 
9023 	/* make sure we haven't exceeded the max supported in the HW */
9024 	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
9025 		hw->config.n_wq = OCS_HW_MAX_NUM_WQ;
9026 	}
9027 
9028 	/* make sure we haven't exceeded the chip maximum */
9029 	if (hw->config.n_wq > max_wq_num) {
9030 		hw->config.n_wq = max_wq_num;
9031 	}
9032 
9033 	/*
9034 	 * Using Queue Topology string, we divide by number of chutes
9035 	 */
9036 	hw->config.n_wq /= ocs_hw_get_num_chutes(hw);
9037 }
9038 
9039 static int32_t
9040 ocs_hw_command_process(ocs_hw_t *hw, int32_t status, uint8_t *mqe, size_t size)
9041 {
9042 	ocs_command_ctx_t *ctx = NULL;
9043 
9044 	ocs_lock(&hw->cmd_lock);
9045 		if (NULL == (ctx = ocs_list_remove_head(&hw->cmd_head))) {
9046 			ocs_log_err(hw->os, "XXX no command context?!?\n");
9047 			ocs_unlock(&hw->cmd_lock);
9048 			return -1;
9049 		}
9050 
9051 		hw->cmd_head_count--;
9052 
9053 		/* Post any pending requests */
9054 		ocs_hw_cmd_submit_pending(hw);
9055 
9056 	ocs_unlock(&hw->cmd_lock);
9057 
9058 	if (ctx->cb) {
9059 		if (ctx->buf) {
9060 			ocs_memcpy(ctx->buf, mqe, size);
9061 		}
9062 		ctx->cb(hw, status, ctx->buf, ctx->arg);
9063 	}
9064 
9065 	ocs_memset(ctx, 0, sizeof(ocs_command_ctx_t));
9066 	ocs_free(hw->os, ctx, sizeof(ocs_command_ctx_t));
9067 
9068 	return 0;
9069 }
9070 
9071 /**
9072  * @brief Process entries on the given mailbox queue.
9073  *
9074  * @param hw Hardware context.
9075  * @param status CQE status.
9076  * @param mq Pointer to the mailbox queue object.
9077  *
9078  * @return Returns 0 on success, or a non-zero value on failure.
9079  */
9080 static int32_t
9081 ocs_hw_mq_process(ocs_hw_t *hw, int32_t status, sli4_queue_t *mq)
9082 {
9083 	uint8_t		mqe[SLI4_BMBX_SIZE];
9084 
9085 	if (!sli_queue_read(&hw->sli, mq, mqe)) {
9086 		ocs_hw_command_process(hw, status, mqe, mq->size);
9087 	}
9088 
9089 	return 0;
9090 }
9091 
9092 /**
9093  * @brief Read a FCF table entry.
9094  *
9095  * @param hw Hardware context.
9096  * @param index Table index to read. Use SLI4_FCOE_FCF_TABLE_FIRST for the first
9097  * read and the next_index field from the FCOE_READ_FCF_TABLE command
9098  * for subsequent reads.
9099  *
9100  * @return Returns 0 on success, or a non-zero value on failure.
9101  */
9102 static ocs_hw_rtn_e
9103 ocs_hw_read_fcf(ocs_hw_t *hw, uint32_t index)
9104 {
9105 	uint8_t		*buf = NULL;
9106 	int32_t		rc = OCS_HW_RTN_ERROR;
9107 
9108 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9109 	if (!buf) {
9110 		ocs_log_err(hw->os, "no buffer for command\n");
9111 		return OCS_HW_RTN_NO_MEMORY;
9112 	}
9113 
9114 	if (sli_cmd_fcoe_read_fcf_table(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->fcf_dmem,
9115 			index)) {
9116 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_read_fcf, &hw->fcf_dmem);
9117 	}
9118 
9119 	if (rc != OCS_HW_RTN_SUCCESS) {
9120 		ocs_log_test(hw->os, "FCOE_READ_FCF_TABLE failed\n");
9121 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9122 	}
9123 
9124 	return rc;
9125 }
9126 
9127 /**
9128  * @brief Callback function for the FCOE_READ_FCF_TABLE command.
9129  *
9130  * @par Description
9131  * Note that the caller has allocated:
9132  *  - DMA memory to hold the table contents
9133  *  - DMA memory structure
9134  *  - Command/results buffer
9135  *  .
9136  * Each of these must be freed here.
9137  *
9138  * @param hw Hardware context.
9139  * @param status Hardware status.
9140  * @param mqe Pointer to the mailbox command/results buffer.
9141  * @param arg Pointer to the DMA memory structure.
9142  *
9143  * @return Returns 0 on success, or a non-zero value on failure.
9144  */
9145 static int32_t
9146 ocs_hw_cb_read_fcf(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9147 {
9148 	ocs_dma_t	*dma = arg;
9149 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9150 
9151 	if (status || hdr->status) {
9152 		ocs_log_test(hw->os, "bad status cqe=%#x mqe=%#x\n",
9153 				status, hdr->status);
9154 	} else if (dma->virt) {
9155 		sli4_res_fcoe_read_fcf_table_t *read_fcf = dma->virt;
9156 
9157 		/* if FC or FCOE and FCF entry valid, process it */
9158 		if (read_fcf->fcf_entry.fc ||
9159 				(read_fcf->fcf_entry.val && !read_fcf->fcf_entry.sol)) {
9160 			if (hw->callback.domain != NULL) {
9161 				ocs_domain_record_t drec = {0};
9162 
9163 				if (read_fcf->fcf_entry.fc) {
9164 					/*
9165 					 * This is a pseudo FCF entry. Create a domain
9166 					 * record based on the read topology information
9167 					 */
9168 					drec.speed = hw->link.speed;
9169 					drec.fc_id = hw->link.fc_id;
9170 					drec.is_fc = TRUE;
9171 					if (SLI_LINK_TOPO_LOOP == hw->link.topology) {
9172 						drec.is_loop = TRUE;
9173 						ocs_memcpy(drec.map.loop, hw->link.loop_map,
9174 							   sizeof(drec.map.loop));
9175 					} else if (SLI_LINK_TOPO_NPORT == hw->link.topology) {
9176 						drec.is_nport = TRUE;
9177 					}
9178 				} else {
9179 					drec.index = read_fcf->fcf_entry.fcf_index;
9180 					drec.priority = read_fcf->fcf_entry.fip_priority;
9181 
9182 					/* copy address, wwn and vlan_bitmap */
9183 					ocs_memcpy(drec.address, read_fcf->fcf_entry.fcf_mac_address,
9184 						   sizeof(drec.address));
9185 					ocs_memcpy(drec.wwn, read_fcf->fcf_entry.fabric_name_id,
9186 						   sizeof(drec.wwn));
9187 					ocs_memcpy(drec.map.vlan, read_fcf->fcf_entry.vlan_bitmap,
9188 						   sizeof(drec.map.vlan));
9189 
9190 					drec.is_ethernet = TRUE;
9191 					drec.is_nport = TRUE;
9192 				}
9193 
9194 				hw->callback.domain(hw->args.domain,
9195 						OCS_HW_DOMAIN_FOUND,
9196 						&drec);
9197 			}
9198 		} else {
9199 			/* if FCOE and FCF is not valid, ignore it */
9200 			ocs_log_test(hw->os, "ignore invalid FCF entry\n");
9201 		}
9202 
9203 		if (SLI4_FCOE_FCF_TABLE_LAST != read_fcf->next_index) {
9204 			ocs_hw_read_fcf(hw, read_fcf->next_index);
9205 		}
9206 	}
9207 
9208 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9209 	//ocs_dma_free(hw->os, dma);
9210 	//ocs_free(hw->os, dma, sizeof(ocs_dma_t));
9211 
9212 	return 0;
9213 }
9214 
9215 /**
9216  * @brief Callback function for the SLI link events.
9217  *
9218  * @par Description
9219  * This function allocates memory which must be freed in its callback.
9220  *
9221  * @param ctx Hardware context pointer (that is, ocs_hw_t *).
9222  * @param e Event structure pointer (that is, sli4_link_event_t *).
9223  *
9224  * @return Returns 0 on success, or a non-zero value on failure.
9225  */
9226 static int32_t
9227 ocs_hw_cb_link(void *ctx, void *e)
9228 {
9229 	ocs_hw_t	*hw = ctx;
9230 	sli4_link_event_t *event = e;
9231 	ocs_domain_t	*d = NULL;
9232 	uint32_t	i = 0;
9233 	int32_t		rc = OCS_HW_RTN_ERROR;
9234 	ocs_t 		*ocs = hw->os;
9235 
9236 	ocs_hw_link_event_init(hw);
9237 
9238 	switch (event->status) {
9239 	case SLI_LINK_STATUS_UP:
9240 
9241 		hw->link = *event;
9242 
9243 		if (SLI_LINK_TOPO_NPORT == event->topology) {
9244 			device_printf(ocs->dev, "Link Up, NPORT, speed is %d\n", event->speed);
9245 			ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9246 		} else if (SLI_LINK_TOPO_LOOP == event->topology) {
9247 			uint8_t	*buf = NULL;
9248 			device_printf(ocs->dev, "Link Up, LOOP, speed is %d\n", event->speed);
9249 
9250 			buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9251 			if (!buf) {
9252 				ocs_log_err(hw->os, "no buffer for command\n");
9253 				break;
9254 			}
9255 
9256 			if (sli_cmd_read_topology(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->loop_map)) {
9257 				rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, __ocs_read_topology_cb, NULL);
9258 			}
9259 
9260 			if (rc != OCS_HW_RTN_SUCCESS) {
9261 				ocs_log_test(hw->os, "READ_TOPOLOGY failed\n");
9262 				ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9263 			}
9264 		} else {
9265 			device_printf(ocs->dev, "Link Up, unsupported topology (%#x), speed is %d\n",
9266 					event->topology, event->speed);
9267 		}
9268 		break;
9269 	case SLI_LINK_STATUS_DOWN:
9270 		device_printf(ocs->dev, "Link Down\n");
9271 
9272 		hw->link.status = event->status;
9273 
9274 		for (i = 0; i < SLI4_MAX_FCFI; i++) {
9275 			d = hw->domains[i];
9276 			if (d != NULL &&
9277 			    hw->callback.domain != NULL) {
9278 				hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, d);
9279 			}
9280 		}
9281 		break;
9282 	default:
9283 		ocs_log_test(hw->os, "unhandled link status %#x\n", event->status);
9284 		break;
9285 	}
9286 
9287 	return 0;
9288 }
9289 
9290 static int32_t
9291 ocs_hw_cb_fip(void *ctx, void *e)
9292 {
9293 	ocs_hw_t	*hw = ctx;
9294 	ocs_domain_t	*domain = NULL;
9295 	sli4_fip_event_t *event = e;
9296 
9297 	ocs_hw_assert(event);
9298 	ocs_hw_assert(hw);
9299 
9300 	/* Find the associated domain object */
9301 	if (event->type == SLI4_FCOE_FIP_FCF_CLEAR_VLINK) {
9302 		ocs_domain_t *d = NULL;
9303 		uint32_t	i = 0;
9304 
9305 		/* Clear VLINK is different from the other FIP events as it passes back
9306 		 * a VPI instead of a FCF index. Check all attached SLI ports for a
9307 		 * matching VPI */
9308 		for (i = 0; i < SLI4_MAX_FCFI; i++) {
9309 			d = hw->domains[i];
9310 			if (d != NULL) {
9311 				ocs_sport_t	*sport = NULL;
9312 
9313 				ocs_list_foreach(&d->sport_list, sport) {
9314 					if (sport->indicator == event->index) {
9315 						domain = d;
9316 						break;
9317 					}
9318 				}
9319 
9320 				if (domain != NULL) {
9321 					break;
9322 				}
9323 			}
9324 		}
9325 	} else {
9326 		domain = ocs_hw_domain_get_indexed(hw, event->index);
9327 	}
9328 
9329 	switch (event->type) {
9330 	case SLI4_FCOE_FIP_FCF_DISCOVERED:
9331 		ocs_hw_read_fcf(hw, event->index);
9332 		break;
9333 	case SLI4_FCOE_FIP_FCF_DEAD:
9334 		if (domain != NULL &&
9335 		    hw->callback.domain != NULL) {
9336 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9337 		}
9338 		break;
9339 	case SLI4_FCOE_FIP_FCF_CLEAR_VLINK:
9340 		if (domain != NULL &&
9341 		    hw->callback.domain != NULL) {
9342 			/*
9343 			 * We will want to issue rediscover FCF when this domain is free'd  in order
9344 			 * to invalidate the FCF table
9345 			 */
9346 			domain->req_rediscover_fcf = TRUE;
9347 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9348 		}
9349 		break;
9350 	case SLI4_FCOE_FIP_FCF_MODIFIED:
9351 		if (domain != NULL &&
9352 		    hw->callback.domain != NULL) {
9353 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9354 		}
9355 
9356 		ocs_hw_read_fcf(hw, event->index);
9357 		break;
9358 	default:
9359 		ocs_log_test(hw->os, "unsupported event %#x\n", event->type);
9360 	}
9361 
9362 	return 0;
9363 }
9364 
9365 static int32_t
9366 ocs_hw_cb_node_attach(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9367 {
9368 	ocs_remote_node_t *rnode = arg;
9369 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9370 	ocs_hw_remote_node_event_e	evt = 0;
9371 
9372 	if (status || hdr->status) {
9373 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9374 				hdr->status);
9375 		ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
9376 		rnode->attached = FALSE;
9377 		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9378 		evt = OCS_HW_NODE_ATTACH_FAIL;
9379 	} else {
9380 		rnode->attached = TRUE;
9381 		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 1);
9382 		evt = OCS_HW_NODE_ATTACH_OK;
9383 	}
9384 
9385 	if (hw->callback.rnode != NULL) {
9386 		hw->callback.rnode(hw->args.rnode, evt, rnode);
9387 	}
9388 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9389 
9390 	return 0;
9391 }
9392 
9393 static int32_t
9394 ocs_hw_cb_node_free(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9395 {
9396 	ocs_remote_node_t *rnode = arg;
9397 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9398 	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9399 	int32_t		rc = 0;
9400 
9401 	if (status || hdr->status) {
9402 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9403 				hdr->status);
9404 
9405 		/*
9406 		 * In certain cases, a non-zero MQE status is OK (all must be true):
9407 		 *   - node is attached
9408 		 *   - if High Login Mode is enabled, node is part of a node group
9409 		 *   - status is 0x1400
9410 		 */
9411 		if (!rnode->attached || ((sli_get_hlm(&hw->sli) == TRUE) && !rnode->node_group) ||
9412 				(hdr->status != SLI4_MBOX_STATUS_RPI_NOT_REG)) {
9413 			rc = -1;
9414 		}
9415 	}
9416 
9417 	if (rc == 0) {
9418 		rnode->node_group = FALSE;
9419 		rnode->attached = FALSE;
9420 
9421 		if (ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_count) == 0) {
9422 			ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9423 		}
9424 
9425 		evt = OCS_HW_NODE_FREE_OK;
9426 	}
9427 
9428 	if (hw->callback.rnode != NULL) {
9429 		hw->callback.rnode(hw->args.rnode, evt, rnode);
9430 	}
9431 
9432 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9433 
9434 	return rc;
9435 }
9436 
9437 static int32_t
9438 ocs_hw_cb_node_free_all(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9439 {
9440 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9441 	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9442 	int32_t		rc = 0;
9443 	uint32_t	i;
9444 
9445 	if (status || hdr->status) {
9446 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9447 				hdr->status);
9448 	} else {
9449 		evt = OCS_HW_NODE_FREE_ALL_OK;
9450 	}
9451 
9452 	if (evt == OCS_HW_NODE_FREE_ALL_OK) {
9453 		for (i = 0; i < sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI); i++) {
9454 			ocs_atomic_set(&hw->rpi_ref[i].rpi_count, 0);
9455 		}
9456 
9457 		if (sli_resource_reset(&hw->sli, SLI_RSRC_FCOE_RPI)) {
9458 			ocs_log_test(hw->os, "FCOE_RPI free all failure\n");
9459 			rc = -1;
9460 		}
9461 	}
9462 
9463 	if (hw->callback.rnode != NULL) {
9464 		hw->callback.rnode(hw->args.rnode, evt, NULL);
9465 	}
9466 
9467 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9468 
9469 	return rc;
9470 }
9471 
9472 /**
9473  * @brief Initialize the pool of HW IO objects.
9474  *
9475  * @param hw Hardware context.
9476  *
9477  * @return Returns 0 on success, or a non-zero value on failure.
9478  */
9479 static ocs_hw_rtn_e
9480 ocs_hw_setup_io(ocs_hw_t *hw)
9481 {
9482 	uint32_t	i = 0;
9483 	ocs_hw_io_t	*io = NULL;
9484 	uintptr_t	xfer_virt = 0;
9485 	uintptr_t	xfer_phys = 0;
9486 	uint32_t	index;
9487 	uint8_t		new_alloc = TRUE;
9488 
9489 	if (NULL == hw->io) {
9490 		hw->io = ocs_malloc(hw->os, hw->config.n_io * sizeof(ocs_hw_io_t *), OCS_M_ZERO | OCS_M_NOWAIT);
9491 
9492 		if (NULL == hw->io) {
9493 			ocs_log_err(hw->os, "IO pointer memory allocation failed, %d Ios at size %zu\n",
9494 				    hw->config.n_io,
9495 				    sizeof(ocs_hw_io_t *));
9496 			return OCS_HW_RTN_NO_MEMORY;
9497 		}
9498 		for (i = 0; i < hw->config.n_io; i++) {
9499 			hw->io[i] = ocs_malloc(hw->os, sizeof(ocs_hw_io_t),
9500 						OCS_M_ZERO | OCS_M_NOWAIT);
9501 			if (hw->io[i] == NULL) {
9502 				ocs_log_err(hw->os, "IO(%d) memory allocation failed\n", i);
9503 				goto error;
9504 			}
9505 		}
9506 
9507 		/* Create WQE buffs for IO */
9508 		hw->wqe_buffs = ocs_malloc(hw->os, hw->config.n_io * hw->sli.config.wqe_size,
9509 				OCS_M_ZERO | OCS_M_NOWAIT);
9510 		if (NULL == hw->wqe_buffs) {
9511 			ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t));
9512 			ocs_log_err(hw->os, "%s: IO WQE buff allocation failed, %d Ios at size %zu\n",
9513 					__func__, hw->config.n_io, hw->sli.config.wqe_size);
9514 			return OCS_HW_RTN_NO_MEMORY;
9515 		}
9516 
9517 	} else {
9518 		/* re-use existing IOs, including SGLs */
9519 		new_alloc = FALSE;
9520 	}
9521 
9522 	if (new_alloc) {
9523 		if (ocs_dma_alloc(hw->os, &hw->xfer_rdy,
9524 					sizeof(fcp_xfer_rdy_iu_t) * hw->config.n_io,
9525 					4/*XXX what does this need to be? */)) {
9526 			ocs_log_err(hw->os, "XFER_RDY buffer allocation failed\n");
9527 			return OCS_HW_RTN_NO_MEMORY;
9528 		}
9529 	}
9530 	xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
9531 	xfer_phys = hw->xfer_rdy.phys;
9532 
9533 	for (i = 0; i < hw->config.n_io; i++) {
9534 		hw_wq_callback_t *wqcb;
9535 
9536 		io = hw->io[i];
9537 
9538 		/* initialize IO fields */
9539 		io->hw = hw;
9540 
9541 		/* Assign a WQE buff */
9542 		io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.config.wqe_size];
9543 
9544 		/* Allocate the request tag for this IO */
9545 		wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_io, io);
9546 		if (wqcb == NULL) {
9547 			ocs_log_err(hw->os, "can't allocate request tag\n");
9548 			return OCS_HW_RTN_NO_RESOURCES;
9549 		}
9550 		io->reqtag = wqcb->instance_index;
9551 
9552 		/* Now for the fields that are initialized on each free */
9553 		ocs_hw_init_free_io(io);
9554 
9555 		/* The XB flag isn't cleared on IO free, so initialize it to zero here */
9556 		io->xbusy = 0;
9557 
9558 		if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_XRI, &io->indicator, &index)) {
9559 			ocs_log_err(hw->os, "sli_resource_alloc failed @ %d\n", i);
9560 			return OCS_HW_RTN_NO_MEMORY;
9561 		}
9562 
9563 		if (new_alloc && ocs_dma_alloc(hw->os, &io->def_sgl, hw->config.n_sgl * sizeof(sli4_sge_t), 64)) {
9564 			ocs_log_err(hw->os, "ocs_dma_alloc failed @ %d\n", i);
9565 			ocs_memset(&io->def_sgl, 0, sizeof(ocs_dma_t));
9566 			return OCS_HW_RTN_NO_MEMORY;
9567 		}
9568 		io->def_sgl_count = hw->config.n_sgl;
9569 		io->sgl = &io->def_sgl;
9570 		io->sgl_count = io->def_sgl_count;
9571 
9572 		if (hw->xfer_rdy.size) {
9573 			io->xfer_rdy.virt = (void *)xfer_virt;
9574 			io->xfer_rdy.phys = xfer_phys;
9575 			io->xfer_rdy.size = sizeof(fcp_xfer_rdy_iu_t);
9576 
9577 			xfer_virt += sizeof(fcp_xfer_rdy_iu_t);
9578 			xfer_phys += sizeof(fcp_xfer_rdy_iu_t);
9579 		}
9580 	}
9581 
9582 	return OCS_HW_RTN_SUCCESS;
9583 error:
9584 	for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
9585 		ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
9586 		hw->io[i] = NULL;
9587 	}
9588 
9589 	return OCS_HW_RTN_NO_MEMORY;
9590 }
9591 
9592 static ocs_hw_rtn_e
9593 ocs_hw_init_io(ocs_hw_t *hw)
9594 {
9595 	uint32_t        i = 0, io_index = 0;
9596 	uint32_t        prereg = 0;
9597 	ocs_hw_io_t	*io = NULL;
9598 	uint8_t		cmd[SLI4_BMBX_SIZE];
9599 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
9600 	uint32_t	nremaining;
9601 	uint32_t	n = 0;
9602 	uint32_t	sgls_per_request = 256;
9603 	ocs_dma_t	**sgls = NULL;
9604 	ocs_dma_t	reqbuf = { 0 };
9605 
9606 	prereg = sli_get_sgl_preregister(&hw->sli);
9607 
9608 	if (prereg) {
9609 		sgls = ocs_malloc(hw->os, sizeof(*sgls) * sgls_per_request, OCS_M_NOWAIT);
9610 		if (sgls == NULL) {
9611 			ocs_log_err(hw->os, "ocs_malloc sgls failed\n");
9612 			return OCS_HW_RTN_NO_MEMORY;
9613 		}
9614 
9615 		rc = ocs_dma_alloc(hw->os, &reqbuf, 32 + sgls_per_request*16, OCS_MIN_DMA_ALIGNMENT);
9616 		if (rc) {
9617 			ocs_log_err(hw->os, "ocs_dma_alloc reqbuf failed\n");
9618 			ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9619 			return OCS_HW_RTN_NO_MEMORY;
9620 		}
9621 	}
9622 
9623 	io = hw->io[io_index];
9624 	for (nremaining = hw->config.n_io; nremaining; nremaining -= n) {
9625 		if (prereg) {
9626 			/* Copy address of SGL's into local sgls[] array, break out if the xri
9627 			 * is not contiguous.
9628 			 */
9629 			for (n = 0; n < MIN(sgls_per_request, nremaining); n++) {
9630 				/* Check that we have contiguous xri values */
9631 				if (n > 0) {
9632 					if (hw->io[io_index + n]->indicator != (hw->io[io_index + n-1]->indicator+1)) {
9633 						break;
9634 					}
9635 				}
9636 				sgls[n] = hw->io[io_index + n]->sgl;
9637 			}
9638 
9639 			if (sli_cmd_fcoe_post_sgl_pages(&hw->sli, cmd, sizeof(cmd),
9640 						io->indicator, n, sgls, NULL, &reqbuf)) {
9641 				if (ocs_hw_command(hw, cmd, OCS_CMD_POLL, NULL, NULL)) {
9642 					rc = OCS_HW_RTN_ERROR;
9643 					ocs_log_err(hw->os, "SGL post failed\n");
9644 					break;
9645 				}
9646 			}
9647 		} else {
9648 			n = nremaining;
9649 		}
9650 
9651 		/* Add to tail if successful */
9652 		for (i = 0; i < n; i ++) {
9653 			io->is_port_owned = 0;
9654 			io->state = OCS_HW_IO_STATE_FREE;
9655 			ocs_list_add_tail(&hw->io_free, io);
9656 			io = hw->io[io_index+1];
9657 			io_index++;
9658 		}
9659 	}
9660 
9661 	if (prereg) {
9662 		ocs_dma_free(hw->os, &reqbuf);
9663 		ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9664 	}
9665 
9666 	return rc;
9667 }
9668 
9669 static int32_t
9670 ocs_hw_flush(ocs_hw_t *hw)
9671 {
9672 	uint32_t	i = 0;
9673 
9674 	/* Process any remaining completions */
9675 	for (i = 0; i < hw->eq_count; i++) {
9676 		ocs_hw_process(hw, i, ~0);
9677 	}
9678 
9679 	return 0;
9680 }
9681 
9682 static int32_t
9683 ocs_hw_command_cancel(ocs_hw_t *hw)
9684 {
9685 
9686 	ocs_lock(&hw->cmd_lock);
9687 
9688 	/*
9689 	 * Manually clean up remaining commands. Note: since this calls
9690 	 * ocs_hw_command_process(), we'll also process the cmd_pending
9691 	 * list, so no need to manually clean that out.
9692 	 */
9693 	while (!ocs_list_empty(&hw->cmd_head)) {
9694 		uint8_t		mqe[SLI4_BMBX_SIZE] = { 0 };
9695 		ocs_command_ctx_t *ctx = ocs_list_get_head(&hw->cmd_head);
9696 
9697 		ocs_log_test(hw->os, "hung command %08x\n",
9698 				NULL == ctx ? UINT32_MAX :
9699 				(NULL == ctx->buf ? UINT32_MAX : *((uint32_t *)ctx->buf)));
9700 		ocs_unlock(&hw->cmd_lock);
9701 		ocs_hw_command_process(hw, -1/*Bad status*/, mqe, SLI4_BMBX_SIZE);
9702 		ocs_lock(&hw->cmd_lock);
9703 	}
9704 
9705 	ocs_unlock(&hw->cmd_lock);
9706 
9707 	return 0;
9708 }
9709 
9710 /**
9711  * @brief Find IO given indicator (xri).
9712  *
9713  * @param hw Hal context.
9714  * @param indicator Indicator (xri) to look for.
9715  *
9716  * @return Returns io if found, NULL otherwise.
9717  */
9718 ocs_hw_io_t *
9719 ocs_hw_io_lookup(ocs_hw_t *hw, uint32_t xri)
9720 {
9721 	uint32_t ioindex;
9722 	ioindex = xri - hw->sli.config.extent[SLI_RSRC_FCOE_XRI].base[0];
9723 	return hw->io[ioindex];
9724 }
9725 
9726 /**
9727  * @brief Issue any pending callbacks for an IO and remove off the timer and pending lists.
9728  *
9729  * @param hw Hal context.
9730  * @param io Pointer to the IO to cleanup.
9731  */
9732 static void
9733 ocs_hw_io_cancel_cleanup(ocs_hw_t *hw, ocs_hw_io_t *io)
9734 {
9735 	ocs_hw_done_t  done = io->done;
9736 	ocs_hw_done_t  abort_done = io->abort_done;
9737 
9738 	/* first check active_wqe list and remove if there */
9739 	if (ocs_list_on_list(&io->wqe_link)) {
9740 		ocs_list_remove(&hw->io_timed_wqe, io);
9741 	}
9742 
9743 	/* Remove from WQ pending list */
9744 	if ((io->wq != NULL) && ocs_list_on_list(&io->wq->pending_list)) {
9745 		ocs_list_remove(&io->wq->pending_list, io);
9746 	}
9747 
9748 	if (io->done) {
9749 		void		*arg = io->arg;
9750 
9751 		io->done = NULL;
9752 		ocs_unlock(&hw->io_lock);
9753 		done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, arg);
9754 		ocs_lock(&hw->io_lock);
9755 	}
9756 
9757 	if (io->abort_done != NULL) {
9758 		void		*abort_arg = io->abort_arg;
9759 
9760 		io->abort_done = NULL;
9761 		ocs_unlock(&hw->io_lock);
9762 		abort_done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, abort_arg);
9763 		ocs_lock(&hw->io_lock);
9764 	}
9765 }
9766 
9767 static int32_t
9768 ocs_hw_io_cancel(ocs_hw_t *hw)
9769 {
9770 	ocs_hw_io_t	*io = NULL;
9771 	ocs_hw_io_t	*tmp_io = NULL;
9772 	uint32_t	iters = 100; /* One second limit */
9773 
9774 	/*
9775 	 * Manually clean up outstanding IO.
9776 	 * Only walk through list once: the backend will cleanup any IOs when done/abort_done is called.
9777 	 */
9778 	ocs_lock(&hw->io_lock);
9779 	ocs_list_foreach_safe(&hw->io_inuse, io, tmp_io) {
9780 		ocs_hw_done_t  done = io->done;
9781 		ocs_hw_done_t  abort_done = io->abort_done;
9782 
9783 		ocs_hw_io_cancel_cleanup(hw, io);
9784 
9785 		/*
9786 		 * Since this is called in a reset/shutdown
9787 		 * case, If there is no callback, then just
9788 		 * free the IO.
9789 		 *
9790 		 * Note: A port owned XRI cannot be on
9791 		 *       the in use list. We cannot call
9792 		 *       ocs_hw_io_free() because we already
9793 		 *       hold the io_lock.
9794 		 */
9795 		if (done == NULL &&
9796 		    abort_done == NULL) {
9797 			/*
9798 			 * Since this is called in a reset/shutdown
9799 			 * case, If there is no callback, then just
9800 			 * free the IO.
9801 			 */
9802 			ocs_hw_io_free_common(hw, io);
9803 			ocs_list_remove(&hw->io_inuse, io);
9804 			ocs_hw_io_free_move_correct_list(hw, io);
9805 		}
9806 	}
9807 
9808 	/*
9809 	 * For port owned XRIs, they are not on the in use list, so
9810 	 * walk though XRIs and issue any callbacks.
9811 	 */
9812 	ocs_list_foreach_safe(&hw->io_port_owned, io, tmp_io) {
9813 		/* check  list and remove if there */
9814 		if (ocs_list_on_list(&io->dnrx_link)) {
9815 			ocs_list_remove(&hw->io_port_dnrx, io);
9816 			ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
9817 		}
9818 		ocs_hw_io_cancel_cleanup(hw, io);
9819 		ocs_list_remove(&hw->io_port_owned, io);
9820 		ocs_hw_io_free_common(hw, io);
9821 	}
9822 	ocs_unlock(&hw->io_lock);
9823 
9824 	/* Give time for the callbacks to complete */
9825 	do {
9826 		ocs_udelay(10000);
9827 		iters--;
9828 	} while (!ocs_list_empty(&hw->io_inuse) && iters);
9829 
9830 	/* Leave a breadcrumb that cleanup is not yet complete. */
9831 	if (!ocs_list_empty(&hw->io_inuse)) {
9832 		ocs_log_test(hw->os, "io_inuse list is not empty\n");
9833 	}
9834 
9835 	return 0;
9836 }
9837 
9838 static int32_t
9839 ocs_hw_io_ini_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *cmnd, uint32_t cmnd_size,
9840 		ocs_dma_t *rsp)
9841 {
9842 	sli4_sge_t	*data = NULL;
9843 
9844 	if (!hw || !io) {
9845 		ocs_log_err(NULL, "bad parm hw=%p io=%p\n", hw, io);
9846 		return OCS_HW_RTN_ERROR;
9847 	}
9848 
9849 	data = io->def_sgl.virt;
9850 
9851 	/* setup command pointer */
9852 	data->buffer_address_high = ocs_addr32_hi(cmnd->phys);
9853 	data->buffer_address_low  = ocs_addr32_lo(cmnd->phys);
9854 	data->buffer_length = cmnd_size;
9855 	data++;
9856 
9857 	/* setup response pointer */
9858 	data->buffer_address_high = ocs_addr32_hi(rsp->phys);
9859 	data->buffer_address_low  = ocs_addr32_lo(rsp->phys);
9860 	data->buffer_length = rsp->size;
9861 
9862 	return 0;
9863 }
9864 
9865 static int32_t
9866 __ocs_read_topology_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9867 {
9868 	sli4_cmd_read_topology_t *read_topo = (sli4_cmd_read_topology_t *)mqe;
9869 
9870 	if (status || read_topo->hdr.status) {
9871 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n",
9872 				status, read_topo->hdr.status);
9873 		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9874 		return -1;
9875 	}
9876 
9877 	switch (read_topo->attention_type) {
9878 	case SLI4_READ_TOPOLOGY_LINK_UP:
9879 		hw->link.status = SLI_LINK_STATUS_UP;
9880 		break;
9881 	case SLI4_READ_TOPOLOGY_LINK_DOWN:
9882 		hw->link.status = SLI_LINK_STATUS_DOWN;
9883 		break;
9884 	case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
9885 		hw->link.status = SLI_LINK_STATUS_NO_ALPA;
9886 		break;
9887 	default:
9888 		hw->link.status = SLI_LINK_STATUS_MAX;
9889 		break;
9890 	}
9891 
9892 	switch (read_topo->topology) {
9893 	case SLI4_READ_TOPOLOGY_NPORT:
9894 		hw->link.topology = SLI_LINK_TOPO_NPORT;
9895 		break;
9896 	case SLI4_READ_TOPOLOGY_FC_AL:
9897 		hw->link.topology = SLI_LINK_TOPO_LOOP;
9898 		if (SLI_LINK_STATUS_UP == hw->link.status) {
9899 			hw->link.loop_map = hw->loop_map.virt;
9900 		}
9901 		hw->link.fc_id = read_topo->acquired_al_pa;
9902 		break;
9903 	default:
9904 		hw->link.topology = SLI_LINK_TOPO_MAX;
9905 		break;
9906 	}
9907 
9908 	hw->link.medium = SLI_LINK_MEDIUM_FC;
9909 
9910 	switch (read_topo->link_current.link_speed) {
9911 	case SLI4_READ_TOPOLOGY_SPEED_1G:
9912 		hw->link.speed =  1 * 1000;
9913 		break;
9914 	case SLI4_READ_TOPOLOGY_SPEED_2G:
9915 		hw->link.speed =  2 * 1000;
9916 		break;
9917 	case SLI4_READ_TOPOLOGY_SPEED_4G:
9918 		hw->link.speed =  4 * 1000;
9919 		break;
9920 	case SLI4_READ_TOPOLOGY_SPEED_8G:
9921 		hw->link.speed =  8 * 1000;
9922 		break;
9923 	case SLI4_READ_TOPOLOGY_SPEED_16G:
9924 		hw->link.speed = 16 * 1000;
9925 		hw->link.loop_map = NULL;
9926 		break;
9927 	case SLI4_READ_TOPOLOGY_SPEED_32G:
9928 		hw->link.speed = 32 * 1000;
9929 		hw->link.loop_map = NULL;
9930 		break;
9931 	}
9932 
9933 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9934 
9935 	ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9936 
9937 	return 0;
9938 }
9939 
9940 static int32_t
9941 __ocs_hw_port_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9942 {
9943 	ocs_sli_port_t	*sport = ctx->app;
9944 	ocs_hw_t	*hw = sport->hw;
9945 
9946 	smtrace("port");
9947 
9948 	switch (evt) {
9949 	case OCS_EVT_EXIT:
9950 		/* ignore */
9951 		break;
9952 
9953 	case OCS_EVT_HW_PORT_REQ_FREE:
9954 	case OCS_EVT_HW_PORT_REQ_ATTACH:
9955 		if (data != NULL) {
9956 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
9957 		}
9958 		/* fall through */
9959 	default:
9960 		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
9961 		break;
9962 	}
9963 
9964 	return 0;
9965 }
9966 
9967 static void *
9968 __ocs_hw_port_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9969 {
9970 	ocs_sli_port_t	*sport = ctx->app;
9971 	ocs_hw_t	*hw = sport->hw;
9972 
9973 	smtrace("port");
9974 
9975 	switch (evt) {
9976 	case OCS_EVT_ENTER:
9977 		if (data != NULL) {
9978 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
9979 		}
9980 		if (hw->callback.port != NULL) {
9981 			hw->callback.port(hw->args.port,
9982 					OCS_HW_PORT_FREE_FAIL, sport);
9983 		}
9984 		break;
9985 	default:
9986 		break;
9987 	}
9988 
9989 	return NULL;
9990 }
9991 
9992 static void *
9993 __ocs_hw_port_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9994 {
9995 	ocs_sli_port_t	*sport = ctx->app;
9996 	ocs_hw_t	*hw = sport->hw;
9997 
9998 	smtrace("port");
9999 
10000 	switch (evt) {
10001 	case OCS_EVT_ENTER:
10002 		/* free SLI resource */
10003 		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator)) {
10004 			ocs_log_err(hw->os, "FCOE_VPI free failure addr=%#x\n", sport->fc_id);
10005 		}
10006 
10007 		/* free mailbox buffer */
10008 		if (data != NULL) {
10009 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10010 		}
10011 		if (hw->callback.port != NULL) {
10012 			hw->callback.port(hw->args.port,
10013 					OCS_HW_PORT_FREE_OK, sport);
10014 		}
10015 		break;
10016 	default:
10017 		break;
10018 	}
10019 
10020 	return NULL;
10021 }
10022 
10023 static void *
10024 __ocs_hw_port_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10025 {
10026 	ocs_sli_port_t	*sport = ctx->app;
10027 	ocs_hw_t	*hw = sport->hw;
10028 
10029 	smtrace("port");
10030 
10031 	switch (evt) {
10032 	case OCS_EVT_ENTER:
10033 		/* free SLI resource */
10034 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10035 
10036 		/* free mailbox buffer */
10037 		if (data != NULL) {
10038 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10039 		}
10040 
10041 		if (hw->callback.port != NULL) {
10042 			hw->callback.port(hw->args.port,
10043 					OCS_HW_PORT_ATTACH_FAIL, sport);
10044 		}
10045 		if (sport->sm_free_req_pending) {
10046 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10047 		}
10048 		break;
10049 	default:
10050 		__ocs_hw_port_common(__func__, ctx, evt, data);
10051 		break;
10052 	}
10053 
10054 	return NULL;
10055 }
10056 
10057 static void *
10058 __ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10059 {
10060 	ocs_sli_port_t	*sport = ctx->app;
10061 	ocs_hw_t	*hw = sport->hw;
10062 	uint8_t		*cmd = NULL;
10063 
10064 	smtrace("port");
10065 
10066 	switch (evt) {
10067 	case OCS_EVT_ENTER:
10068 		/* allocate memory and send unreg_vpi */
10069 		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10070 		if (!cmd) {
10071 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10072 			break;
10073 		}
10074 
10075 		if (0 == sli_cmd_unreg_vpi(&hw->sli, cmd, SLI4_BMBX_SIZE, sport->indicator,
10076 					   SLI4_UNREG_TYPE_PORT)) {
10077 			ocs_log_err(hw->os, "UNREG_VPI format failure\n");
10078 			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10079 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10080 			break;
10081 		}
10082 
10083 		if (ocs_hw_command(hw, cmd, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10084 			ocs_log_err(hw->os, "UNREG_VPI command failure\n");
10085 			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10086 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10087 			break;
10088 		}
10089 		break;
10090 	case OCS_EVT_RESPONSE:
10091 		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10092 		break;
10093 	case OCS_EVT_ERROR:
10094 		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10095 		break;
10096 	default:
10097 		__ocs_hw_port_common(__func__, ctx, evt, data);
10098 		break;
10099 	}
10100 
10101 	return NULL;
10102 }
10103 
10104 static void *
10105 __ocs_hw_port_free_nop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10106 {
10107 	ocs_sli_port_t	*sport = ctx->app;
10108 	ocs_hw_t	*hw = sport->hw;
10109 
10110 	smtrace("port");
10111 
10112 	switch (evt) {
10113 	case OCS_EVT_ENTER:
10114 		/* Forward to execute in mailbox completion processing context */
10115 		if (ocs_hw_async_call(hw, __ocs_hw_port_realloc_cb, sport)) {
10116 			ocs_log_err(hw->os, "ocs_hw_async_call failed\n");
10117 		}
10118 		break;
10119 	case OCS_EVT_RESPONSE:
10120 		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10121 		break;
10122 	case OCS_EVT_ERROR:
10123 		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10124 		break;
10125 	default:
10126 		break;
10127 	}
10128 
10129 	return NULL;
10130 }
10131 
10132 static void *
10133 __ocs_hw_port_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10134 {
10135 	ocs_sli_port_t	*sport = ctx->app;
10136 	ocs_hw_t	*hw = sport->hw;
10137 
10138 	smtrace("port");
10139 
10140 	switch (evt) {
10141 	case OCS_EVT_ENTER:
10142 		if (data != NULL) {
10143 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10144 		}
10145 		if (hw->callback.port != NULL) {
10146 			hw->callback.port(hw->args.port,
10147 					OCS_HW_PORT_ATTACH_OK, sport);
10148 		}
10149 		if (sport->sm_free_req_pending) {
10150 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10151 		}
10152 		break;
10153 	case OCS_EVT_HW_PORT_REQ_FREE:
10154 		/* virtual/physical port request free */
10155 		ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10156 		break;
10157 	default:
10158 		__ocs_hw_port_common(__func__, ctx, evt, data);
10159 		break;
10160 	}
10161 
10162 	return NULL;
10163 }
10164 
10165 static void *
10166 __ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10167 {
10168 	ocs_sli_port_t	*sport = ctx->app;
10169 	ocs_hw_t	*hw = sport->hw;
10170 
10171 	smtrace("port");
10172 
10173 	switch (evt) {
10174 	case OCS_EVT_ENTER:
10175 		if (0 == sli_cmd_reg_vpi(&hw->sli, data, SLI4_BMBX_SIZE, sport, FALSE)) {
10176 			ocs_log_err(hw->os, "REG_VPI format failure\n");
10177 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10178 			break;
10179 		}
10180 
10181 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10182 			ocs_log_err(hw->os, "REG_VPI command failure\n");
10183 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10184 			break;
10185 		}
10186 		break;
10187 	case OCS_EVT_RESPONSE:
10188 		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10189 		break;
10190 	case OCS_EVT_ERROR:
10191 		ocs_sm_transition(ctx, __ocs_hw_port_attach_report_fail, data);
10192 		break;
10193 	case OCS_EVT_HW_PORT_REQ_FREE:
10194 		/* Wait for attach response and then free */
10195 		sport->sm_free_req_pending = 1;
10196 		break;
10197 	default:
10198 		__ocs_hw_port_common(__func__, ctx, evt, data);
10199 		break;
10200 	}
10201 
10202 	return NULL;
10203 }
10204 
10205 static void *
10206 __ocs_hw_port_done(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10207 {
10208 	ocs_sli_port_t	*sport = ctx->app;
10209 	ocs_hw_t	*hw = sport->hw;
10210 
10211 	smtrace("port");
10212 
10213 	switch (evt) {
10214 	case OCS_EVT_ENTER:
10215 		/* free SLI resource */
10216 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10217 
10218 		/* free mailbox buffer */
10219 		if (data != NULL) {
10220 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10221 		}
10222 		break;
10223 	default:
10224 		__ocs_hw_port_common(__func__, ctx, evt, data);
10225 		break;
10226 	}
10227 
10228 	return NULL;
10229 }
10230 
10231 static void *
10232 __ocs_hw_port_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10233 {
10234 	ocs_sli_port_t	*sport = ctx->app;
10235 	ocs_hw_t	*hw = sport->hw;
10236 
10237 	smtrace("port");
10238 
10239 	switch (evt) {
10240 	case OCS_EVT_ENTER:
10241 		if (data != NULL) {
10242 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10243 		}
10244 		if (hw->callback.port != NULL) {
10245 			hw->callback.port(hw->args.port,
10246 					OCS_HW_PORT_ALLOC_OK, sport);
10247 		}
10248 		/* If there is a pending free request, then handle it now */
10249 		if (sport->sm_free_req_pending) {
10250 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10251 		}
10252 		break;
10253 	case OCS_EVT_HW_PORT_REQ_ATTACH:
10254 		/* virtual port requests attach */
10255 		ocs_sm_transition(ctx, __ocs_hw_port_attach_reg_vpi, data);
10256 		break;
10257 	case OCS_EVT_HW_PORT_ATTACH_OK:
10258 		/* physical port attached (as part of attaching domain) */
10259 		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10260 		break;
10261 	case OCS_EVT_HW_PORT_REQ_FREE:
10262 		/* virtual port request free */
10263 		if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
10264 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10265 		} else {
10266 			/*
10267 			 * Note: BE3/Skyhawk will respond with a status of 0x20
10268 			 *       unless the reg_vpi has been issued, so we can
10269 			 *       skip the unreg_vpi for these adapters.
10270 			 *
10271 			 * Send a nop to make sure that free doesn't occur in
10272 			 * same context
10273 			 */
10274 			ocs_sm_transition(ctx, __ocs_hw_port_free_nop, NULL);
10275 		}
10276 		break;
10277 	default:
10278 		__ocs_hw_port_common(__func__, ctx, evt, data);
10279 		break;
10280 	}
10281 
10282 	return NULL;
10283 }
10284 
10285 static void *
10286 __ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10287 {
10288 	ocs_sli_port_t	*sport = ctx->app;
10289 	ocs_hw_t	*hw = sport->hw;
10290 
10291 	smtrace("port");
10292 
10293 	switch (evt) {
10294 	case OCS_EVT_ENTER:
10295 		/* free SLI resource */
10296 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10297 
10298 		/* free mailbox buffer */
10299 		if (data != NULL) {
10300 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10301 		}
10302 
10303 		if (hw->callback.port != NULL) {
10304 			hw->callback.port(hw->args.port,
10305 					OCS_HW_PORT_ALLOC_FAIL, sport);
10306 		}
10307 
10308 		/* If there is a pending free request, then handle it now */
10309 		if (sport->sm_free_req_pending) {
10310 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10311 		}
10312 		break;
10313 	default:
10314 		__ocs_hw_port_common(__func__, ctx, evt, data);
10315 		break;
10316 	}
10317 
10318 	return NULL;
10319 }
10320 
10321 static void *
10322 __ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10323 {
10324 	ocs_sli_port_t	*sport = ctx->app;
10325 	ocs_hw_t	*hw = sport->hw;
10326 	uint8_t		*payload = NULL;
10327 
10328 	smtrace("port");
10329 
10330 	switch (evt) {
10331 	case OCS_EVT_ENTER:
10332 		/* allocate memory for the service parameters */
10333 		if (ocs_dma_alloc(hw->os, &sport->dma, 112, 4)) {
10334 			ocs_log_err(hw->os, "Failed to allocate DMA memory\n");
10335 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10336 			break;
10337 		}
10338 
10339 		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10340 					&sport->dma, sport->indicator)) {
10341 			ocs_log_err(hw->os, "READ_SPARM64 allocation failure\n");
10342 			ocs_dma_free(hw->os, &sport->dma);
10343 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10344 			break;
10345 		}
10346 
10347 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10348 			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10349 			ocs_dma_free(hw->os, &sport->dma);
10350 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10351 			break;
10352 		}
10353 		break;
10354 	case OCS_EVT_RESPONSE:
10355 		payload = sport->dma.virt;
10356 
10357 		ocs_display_sparams(sport->display_name, "sport sparm64", 0, NULL, payload);
10358 
10359 		ocs_memcpy(&sport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
10360 				sizeof(sport->sli_wwpn));
10361 		ocs_memcpy(&sport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
10362 				sizeof(sport->sli_wwnn));
10363 
10364 		ocs_dma_free(hw->os, &sport->dma);
10365 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_init_vpi, data);
10366 		break;
10367 	case OCS_EVT_ERROR:
10368 		ocs_dma_free(hw->os, &sport->dma);
10369 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10370 		break;
10371 	case OCS_EVT_HW_PORT_REQ_FREE:
10372 		/* Wait for attach response and then free */
10373 		sport->sm_free_req_pending = 1;
10374 		break;
10375 	case OCS_EVT_EXIT:
10376 		break;
10377 	default:
10378 		__ocs_hw_port_common(__func__, ctx, evt, data);
10379 		break;
10380 	}
10381 
10382 	return NULL;
10383 }
10384 
10385 static void *
10386 __ocs_hw_port_alloc_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10387 {
10388 	ocs_sli_port_t	*sport = ctx->app;
10389 
10390 	smtrace("port");
10391 
10392 	switch (evt) {
10393 	case OCS_EVT_ENTER:
10394 		/* no-op */
10395 		break;
10396 	case OCS_EVT_HW_PORT_ALLOC_OK:
10397 		ocs_sm_transition(ctx, __ocs_hw_port_allocated, NULL);
10398 		break;
10399 	case OCS_EVT_HW_PORT_ALLOC_FAIL:
10400 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, NULL);
10401 		break;
10402 	case OCS_EVT_HW_PORT_REQ_FREE:
10403 		/* Wait for attach response and then free */
10404 		sport->sm_free_req_pending = 1;
10405 		break;
10406 	default:
10407 		__ocs_hw_port_common(__func__, ctx, evt, data);
10408 		break;
10409 	}
10410 
10411 	return NULL;
10412 }
10413 
10414 static void *
10415 __ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10416 {
10417 	ocs_sli_port_t	*sport = ctx->app;
10418 	ocs_hw_t	*hw = sport->hw;
10419 
10420 	smtrace("port");
10421 
10422 	switch (evt) {
10423 	case OCS_EVT_ENTER:
10424 		/* If there is a pending free request, then handle it now */
10425 		if (sport->sm_free_req_pending) {
10426 			ocs_sm_transition(ctx, __ocs_hw_port_freed, NULL);
10427 			return NULL;
10428 		}
10429 
10430 		/* TODO XXX transitioning to done only works if this is called
10431 		 * directly from ocs_hw_port_alloc BUT not if called from
10432 		 * read_sparm64. In the later case, we actually want to go
10433 		 * through report_ok/fail
10434 		 */
10435 		if (0 == sli_cmd_init_vpi(&hw->sli, data, SLI4_BMBX_SIZE,
10436 					sport->indicator, sport->domain->indicator)) {
10437 			ocs_log_err(hw->os, "INIT_VPI allocation failure\n");
10438 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10439 			break;
10440 		}
10441 
10442 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10443 			ocs_log_err(hw->os, "INIT_VPI command failure\n");
10444 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10445 			break;
10446 		}
10447 		break;
10448 	case OCS_EVT_RESPONSE:
10449 		ocs_sm_transition(ctx, __ocs_hw_port_allocated, data);
10450 		break;
10451 	case OCS_EVT_ERROR:
10452 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10453 		break;
10454 	case OCS_EVT_HW_PORT_REQ_FREE:
10455 		/* Wait for attach response and then free */
10456 		sport->sm_free_req_pending = 1;
10457 		break;
10458 	case OCS_EVT_EXIT:
10459 		break;
10460 	default:
10461 		__ocs_hw_port_common(__func__, ctx, evt, data);
10462 		break;
10463 	}
10464 
10465 	return NULL;
10466 }
10467 
10468 static int32_t
10469 __ocs_hw_port_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10470 {
10471 	ocs_sli_port_t *sport = arg;
10472 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10473 	ocs_sm_event_t	evt;
10474 
10475 	if (status || hdr->status) {
10476 		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10477 			      sport->indicator, status, hdr->status);
10478 		evt = OCS_EVT_ERROR;
10479 	} else {
10480 		evt = OCS_EVT_RESPONSE;
10481 	}
10482 
10483 	ocs_sm_post_event(&sport->ctx, evt, mqe);
10484 
10485 	return 0;
10486 }
10487 
10488 static int32_t
10489 __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10490 {
10491 	ocs_sli_port_t *sport = arg;
10492 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10493 	ocs_sm_event_t	evt;
10494 	uint8_t *mqecpy;
10495 
10496 	if (status || hdr->status) {
10497 		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10498 			      sport->indicator, status, hdr->status);
10499 		evt = OCS_EVT_ERROR;
10500 	} else {
10501 		evt = OCS_EVT_RESPONSE;
10502 	}
10503 
10504 	/*
10505 	 * In this case we have to malloc a mailbox command buffer, as it is reused
10506 	 * in the state machine post event call, and eventually freed
10507 	 */
10508 	mqecpy = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10509 	if (mqecpy == NULL) {
10510 		ocs_log_err(hw->os, "malloc mqecpy failed\n");
10511 		return -1;
10512 	}
10513 	ocs_memcpy(mqecpy, mqe, SLI4_BMBX_SIZE);
10514 
10515 	ocs_sm_post_event(&sport->ctx, evt, mqecpy);
10516 
10517 	return 0;
10518 }
10519 
10520 /***************************************************************************
10521  * Domain state machine
10522  */
10523 
10524 static int32_t
10525 __ocs_hw_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10526 {
10527 	ocs_domain_t	*domain = ctx->app;
10528 	ocs_hw_t	*hw = domain->hw;
10529 
10530 	smtrace("domain");
10531 
10532 	switch (evt) {
10533 	case OCS_EVT_EXIT:
10534 		/* ignore */
10535 		break;
10536 
10537 	default:
10538 		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
10539 		break;
10540 	}
10541 
10542 	return 0;
10543 }
10544 
10545 static void *
10546 __ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10547 {
10548 	ocs_domain_t	*domain = ctx->app;
10549 	ocs_hw_t	*hw = domain->hw;
10550 
10551 	smtrace("domain");
10552 
10553 	switch (evt) {
10554 	case OCS_EVT_ENTER:
10555 		/* free command buffer */
10556 		if (data != NULL) {
10557 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10558 		}
10559 		/* free SLI resources */
10560 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10561 		/* TODO how to free FCFI (or do we at all)? */
10562 
10563 		if (hw->callback.domain != NULL) {
10564 			hw->callback.domain(hw->args.domain,
10565 					OCS_HW_DOMAIN_ALLOC_FAIL,
10566 					domain);
10567 		}
10568 		break;
10569 	default:
10570 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10571 		break;
10572 	}
10573 
10574 	return NULL;
10575 }
10576 
10577 static void *
10578 __ocs_hw_domain_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10579 {
10580 	ocs_domain_t	*domain = ctx->app;
10581 	ocs_hw_t	*hw = domain->hw;
10582 
10583 	smtrace("domain");
10584 
10585 	switch (evt) {
10586 	case OCS_EVT_ENTER:
10587 		/* free mailbox buffer and send alloc ok to physical sport */
10588 		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10589 		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ATTACH_OK, NULL);
10590 
10591 		/* now inform registered callbacks */
10592 		if (hw->callback.domain != NULL) {
10593 			hw->callback.domain(hw->args.domain,
10594 					OCS_HW_DOMAIN_ATTACH_OK,
10595 					domain);
10596 		}
10597 		break;
10598 	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10599 		ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10600 		break;
10601 	default:
10602 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10603 		break;
10604 	}
10605 
10606 	return NULL;
10607 }
10608 
10609 static void *
10610 __ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10611 {
10612 	ocs_domain_t	*domain = ctx->app;
10613 	ocs_hw_t	*hw = domain->hw;
10614 
10615 	smtrace("domain");
10616 
10617 	switch (evt) {
10618 	case OCS_EVT_ENTER:
10619 		if (data != NULL) {
10620 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10621 		}
10622 		/* free SLI resources */
10623 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10624 		/* TODO how to free FCFI (or do we at all)? */
10625 
10626 		if (hw->callback.domain != NULL) {
10627 			hw->callback.domain(hw->args.domain,
10628 					OCS_HW_DOMAIN_ATTACH_FAIL,
10629 					domain);
10630 		}
10631 		break;
10632 	case OCS_EVT_EXIT:
10633 		break;
10634 	default:
10635 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10636 		break;
10637 	}
10638 
10639 	return NULL;
10640 }
10641 
10642 static void *
10643 __ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10644 {
10645 	ocs_domain_t	*domain = ctx->app;
10646 	ocs_hw_t	*hw = domain->hw;
10647 
10648 	smtrace("domain");
10649 
10650 	switch (evt) {
10651 	case OCS_EVT_ENTER:
10652 
10653 		ocs_display_sparams("", "reg vpi", 0, NULL, domain->dma.virt);
10654 
10655 		if (0 == sli_cmd_reg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain)) {
10656 			ocs_log_err(hw->os, "REG_VFI format failure\n");
10657 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10658 			break;
10659 		}
10660 
10661 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10662 			ocs_log_err(hw->os, "REG_VFI command failure\n");
10663 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10664 			break;
10665 		}
10666 		break;
10667 	case OCS_EVT_RESPONSE:
10668 		ocs_sm_transition(ctx, __ocs_hw_domain_attached, data);
10669 		break;
10670 	case OCS_EVT_ERROR:
10671 		ocs_sm_transition(ctx, __ocs_hw_domain_attach_report_fail, data);
10672 		break;
10673 	default:
10674 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10675 		break;
10676 	}
10677 
10678 	return NULL;
10679 }
10680 
10681 static void *
10682 __ocs_hw_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10683 {
10684 	ocs_domain_t	*domain = ctx->app;
10685 	ocs_hw_t	*hw = domain->hw;
10686 
10687 	smtrace("domain");
10688 
10689 	switch (evt) {
10690 	case OCS_EVT_ENTER:
10691 		/* free mailbox buffer and send alloc ok to physical sport */
10692 		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10693 		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ALLOC_OK, NULL);
10694 
10695 		ocs_hw_domain_add(hw, domain);
10696 
10697 		/* now inform registered callbacks */
10698 		if (hw->callback.domain != NULL) {
10699 			hw->callback.domain(hw->args.domain,
10700 					OCS_HW_DOMAIN_ALLOC_OK,
10701 					domain);
10702 		}
10703 		break;
10704 	case OCS_EVT_HW_DOMAIN_REQ_ATTACH:
10705 		ocs_sm_transition(ctx, __ocs_hw_domain_attach_reg_vfi, data);
10706 		break;
10707 	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10708 		/* unreg_fcfi/vfi */
10709 		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10710 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, NULL);
10711 		} else {
10712 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10713 		}
10714 		break;
10715 	default:
10716 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10717 		break;
10718 	}
10719 
10720 	return NULL;
10721 }
10722 
10723 static void *
10724 __ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10725 {
10726 	ocs_domain_t	*domain = ctx->app;
10727 	ocs_hw_t	*hw = domain->hw;
10728 
10729 	smtrace("domain");
10730 
10731 	switch (evt) {
10732 	case OCS_EVT_ENTER:
10733 		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10734 					&domain->dma, SLI4_READ_SPARM64_VPI_DEFAULT)) {
10735 			ocs_log_err(hw->os, "READ_SPARM64 format failure\n");
10736 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10737 			break;
10738 		}
10739 
10740 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10741 			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10742 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10743 			break;
10744 		}
10745 		break;
10746 	case OCS_EVT_EXIT:
10747 		break;
10748 	case OCS_EVT_RESPONSE:
10749 		ocs_display_sparams(domain->display_name, "domain sparm64", 0, NULL, domain->dma.virt);
10750 
10751 		ocs_sm_transition(ctx, __ocs_hw_domain_allocated, data);
10752 		break;
10753 	case OCS_EVT_ERROR:
10754 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10755 		break;
10756 	default:
10757 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10758 		break;
10759 	}
10760 
10761 	return NULL;
10762 }
10763 
10764 static void *
10765 __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10766 {
10767 	ocs_domain_t	*domain = ctx->app;
10768 	ocs_sli_port_t	*sport = domain->sport;
10769 	ocs_hw_t	*hw = domain->hw;
10770 
10771 	smtrace("domain");
10772 
10773 	switch (evt) {
10774 	case OCS_EVT_ENTER:
10775 		if (0 == sli_cmd_init_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->indicator,
10776 					domain->fcf_indicator, sport->indicator)) {
10777 			ocs_log_err(hw->os, "INIT_VFI format failure\n");
10778 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10779 			break;
10780 		}
10781 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10782 			ocs_log_err(hw->os, "INIT_VFI command failure\n");
10783 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10784 			break;
10785 		}
10786 		break;
10787 	case OCS_EVT_EXIT:
10788 		break;
10789 	case OCS_EVT_RESPONSE:
10790 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10791 		break;
10792 	case OCS_EVT_ERROR:
10793 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10794 		break;
10795 	default:
10796 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10797 		break;
10798 	}
10799 
10800 	return NULL;
10801 }
10802 
10803 static void *
10804 __ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10805 {
10806 	ocs_domain_t	*domain = ctx->app;
10807 	ocs_hw_t	*hw = domain->hw;
10808 
10809 	smtrace("domain");
10810 
10811 	switch (evt) {
10812 	case OCS_EVT_ENTER: {
10813 		sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
10814 		uint32_t i;
10815 
10816 		/* Set the filter match/mask values from hw's filter_def values */
10817 		for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
10818 			rq_cfg[i].rq_id = 0xffff;
10819 			rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
10820 			rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
10821 			rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
10822 			rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
10823 		}
10824 
10825 		/* Set the rq_id for each, in order of RQ definition */
10826 		for (i = 0; i < hw->hw_rq_count; i++) {
10827 			if (i >= ARRAY_SIZE(rq_cfg)) {
10828 				ocs_log_warn(hw->os, "more RQs than REG_FCFI filter entries\n");
10829 				break;
10830 			}
10831 			rq_cfg[i].rq_id = hw->hw_rq[i]->hdr->id;
10832 		}
10833 
10834 		if (!data) {
10835 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10836 			break;
10837 		}
10838 
10839 		if (hw->hw_mrq_count) {
10840 			if (OCS_HW_RTN_SUCCESS != ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE,
10841 				 domain->vlan_id, domain->fcf)) {
10842 				ocs_log_err(hw->os, "REG_FCFI_MRQ format failure\n");
10843 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10844 				break;
10845 			}
10846 
10847 		} else {
10848 			if (0 == sli_cmd_reg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf,
10849 						rq_cfg, domain->vlan_id)) {
10850 				ocs_log_err(hw->os, "REG_FCFI format failure\n");
10851 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10852 				break;
10853 			}
10854 		}
10855 
10856 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10857 			ocs_log_err(hw->os, "REG_FCFI command failure\n");
10858 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10859 			break;
10860 		}
10861 		break;
10862 	}
10863 	case OCS_EVT_EXIT:
10864 		break;
10865 	case OCS_EVT_RESPONSE:
10866 		if (!data) {
10867 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10868 			break;
10869 		}
10870 
10871 		domain->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)data)->fcfi;
10872 
10873 		/*
10874 		 * IF_TYPE 0 devices do not support explicit VFI and VPI initialization
10875 		 * and instead rely on implicit initialization during VFI registration.
10876 		 * Short circuit normal processing here for those devices.
10877 		 */
10878 		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10879 			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10880 		} else {
10881 			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_init_vfi, data);
10882 		}
10883 		break;
10884 	case OCS_EVT_ERROR:
10885 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10886 		break;
10887 	default:
10888 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10889 		break;
10890 	}
10891 
10892 	return NULL;
10893 }
10894 
10895 static void *
10896 __ocs_hw_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10897 {
10898 	ocs_domain_t	*domain = ctx->app;
10899 	ocs_hw_t	*hw = domain->hw;
10900 
10901 	smtrace("domain");
10902 
10903 	switch (evt) {
10904 	case OCS_EVT_ENTER:
10905 		if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
10906 			/*
10907 			 * For FC, the HW alread registered a FCFI
10908 			 * Copy FCF information into the domain and jump to INIT_VFI
10909 			 */
10910 			domain->fcf_indicator = hw->fcf_indicator;
10911 			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_init_vfi, data);
10912 		} else {
10913 			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_reg_fcfi, data);
10914 		}
10915 		break;
10916 	default:
10917 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10918 		break;
10919 	}
10920 
10921 	return NULL;
10922 }
10923 
10924 static void *
10925 __ocs_hw_domain_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10926 {
10927 	ocs_domain_t	*domain = ctx->app;
10928 
10929 	smtrace("domain");
10930 
10931 	switch (evt) {
10932 	case OCS_EVT_ENTER:
10933 		if (domain != NULL) {
10934 			ocs_hw_t	*hw = domain->hw;
10935 
10936 			ocs_hw_domain_del(hw, domain);
10937 
10938 			if (hw->callback.domain != NULL) {
10939 				hw->callback.domain(hw->args.domain,
10940 						     OCS_HW_DOMAIN_FREE_FAIL,
10941 						     domain);
10942 			}
10943 		}
10944 
10945 		/* free command buffer */
10946 		if (data != NULL) {
10947 			ocs_free(domain != NULL ? domain->hw->os : NULL, data, SLI4_BMBX_SIZE);
10948 		}
10949 		break;
10950 	case OCS_EVT_EXIT:
10951 		break;
10952 	default:
10953 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10954 		break;
10955 	}
10956 
10957 	return NULL;
10958 }
10959 
10960 static void *
10961 __ocs_hw_domain_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10962 {
10963 	ocs_domain_t	*domain = ctx->app;
10964 
10965 	smtrace("domain");
10966 
10967 	switch (evt) {
10968 	case OCS_EVT_ENTER:
10969 		/* Free DMA and mailbox buffer */
10970 		if (domain != NULL) {
10971 			ocs_hw_t *hw = domain->hw;
10972 
10973 			/* free VFI resource */
10974 			sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI,
10975 					  domain->indicator);
10976 
10977 			ocs_hw_domain_del(hw, domain);
10978 
10979 			/* inform registered callbacks */
10980 			if (hw->callback.domain != NULL) {
10981 				hw->callback.domain(hw->args.domain,
10982 						     OCS_HW_DOMAIN_FREE_OK,
10983 						     domain);
10984 			}
10985 		}
10986 		if (data != NULL) {
10987 			ocs_free(NULL, data, SLI4_BMBX_SIZE);
10988 		}
10989 		break;
10990 	case OCS_EVT_EXIT:
10991 		break;
10992 	default:
10993 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10994 		break;
10995 	}
10996 
10997 	return NULL;
10998 }
10999 
11000 static void *
11001 __ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11002 {
11003 	ocs_domain_t	*domain = ctx->app;
11004 	ocs_hw_t	*hw = domain->hw;
11005 
11006 	smtrace("domain");
11007 
11008 	switch (evt) {
11009 	case OCS_EVT_ENTER:
11010 		/* if we're in the middle of a teardown, skip sending rediscover */
11011 		if (hw->state == OCS_HW_STATE_TEARDOWN_IN_PROGRESS) {
11012 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11013 			break;
11014 		}
11015 		if (0 == sli_cmd_fcoe_rediscover_fcf(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf)) {
11016 			ocs_log_err(hw->os, "REDISCOVER_FCF format failure\n");
11017 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11018 			break;
11019 		}
11020 
11021 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11022 			ocs_log_err(hw->os, "REDISCOVER_FCF command failure\n");
11023 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11024 		}
11025 		break;
11026 	case OCS_EVT_RESPONSE:
11027 	case OCS_EVT_ERROR:
11028 		/* REDISCOVER_FCF can fail if none exist */
11029 		ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11030 		break;
11031 	case OCS_EVT_EXIT:
11032 		break;
11033 	default:
11034 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11035 		break;
11036 	}
11037 
11038 	return NULL;
11039 }
11040 
11041 static void *
11042 __ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11043 {
11044 	ocs_domain_t	*domain = ctx->app;
11045 	ocs_hw_t	*hw = domain->hw;
11046 
11047 	smtrace("domain");
11048 
11049 	switch (evt) {
11050 	case OCS_EVT_ENTER:
11051 		if (data == NULL) {
11052 			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11053 			if (!data) {
11054 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11055 				break;
11056 			}
11057 		}
11058 
11059 		if (0 == sli_cmd_unreg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf_indicator)) {
11060 			ocs_log_err(hw->os, "UNREG_FCFI format failure\n");
11061 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11062 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11063 			break;
11064 		}
11065 
11066 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11067 			ocs_log_err(hw->os, "UNREG_FCFI command failure\n");
11068 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11069 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11070 			break;
11071 		}
11072 		break;
11073 	case OCS_EVT_RESPONSE:
11074 		if (domain->req_rediscover_fcf) {
11075 			domain->req_rediscover_fcf = FALSE;
11076 			ocs_sm_transition(ctx, __ocs_hw_domain_free_redisc_fcf, data);
11077 		} else {
11078 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11079 		}
11080 		break;
11081 	case OCS_EVT_ERROR:
11082 		ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11083 		break;
11084 	case OCS_EVT_EXIT:
11085 		break;
11086 	default:
11087 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11088 		break;
11089 	}
11090 
11091 	return NULL;
11092 }
11093 
11094 static void *
11095 __ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11096 {
11097 	ocs_domain_t	*domain = ctx->app;
11098 	ocs_hw_t	*hw = domain->hw;
11099 	uint8_t		is_fc = FALSE;
11100 
11101 	smtrace("domain");
11102 
11103 	is_fc = (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC);
11104 
11105 	switch (evt) {
11106 	case OCS_EVT_ENTER:
11107 		if (data == NULL) {
11108 			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11109 			if (!data) {
11110 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11111 				break;
11112 			}
11113 		}
11114 
11115 		if (0 == sli_cmd_unreg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain,
11116 					SLI4_UNREG_TYPE_DOMAIN)) {
11117 			ocs_log_err(hw->os, "UNREG_VFI format failure\n");
11118 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11119 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11120 			break;
11121 		}
11122 
11123 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11124 			ocs_log_err(hw->os, "UNREG_VFI command failure\n");
11125 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11126 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11127 			break;
11128 		}
11129 		break;
11130 	case OCS_EVT_ERROR:
11131 		if (is_fc) {
11132 			ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11133 		} else {
11134 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11135 		}
11136 		break;
11137 	case OCS_EVT_RESPONSE:
11138 		if (is_fc) {
11139 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11140 		} else {
11141 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11142 		}
11143 		break;
11144 	default:
11145 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11146 		break;
11147 	}
11148 
11149 	return NULL;
11150 }
11151 
11152 /* callback for domain alloc/attach/free */
11153 static int32_t
11154 __ocs_hw_domain_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11155 {
11156 	ocs_domain_t	*domain = arg;
11157 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11158 	ocs_sm_event_t	evt;
11159 
11160 	if (status || hdr->status) {
11161 		ocs_log_debug(hw->os, "bad status vfi=%#x st=%x hdr=%x\n",
11162 			      domain->indicator, status, hdr->status);
11163 		evt = OCS_EVT_ERROR;
11164 	} else {
11165 		evt = OCS_EVT_RESPONSE;
11166 	}
11167 
11168 	ocs_sm_post_event(&domain->sm, evt, mqe);
11169 
11170 	return 0;
11171 }
11172 
11173 static int32_t
11174 target_wqe_timer_nop_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11175 {
11176 	ocs_hw_io_t *io = NULL;
11177 	ocs_hw_io_t *io_next = NULL;
11178 	uint64_t ticks_current = ocs_get_os_ticks();
11179 	uint32_t sec_elapsed;
11180 	ocs_hw_rtn_e rc;
11181 
11182 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11183 
11184 	if (status || hdr->status) {
11185 		ocs_log_debug(hw->os, "bad status st=%x hdr=%x\n",
11186 			      status, hdr->status);
11187 		/* go ahead and proceed with wqe timer checks... */
11188 	}
11189 
11190 	/* loop through active WQE list and check for timeouts */
11191 	ocs_lock(&hw->io_lock);
11192 	ocs_list_foreach_safe(&hw->io_timed_wqe, io, io_next) {
11193 		sec_elapsed = ((ticks_current - io->submit_ticks) / ocs_get_os_tick_freq());
11194 
11195 		/*
11196 		 * If elapsed time > timeout, abort it. No need to check type since
11197 		 * it wouldn't be on this list unless it was a target WQE
11198 		 */
11199 		if (sec_elapsed > io->tgt_wqe_timeout) {
11200 			ocs_log_test(hw->os, "IO timeout xri=0x%x tag=0x%x type=%d\n",
11201 				     io->indicator, io->reqtag, io->type);
11202 
11203 			/* remove from active_wqe list so won't try to abort again */
11204 			ocs_list_remove(&hw->io_timed_wqe, io);
11205 
11206 			/* save status of "timed out" for when abort completes */
11207 			io->status_saved = 1;
11208 			io->saved_status = SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT;
11209 			io->saved_ext = 0;
11210 			io->saved_len = 0;
11211 
11212 			/* now abort outstanding IO */
11213 			rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
11214 			if (rc) {
11215 				ocs_log_test(hw->os,
11216 					"abort failed xri=%#x tag=%#x rc=%d\n",
11217 					io->indicator, io->reqtag, rc);
11218 			}
11219 		}
11220 		/*
11221 		 * need to go through entire list since each IO could have a
11222 		 * different timeout value
11223 		 */
11224 	}
11225 	ocs_unlock(&hw->io_lock);
11226 
11227 	/* if we're not in the middle of shutting down, schedule next timer */
11228 	if (!hw->active_wqe_timer_shutdown) {
11229 		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw, OCS_HW_WQ_TIMER_PERIOD_MS);
11230 	}
11231 	hw->in_active_wqe_timer = FALSE;
11232 	return 0;
11233 }
11234 
11235 static void
11236 target_wqe_timer_cb(void *arg)
11237 {
11238 	ocs_hw_t *hw = (ocs_hw_t *)arg;
11239 
11240 	/* delete existing timer; will kick off new timer after checking wqe timeouts */
11241 	hw->in_active_wqe_timer = TRUE;
11242 	ocs_del_timer(&hw->wqe_timer);
11243 
11244 	/* Forward timer callback to execute in the mailbox completion processing context */
11245 	if (ocs_hw_async_call(hw, target_wqe_timer_nop_cb, hw)) {
11246 		ocs_log_test(hw->os, "ocs_hw_async_call failed\n");
11247 	}
11248 }
11249 
11250 static void
11251 shutdown_target_wqe_timer(ocs_hw_t *hw)
11252 {
11253 	uint32_t	iters = 100;
11254 
11255 	if (hw->config.emulate_tgt_wqe_timeout) {
11256 		/* request active wqe timer shutdown, then wait for it to complete */
11257 		hw->active_wqe_timer_shutdown = TRUE;
11258 
11259 		/* delete WQE timer and wait for timer handler to complete (if necessary) */
11260 		ocs_del_timer(&hw->wqe_timer);
11261 
11262 		/* now wait for timer handler to complete (if necessary) */
11263 		while (hw->in_active_wqe_timer && iters) {
11264 			/*
11265 			 * if we happen to have just sent NOP mailbox command, make sure
11266 			 * completions are being processed
11267 			 */
11268 			ocs_hw_flush(hw);
11269 			iters--;
11270 		}
11271 
11272 		if (iters == 0) {
11273 			ocs_log_test(hw->os, "Failed to shutdown active wqe timer\n");
11274 		}
11275 	}
11276 }
11277 
11278 /**
11279  * @brief Determine if HW IO is owned by the port.
11280  *
11281  * @par Description
11282  * Determines if the given HW IO has been posted to the chip.
11283  *
11284  * @param hw Hardware context allocated by the caller.
11285  * @param io HW IO.
11286  *
11287  * @return Returns TRUE if given HW IO is port-owned.
11288  */
11289 uint8_t
11290 ocs_hw_is_io_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
11291 {
11292 	/* Check to see if this is a port owned XRI */
11293 	return io->is_port_owned;
11294 }
11295 
11296 /**
11297  * @brief Return TRUE if exchange is port-owned.
11298  *
11299  * @par Description
11300  * Test to see if the xri is a port-owned xri.
11301  *
11302  * @param hw Hardware context.
11303  * @param xri Exchange indicator.
11304  *
11305  * @return Returns TRUE if XRI is a port owned XRI.
11306  */
11307 
11308 uint8_t
11309 ocs_hw_is_xri_port_owned(ocs_hw_t *hw, uint32_t xri)
11310 {
11311 	ocs_hw_io_t *io = ocs_hw_io_lookup(hw, xri);
11312 	return (io == NULL ? FALSE : io->is_port_owned);
11313 }
11314 
11315 /**
11316  * @brief Returns an XRI from the port owned list to the host.
11317  *
11318  * @par Description
11319  * Used when the POST_XRI command fails as well as when the RELEASE_XRI completes.
11320  *
11321  * @param hw Hardware context.
11322  * @param xri_base The starting XRI number.
11323  * @param xri_count The number of XRIs to free from the base.
11324  */
11325 static void
11326 ocs_hw_reclaim_xri(ocs_hw_t *hw, uint16_t xri_base, uint16_t xri_count)
11327 {
11328 	ocs_hw_io_t	*io;
11329 	uint32_t i;
11330 
11331 	for (i = 0; i < xri_count; i++) {
11332 		io = ocs_hw_io_lookup(hw, xri_base + i);
11333 
11334 		/*
11335 		 * if this is an auto xfer rdy XRI, then we need to release any
11336 		 * buffer attached to the XRI before moving the XRI back to the free pool.
11337 		 */
11338 		if (hw->auto_xfer_rdy_enabled) {
11339 			ocs_hw_rqpair_auto_xfer_rdy_move_to_host(hw, io);
11340 		}
11341 
11342 		ocs_lock(&hw->io_lock);
11343 			ocs_list_remove(&hw->io_port_owned, io);
11344 			io->is_port_owned = 0;
11345 			ocs_list_add_tail(&hw->io_free, io);
11346 		ocs_unlock(&hw->io_lock);
11347 	}
11348 }
11349 
11350 /**
11351  * @brief Called when the POST_XRI command completes.
11352  *
11353  * @par Description
11354  * Free the mailbox command buffer and reclaim the XRIs on failure.
11355  *
11356  * @param hw Hardware context.
11357  * @param status Status field from the mbox completion.
11358  * @param mqe Mailbox response structure.
11359  * @param arg Pointer to a callback function that signals the caller that the command is done.
11360  *
11361  * @return Returns 0.
11362  */
11363 static int32_t
11364 ocs_hw_cb_post_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11365 {
11366 	sli4_cmd_post_xri_t	*post_xri = (sli4_cmd_post_xri_t*)mqe;
11367 
11368 	/* Reclaim the XRIs as host owned if the command fails */
11369 	if (status != 0) {
11370 		ocs_log_debug(hw->os, "Status 0x%x for XRI base 0x%x, cnt =x%x\n",
11371 			      status, post_xri->xri_base, post_xri->xri_count);
11372 		ocs_hw_reclaim_xri(hw, post_xri->xri_base, post_xri->xri_count);
11373 	}
11374 
11375 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11376 	return 0;
11377 }
11378 
11379 /**
11380  * @brief Issues a mailbox command to move XRIs from the host-controlled pool to the port.
11381  *
11382  * @param hw Hardware context.
11383  * @param xri_start The starting XRI to post.
11384  * @param num_to_post The number of XRIs to post.
11385  *
11386  * @return Returns OCS_HW_RTN_NO_MEMORY, OCS_HW_RTN_ERROR, or OCS_HW_RTN_SUCCESS.
11387  */
11388 
11389 static ocs_hw_rtn_e
11390 ocs_hw_post_xri(ocs_hw_t *hw, uint32_t xri_start, uint32_t num_to_post)
11391 {
11392 	uint8_t	*post_xri;
11393 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11394 
11395 	/* Since we need to allocate for mailbox queue, just always allocate */
11396 	post_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11397 	if (post_xri == NULL) {
11398 		ocs_log_err(hw->os, "no buffer for command\n");
11399 		return OCS_HW_RTN_NO_MEMORY;
11400 	}
11401 
11402 	/* Register the XRIs */
11403 	if (sli_cmd_post_xri(&hw->sli, post_xri, SLI4_BMBX_SIZE,
11404 			     xri_start, num_to_post)) {
11405 		rc = ocs_hw_command(hw, post_xri, OCS_CMD_NOWAIT, ocs_hw_cb_post_xri, NULL);
11406 		if (rc != OCS_HW_RTN_SUCCESS) {
11407 			ocs_free(hw->os, post_xri, SLI4_BMBX_SIZE);
11408 			ocs_log_err(hw->os, "post_xri failed\n");
11409 		}
11410 	}
11411 	return rc;
11412 }
11413 
11414 /**
11415  * @brief Move XRIs from the host-controlled pool to the port.
11416  *
11417  * @par Description
11418  * Removes IOs from the free list and moves them to the port.
11419  *
11420  * @param hw Hardware context.
11421  * @param num_xri The number of XRIs being requested to move to the chip.
11422  *
11423  * @return Returns the number of XRIs that were moved.
11424  */
11425 
11426 uint32_t
11427 ocs_hw_xri_move_to_port_owned(ocs_hw_t *hw, uint32_t num_xri)
11428 {
11429 	ocs_hw_io_t	*io;
11430 	uint32_t i;
11431 	uint32_t num_posted = 0;
11432 
11433 	/*
11434 	 * Note: We cannot use ocs_hw_io_alloc() because that would place the
11435 	 *       IO on the io_inuse list. We need to move from the io_free to
11436 	 *       the io_port_owned list.
11437 	 */
11438 	ocs_lock(&hw->io_lock);
11439 
11440 	for (i = 0; i < num_xri; i++) {
11441 		if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
11442 			ocs_hw_rtn_e rc;
11443 
11444 			/*
11445 			 * if this is an auto xfer rdy XRI, then we need to attach a
11446 			 * buffer to the XRI before submitting it to the chip. If a
11447 			 * buffer is unavailable, then we cannot post it, so return it
11448 			 * to the free pool.
11449 			 */
11450 			if (hw->auto_xfer_rdy_enabled) {
11451 				/* Note: uses the IO lock to get the auto xfer rdy buffer */
11452 				ocs_unlock(&hw->io_lock);
11453 				rc = ocs_hw_rqpair_auto_xfer_rdy_move_to_port(hw, io);
11454 				ocs_lock(&hw->io_lock);
11455 				if (rc != OCS_HW_RTN_SUCCESS) {
11456 					ocs_list_add_head(&hw->io_free, io);
11457 					break;
11458 				}
11459 			}
11460 			ocs_lock_init(hw->os, &io->axr_lock, "HW_axr_lock[%d]", io->indicator);
11461 			io->is_port_owned = 1;
11462 			ocs_list_add_tail(&hw->io_port_owned, io);
11463 
11464 			/* Post XRI */
11465 			if (ocs_hw_post_xri(hw, io->indicator, 1) != OCS_HW_RTN_SUCCESS ) {
11466 				ocs_hw_reclaim_xri(hw, io->indicator, i);
11467 				break;
11468 			}
11469 			num_posted++;
11470 		} else {
11471 			/* no more free XRIs */
11472 			break;
11473 		}
11474 	}
11475 	ocs_unlock(&hw->io_lock);
11476 
11477 	return num_posted;
11478 }
11479 
11480 /**
11481  * @brief Called when the RELEASE_XRI command completes.
11482  *
11483  * @par Description
11484  * Move the IOs back to the free pool on success.
11485  *
11486  * @param hw Hardware context.
11487  * @param status Status field from the mbox completion.
11488  * @param mqe Mailbox response structure.
11489  * @param arg Pointer to a callback function that signals the caller that the command is done.
11490  *
11491  * @return Returns 0.
11492  */
11493 static int32_t
11494 ocs_hw_cb_release_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11495 {
11496 	sli4_cmd_release_xri_t	*release_xri = (sli4_cmd_release_xri_t*)mqe;
11497 	uint8_t i;
11498 
11499 	/* Reclaim the XRIs as host owned if the command fails */
11500 	if (status != 0) {
11501 		ocs_log_err(hw->os, "Status 0x%x\n", status);
11502 	} else {
11503 		for (i = 0; i < release_xri->released_xri_count; i++) {
11504 			uint16_t xri = ((i & 1) == 0 ? release_xri->xri_tbl[i/2].xri_tag0 :
11505 					release_xri->xri_tbl[i/2].xri_tag1);
11506 			ocs_hw_reclaim_xri(hw, xri, 1);
11507 		}
11508 	}
11509 
11510 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11511 	return 0;
11512 }
11513 
11514 /**
11515  * @brief Move XRIs from the port-controlled pool to the host.
11516  *
11517  * Requests XRIs from the FW to return to the host-owned pool.
11518  *
11519  * @param hw Hardware context.
11520  * @param num_xri The number of XRIs being requested to moved from the chip.
11521  *
11522  * @return Returns 0 for success, or a negative error code value for failure.
11523  */
11524 
11525 ocs_hw_rtn_e
11526 ocs_hw_xri_move_to_host_owned(ocs_hw_t *hw, uint8_t num_xri)
11527 {
11528 	uint8_t	*release_xri;
11529 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11530 
11531 	/* non-local buffer required for mailbox queue */
11532 	release_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11533 	if (release_xri == NULL) {
11534 		ocs_log_err(hw->os, "no buffer for command\n");
11535 		return OCS_HW_RTN_NO_MEMORY;
11536 	}
11537 
11538 	/* release the XRIs */
11539 	if (sli_cmd_release_xri(&hw->sli, release_xri, SLI4_BMBX_SIZE, num_xri)) {
11540 		rc = ocs_hw_command(hw, release_xri, OCS_CMD_NOWAIT, ocs_hw_cb_release_xri, NULL);
11541 		if (rc != OCS_HW_RTN_SUCCESS) {
11542 			ocs_log_err(hw->os, "release_xri failed\n");
11543 		}
11544 	}
11545 	/* If we are polling or an error occurred, then free the mailbox buffer */
11546 	if (release_xri != NULL && rc != OCS_HW_RTN_SUCCESS) {
11547 		ocs_free(hw->os, release_xri, SLI4_BMBX_SIZE);
11548 	}
11549 	return rc;
11550 }
11551 
11552 /**
11553  * @brief Allocate an ocs_hw_rx_buffer_t array.
11554  *
11555  * @par Description
11556  * An ocs_hw_rx_buffer_t array is allocated, along with the required DMA memory.
11557  *
11558  * @param hw Pointer to HW object.
11559  * @param rqindex RQ index for this buffer.
11560  * @param count Count of buffers in array.
11561  * @param size Size of buffer.
11562  *
11563  * @return Returns the pointer to the allocated ocs_hw_rq_buffer_t array.
11564  */
11565 static ocs_hw_rq_buffer_t *
11566 ocs_hw_rx_buffer_alloc(ocs_hw_t *hw, uint32_t rqindex, uint32_t count, uint32_t size)
11567 {
11568 	ocs_t *ocs = hw->os;
11569 	ocs_hw_rq_buffer_t *rq_buf = NULL;
11570 	ocs_hw_rq_buffer_t *prq;
11571 	uint32_t i;
11572 
11573 	if (count != 0) {
11574 		rq_buf = ocs_malloc(hw->os, sizeof(*rq_buf) * count, OCS_M_NOWAIT | OCS_M_ZERO);
11575 		if (rq_buf == NULL) {
11576 			ocs_log_err(hw->os, "Failure to allocate unsolicited DMA trackers\n");
11577 			return NULL;
11578 		}
11579 
11580 		for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
11581 			prq->rqindex = rqindex;
11582 			if (ocs_dma_alloc(ocs, &prq->dma, size, OCS_MIN_DMA_ALIGNMENT)) {
11583 				ocs_log_err(hw->os, "DMA allocation failed\n");
11584 				ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11585 				rq_buf = NULL;
11586 				break;
11587 			}
11588 		}
11589 	}
11590 	return rq_buf;
11591 }
11592 
11593 /**
11594  * @brief Free an ocs_hw_rx_buffer_t array.
11595  *
11596  * @par Description
11597  * The ocs_hw_rx_buffer_t array is freed, along with allocated DMA memory.
11598  *
11599  * @param hw Pointer to HW object.
11600  * @param rq_buf Pointer to ocs_hw_rx_buffer_t array.
11601  * @param count Count of buffers in array.
11602  *
11603  * @return None.
11604  */
11605 static void
11606 ocs_hw_rx_buffer_free(ocs_hw_t *hw, ocs_hw_rq_buffer_t *rq_buf, uint32_t count)
11607 {
11608 	ocs_t *ocs = hw->os;
11609 	uint32_t i;
11610 	ocs_hw_rq_buffer_t *prq;
11611 
11612 	if (rq_buf != NULL) {
11613 		for (i = 0, prq = rq_buf; i < count; i++, prq++) {
11614 			ocs_dma_free(ocs, &prq->dma);
11615 		}
11616 		ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11617 	}
11618 }
11619 
11620 /**
11621  * @brief Allocate the RQ data buffers.
11622  *
11623  * @param hw Pointer to HW object.
11624  *
11625  * @return Returns 0 on success, or a non-zero value on failure.
11626  */
11627 ocs_hw_rtn_e
11628 ocs_hw_rx_allocate(ocs_hw_t *hw)
11629 {
11630 	ocs_t *ocs = hw->os;
11631 	uint32_t i;
11632 	int32_t rc = OCS_HW_RTN_SUCCESS;
11633 	uint32_t rqindex = 0;
11634 	hw_rq_t *rq;
11635 	uint32_t hdr_size = OCS_HW_RQ_SIZE_HDR;
11636 	uint32_t payload_size = hw->config.rq_default_buffer_size;
11637 
11638 	rqindex = 0;
11639 
11640 	for (i = 0; i < hw->hw_rq_count; i++) {
11641 		rq = hw->hw_rq[i];
11642 
11643 		/* Allocate header buffers */
11644 		rq->hdr_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, hdr_size);
11645 		if (rq->hdr_buf == NULL) {
11646 			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc hdr_buf failed\n");
11647 			rc = OCS_HW_RTN_ERROR;
11648 			break;
11649 		}
11650 
11651 		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d header  %4d by %4d bytes\n", i, rq->hdr->id,
11652 			      rq->entry_count, hdr_size);
11653 
11654 		rqindex++;
11655 
11656 		/* Allocate payload buffers */
11657 		rq->payload_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, payload_size);
11658 		if (rq->payload_buf == NULL) {
11659 			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc fb_buf failed\n");
11660 			rc = OCS_HW_RTN_ERROR;
11661 			break;
11662 		}
11663 		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d default %4d by %4d bytes\n", i, rq->data->id,
11664 			      rq->entry_count, payload_size);
11665 		rqindex++;
11666 	}
11667 
11668 	return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
11669 }
11670 
11671 /**
11672  * @brief Post the RQ data buffers to the chip.
11673  *
11674  * @param hw Pointer to HW object.
11675  *
11676  * @return Returns 0 on success, or a non-zero value on failure.
11677  */
11678 ocs_hw_rtn_e
11679 ocs_hw_rx_post(ocs_hw_t *hw)
11680 {
11681 	uint32_t i;
11682 	uint32_t idx;
11683 	uint32_t rq_idx;
11684 	int32_t rc = 0;
11685 
11686 	/*
11687 	 * In RQ pair mode, we MUST post the header and payload buffer at the
11688 	 * same time.
11689 	 */
11690 	for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
11691 		hw_rq_t *rq = hw->hw_rq[rq_idx];
11692 
11693 		for (i = 0; i < rq->entry_count-1; i++) {
11694 			ocs_hw_sequence_t *seq = ocs_array_get(hw->seq_pool, idx++);
11695 			ocs_hw_assert(seq != NULL);
11696 
11697 			seq->header = &rq->hdr_buf[i];
11698 
11699 			seq->payload = &rq->payload_buf[i];
11700 
11701 			rc = ocs_hw_sequence_free(hw, seq);
11702 			if (rc) {
11703 				break;
11704 			}
11705 		}
11706 		if (rc) {
11707 			break;
11708 		}
11709 	}
11710 
11711 	return rc;
11712 }
11713 
11714 /**
11715  * @brief Free the RQ data buffers.
11716  *
11717  * @param hw Pointer to HW object.
11718  *
11719  */
11720 void
11721 ocs_hw_rx_free(ocs_hw_t *hw)
11722 {
11723 	hw_rq_t *rq;
11724 	uint32_t i;
11725 
11726 	/* Free hw_rq buffers */
11727 	for (i = 0; i < hw->hw_rq_count; i++) {
11728 		rq = hw->hw_rq[i];
11729 		if (rq != NULL) {
11730 			ocs_hw_rx_buffer_free(hw, rq->hdr_buf, rq->entry_count);
11731 			rq->hdr_buf = NULL;
11732 			ocs_hw_rx_buffer_free(hw, rq->payload_buf, rq->entry_count);
11733 			rq->payload_buf = NULL;
11734 		}
11735 	}
11736 }
11737 
11738 /**
11739  * @brief HW async call context structure.
11740  */
11741 typedef struct {
11742 	ocs_hw_async_cb_t callback;
11743 	void *arg;
11744 	uint8_t cmd[SLI4_BMBX_SIZE];
11745 } ocs_hw_async_call_ctx_t;
11746 
11747 /**
11748  * @brief HW async callback handler
11749  *
11750  * @par Description
11751  * This function is called when the NOP mailbox command completes.  The callback stored
11752  * in the requesting context is invoked.
11753  *
11754  * @param hw Pointer to HW object.
11755  * @param status Completion status.
11756  * @param mqe Pointer to mailbox completion queue entry.
11757  * @param arg Caller-provided argument.
11758  *
11759  * @return None.
11760  */
11761 static void
11762 ocs_hw_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11763 {
11764 	ocs_hw_async_call_ctx_t *ctx = arg;
11765 
11766 	if (ctx != NULL) {
11767 		if (ctx->callback != NULL) {
11768 			(*ctx->callback)(hw, status, mqe, ctx->arg);
11769 		}
11770 		ocs_free(hw->os, ctx, sizeof(*ctx));
11771 	}
11772 }
11773 
11774 /**
11775  * @brief Make an async callback using NOP mailbox command
11776  *
11777  * @par Description
11778  * Post a NOP mailbox command; the callback with argument is invoked upon completion
11779  * while in the event processing context.
11780  *
11781  * @param hw Pointer to HW object.
11782  * @param callback Pointer to callback function.
11783  * @param arg Caller-provided callback.
11784  *
11785  * @return Returns 0 on success, or a negative error code value on failure.
11786  */
11787 int32_t
11788 ocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
11789 {
11790 	ocs_hw_async_call_ctx_t *ctx;
11791 
11792 	/*
11793 	 * Allocate a callback context (which includes the mailbox command buffer), we need
11794 	 * this to be persistent as the mailbox command submission may be queued and executed later
11795 	 * execution.
11796 	 */
11797 	ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
11798 	if (ctx == NULL) {
11799 		ocs_log_err(hw->os, "failed to malloc async call context\n");
11800 		return OCS_HW_RTN_NO_MEMORY;
11801 	}
11802 	ctx->callback = callback;
11803 	ctx->arg = arg;
11804 
11805 	/* Build and send a NOP mailbox command */
11806 	if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
11807 		ocs_log_err(hw->os, "COMMON_NOP format failure\n");
11808 		ocs_free(hw->os, ctx, sizeof(*ctx));
11809 		return OCS_HW_RTN_ERROR;
11810 	}
11811 
11812 	if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
11813 		ocs_log_err(hw->os, "COMMON_NOP command failure\n");
11814 		ocs_free(hw->os, ctx, sizeof(*ctx));
11815 		return OCS_HW_RTN_ERROR;
11816 	}
11817 	return OCS_HW_RTN_SUCCESS;
11818 }
11819 
11820 /**
11821  * @brief Initialize the reqtag pool.
11822  *
11823  * @par Description
11824  * The WQ request tag pool is initialized.
11825  *
11826  * @param hw Pointer to HW object.
11827  *
11828  * @return Returns 0 on success, or a negative error code value on failure.
11829  */
11830 ocs_hw_rtn_e
11831 ocs_hw_reqtag_init(ocs_hw_t *hw)
11832 {
11833 	if (hw->wq_reqtag_pool == NULL) {
11834 		hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
11835 		if (hw->wq_reqtag_pool == NULL) {
11836 			ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
11837 			return OCS_HW_RTN_NO_MEMORY;
11838 		}
11839 	}
11840 	ocs_hw_reqtag_reset(hw);
11841 	return OCS_HW_RTN_SUCCESS;
11842 }
11843 
11844 /**
11845  * @brief Allocate a WQ request tag.
11846  *
11847  * Allocate and populate a WQ request tag from the WQ request tag pool.
11848  *
11849  * @param hw Pointer to HW object.
11850  * @param callback Callback function.
11851  * @param arg Pointer to callback argument.
11852  *
11853  * @return Returns pointer to allocated WQ request tag, or NULL if object cannot be allocated.
11854  */
11855 hw_wq_callback_t *
11856 ocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
11857 {
11858 	hw_wq_callback_t *wqcb;
11859 
11860 	ocs_hw_assert(callback != NULL);
11861 
11862 	wqcb = ocs_pool_get(hw->wq_reqtag_pool);
11863 	if (wqcb != NULL) {
11864 		ocs_hw_assert(wqcb->callback == NULL);
11865 		wqcb->callback = callback;
11866 		wqcb->arg = arg;
11867 	}
11868 	return wqcb;
11869 }
11870 
11871 /**
11872  * @brief Free a WQ request tag.
11873  *
11874  * Free the passed in WQ request tag.
11875  *
11876  * @param hw Pointer to HW object.
11877  * @param wqcb Pointer to WQ request tag object to free.
11878  *
11879  * @return None.
11880  */
11881 void
11882 ocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
11883 {
11884 	ocs_hw_assert(wqcb->callback != NULL);
11885 	wqcb->callback = NULL;
11886 	wqcb->arg = NULL;
11887 	ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11888 }
11889 
11890 /**
11891  * @brief Return WQ request tag by index.
11892  *
11893  * @par Description
11894  * Return pointer to WQ request tag object given an index.
11895  *
11896  * @param hw Pointer to HW object.
11897  * @param instance_index Index of WQ request tag to return.
11898  *
11899  * @return Pointer to WQ request tag, or NULL.
11900  */
11901 hw_wq_callback_t *
11902 ocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
11903 {
11904 	hw_wq_callback_t *wqcb;
11905 
11906 	wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
11907 	if (wqcb == NULL) {
11908 		ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
11909 	}
11910 	return wqcb;
11911 }
11912 
11913 /**
11914  * @brief Reset the WQ request tag pool.
11915  *
11916  * @par Description
11917  * Reset the WQ request tag pool, returning all to the free list.
11918  *
11919  * @param hw pointer to HW object.
11920  *
11921  * @return None.
11922  */
11923 void
11924 ocs_hw_reqtag_reset(ocs_hw_t *hw)
11925 {
11926 	hw_wq_callback_t *wqcb;
11927 	uint32_t i;
11928 
11929 	/* Remove all from freelist */
11930 	while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
11931 		;
11932 	}
11933 
11934 	/* Put them all back */
11935 	for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
11936 		wqcb->instance_index = i;
11937 		wqcb->callback = NULL;
11938 		wqcb->arg = NULL;
11939 		ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11940 	}
11941 }
11942 
11943 /**
11944  * @brief Handle HW assertion
11945  *
11946  * HW assert, display diagnostic message, and abort.
11947  *
11948  * @param cond string describing failing assertion condition
11949  * @param filename file name
11950  * @param linenum line number
11951  *
11952  * @return none
11953  */
11954 void
11955 _ocs_hw_assert(const char *cond, const char *filename, int linenum)
11956 {
11957 	ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
11958 	ocs_abort();
11959 		/* no return */
11960 }
11961 
11962 /**
11963  * @brief Handle HW verify
11964  *
11965  * HW verify, display diagnostic message, dump stack and return.
11966  *
11967  * @param cond string describing failing verify condition
11968  * @param filename file name
11969  * @param linenum line number
11970  *
11971  * @return none
11972  */
11973 void
11974 _ocs_hw_verify(const char *cond, const char *filename, int linenum)
11975 {
11976 	ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
11977 	ocs_print_stack();
11978 }
11979 
11980 /**
11981  * @brief Reque XRI
11982  *
11983  * @par Description
11984  * Reque XRI
11985  *
11986  * @param hw Pointer to HW object.
11987  * @param io Pointer to HW IO
11988  *
11989  * @return Return 0 if successful else returns -1
11990  */
11991 int32_t
11992 ocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
11993 {
11994 	int32_t rc = 0;
11995 
11996 	rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
11997 	if (rc) {
11998 		ocs_list_add_tail(&hw->io_port_dnrx, io);
11999 		rc = -1;
12000 		goto exit_ocs_hw_reque_xri;
12001 	}
12002 
12003 	io->auto_xfer_rdy_dnrx = 0;
12004 	io->type = OCS_HW_IO_DNRX_REQUEUE;
12005 	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)) {
12006 		/* Clear buffer from XRI */
12007 		ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
12008 		io->axr_buf = NULL;
12009 
12010 		ocs_log_err(hw->os, "requeue_xri WQE error\n");
12011 		ocs_list_add_tail(&hw->io_port_dnrx, io);
12012 
12013 		rc = -1;
12014 		goto exit_ocs_hw_reque_xri;
12015 	}
12016 
12017 	if (io->wq == NULL) {
12018 		io->wq = ocs_hw_queue_next_wq(hw, io);
12019 		ocs_hw_assert(io->wq != NULL);
12020 	}
12021 
12022 	/*
12023 	 * Add IO to active io wqe list before submitting, in case the
12024 	 * wcqe processing preempts this thread.
12025 	 */
12026 	OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
12027 	OCS_STAT(io->wq->use_count++);
12028 
12029 	rc = hw_wq_write(io->wq, &io->wqe);
12030 	if (rc < 0) {
12031 		ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
12032 		rc = -1;
12033 	}
12034 
12035 exit_ocs_hw_reque_xri:
12036 	return 0;
12037 }
12038 
12039 uint32_t
12040 ocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
12041 {
12042 	sli4_t *sli4 = &ocs->hw.sli;
12043 	ocs_dma_t       dma;
12044 	uint8_t		*payload = NULL;
12045 
12046 	int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
12047 
12048 	/* allocate memory for the service parameters */
12049 	if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
12050 		ocs_log_err(ocs, "Failed to allocate DMA memory\n");
12051 		return 1;
12052 	}
12053 
12054 	if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
12055 				&dma, indicator)) {
12056 		ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
12057 		ocs_dma_free(ocs, &dma);
12058 		return 1;
12059 	}
12060 
12061 	if (sli_bmbx_command(sli4)) {
12062 		ocs_log_err(ocs, "READ_SPARM64 command failure\n");
12063 		ocs_dma_free(ocs, &dma);
12064 		return 1;
12065 	}
12066 
12067 	payload = dma.virt;
12068 	ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
12069 	ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
12070 	ocs_dma_free(ocs, &dma);
12071 	return 0;
12072 }
12073 
12074 uint32_t
12075 ocs_hw_get_config_persistent_topology(ocs_hw_t *hw)
12076 {
12077         uint32_t topology = OCS_HW_TOPOLOGY_AUTO;
12078 	sli4_t *sli = &hw->sli;
12079 
12080         if (!sli_persist_topology_enabled(sli))
12081                 return topology;
12082 
12083         switch (sli->config.pt) {
12084                 case SLI4_INIT_LINK_F_P2P_ONLY:
12085                         topology = OCS_HW_TOPOLOGY_NPORT;
12086                         break;
12087                 case SLI4_INIT_LINK_F_FCAL_ONLY:
12088                         topology = OCS_HW_TOPOLOGY_LOOP;
12089                         break;
12090                 default:
12091                         break;
12092         }
12093 
12094         return topology;
12095 }
12096 
12097 /*
12098  * @brief Persistent topology configuration callback argument.
12099  */
12100 typedef struct ocs_hw_persistent_topo_cb_arg {
12101 	ocs_sem_t semaphore;
12102 	int32_t status;
12103 } ocs_hw_persistent_topo_cb_arg_t;
12104 
12105 /*
12106  * @brief Called after the completion of set persistent topology request
12107  *
12108  * @par Description
12109  * This is callback fn for the set_persistent_topology
12110  * function. This callback is called when the common feature mbx cmd
12111  * completes.
12112  *
12113  * @param hw Hardware context.
12114  * @param status The status from the MQE.
12115  * @param mqe Pointer to mailbox command buffer.
12116  * @param arg Pointer to a callback argument.
12117  *
12118  * @return 0 on success, non-zero otherwise
12119  */
12120 static int32_t
12121 ocs_hw_set_persistent_topolgy_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
12122 {
12123 	ocs_hw_persistent_topo_cb_arg_t *req = (ocs_hw_persistent_topo_cb_arg_t *)arg;
12124 
12125 	req->status = status;
12126 
12127 	ocs_sem_v(&req->semaphore);
12128 
12129 	return 0;
12130 }
12131 
12132 /**
12133  * @brief Set persistent topology
12134  *
12135  * Sets the persistent topology(PT) feature using
12136  * COMMON_SET_FEATURES cmd. If mbx cmd succeeds, update the
12137  * topology into sli config. PT stores the value to be set into link_flags
12138  * of the cmd INIT_LINK, to bring up the link.
12139  *
12140  * SLI specs defines following for PT:
12141  *     When TF is set to 0:
12142  *       0 Reserved
12143  *       1 Attempt point-to-point initialization (direct attach or Fabric topology).
12144  *       2 Attempt FC-AL loop initialization.
12145  *       3 Reserved
12146  *
12147  *      When TF is set to 1:
12148  *       0 Attempt FC-AL loop initialization; if it fails, attempt point-to-point initialization.
12149  *       1 Attempt point-to-point initialization; if it fails, attempt FC-AL loop initialization.
12150  *       2 Reserved
12151  *      3 Reserved
12152  *
12153  *     Note: Topology failover is only available on Lancer G5. This command will fail
12154  *     if TF is set to 1 on any other ASICs
12155  *
12156  * @param hw Pointer to hw
12157  * @param topology topology value to be set, provided through
12158  *        elxsdkutil set-topology cmd
12159  *
12160  * @return Returns 0 on success, or a non-zero value on failure.
12161  */
12162 ocs_hw_rtn_e
12163 ocs_hw_set_persistent_topology(ocs_hw_t *hw, uint32_t topology, uint32_t opts)
12164 {
12165 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
12166 	uint8_t buf[SLI4_BMBX_SIZE];
12167 	sli4_req_common_set_features_persistent_topo_param_t param;
12168 	ocs_hw_persistent_topo_cb_arg_t request;
12169 
12170 	ocs_memset(&param, 0, sizeof(param));
12171 	param.persistent_topo = topology;
12172 
12173 	switch (topology) {
12174 	case OCS_HW_TOPOLOGY_AUTO:
12175 		if (sli_get_asic_type(&hw->sli) == SLI4_ASIC_TYPE_LANCER) {
12176 			param.persistent_topo = SLI4_INIT_LINK_F_P2P_FAIL_OVER;
12177 			param.topo_failover = 1;
12178 		} else {
12179 			param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;;
12180 			param.topo_failover = 0;
12181 		}
12182 		break;
12183 
12184 	case OCS_HW_TOPOLOGY_NPORT:
12185 		param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;
12186 		param.topo_failover = 0;
12187 		break;
12188 
12189 	case OCS_HW_TOPOLOGY_LOOP:
12190 		param.persistent_topo = SLI4_INIT_LINK_F_FCAL_ONLY;
12191 		param.topo_failover = 0;
12192 		break;
12193 
12194 	default:
12195 		ocs_log_err(hw->os, "unsupported topology %#x\n", topology);
12196 		return -1;
12197 	}
12198 
12199 	ocs_sem_init(&request.semaphore, 0, "set_persistent_topo");
12200 
12201 	/* build the set_features command */
12202 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
12203 		SLI4_SET_FEATURES_PERSISTENT_TOPOLOGY, sizeof(param), &param);
12204 
12205 	if (opts == OCS_CMD_POLL) {
12206 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
12207 		if (rc) {
12208 			ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12209 			return rc;
12210 		}
12211 	} else {
12212 
12213 		// there's no response for this feature command
12214 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_set_persistent_topolgy_cb, &request);
12215 		if (rc) {
12216 			ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12217 			return rc;
12218 		}
12219 
12220 		if (ocs_sem_p(&request.semaphore, OCS_SEM_FOREVER)) {
12221 			ocs_log_err(hw->os, "ocs_sem_p failed\n");
12222 			return -ENXIO;
12223 		}
12224 
12225 		if (request.status) {
12226 			ocs_log_err(hw->os, "set persistent topology failed; status: %d\n", request.status);
12227 			return -EFAULT;
12228 		}
12229 	}
12230 
12231 	sli_config_persistent_topology(&hw->sli, &param);
12232 
12233 	return rc;
12234 }
12235 
12236 /**
12237  * @page fc_hw_api_overview HW APIs
12238  * - @ref devInitShutdown
12239  * - @ref domain
12240  * - @ref port
12241  * - @ref node
12242  * - @ref io
12243  * - @ref interrupt
12244  *
12245  * <div class="overview">
12246  * The Hardware Abstraction Layer (HW) insulates the higher-level code from the SLI-4
12247  * message details, but the higher level code must still manage domains, ports,
12248  * IT nexuses, and IOs. The HW API is designed to help the higher level manage
12249  * these objects.<br><br>
12250  *
12251  * The HW uses function callbacks to notify the higher-level code of events
12252  * that are received from the chip. There are currently three types of
12253  * functions that may be registered:
12254  *
12255  * <ul><li>domain – This function is called whenever a domain event is generated
12256  * within the HW. Examples include a new FCF is discovered, a connection
12257  * to a domain is disrupted, and allocation callbacks.</li>
12258  * <li>unsolicited – This function is called whenever new data is received in
12259  * the SLI-4 receive queue.</li>
12260  * <li>rnode – This function is called for remote node events, such as attach status
12261  * and  allocation callbacks.</li></ul>
12262  *
12263  * Upper layer functions may be registered by using the ocs_hw_callback() function.
12264  *
12265  * <img src="elx_fc_hw.jpg" alt="FC/FCoE HW" title="FC/FCoE HW" align="right"/>
12266  * <h2>FC/FCoE HW API</h2>
12267  * The FC/FCoE HW component builds upon the SLI-4 component to establish a flexible
12268  * interface for creating the necessary common objects and sending I/Os. It may be used
12269  * “as is” in customer implementations or it can serve as an example of typical interactions
12270  * between a driver and the SLI-4 hardware. The broad categories of functionality include:
12271  *
12272  * <ul><li>Setting-up and tearing-down of the HW.</li>
12273  * <li>Allocating and using the common objects (SLI Port, domain, remote node).</li>
12274  * <li>Sending and receiving I/Os.</li></ul>
12275  *
12276  * <h3>HW Setup</h3>
12277  * To set up the HW:
12278  *
12279  * <ol>
12280  * <li>Set up the HW object using ocs_hw_setup().<br>
12281  * This step performs a basic configuration of the SLI-4 component and the HW to
12282  * enable querying the hardware for its capabilities. At this stage, the HW is not
12283  * capable of general operations (such as, receiving events or sending I/Os).</li><br><br>
12284  * <li>Configure the HW according to the driver requirements.<br>
12285  * The HW provides functions to discover hardware capabilities (ocs_hw_get()), as
12286  * well as configures the amount of resources required (ocs_hw_set()). The driver
12287  * must also register callback functions (ocs_hw_callback()) to receive notification of
12288  * various asynchronous events.<br><br>
12289  * @b Note: Once configured, the driver must initialize the HW (ocs_hw_init()). This
12290  * step creates the underlying queues, commits resources to the hardware, and
12291  * prepares the hardware for operation. While the hardware is operational, the
12292  * port is not online, and cannot send or receive data.</li><br><br>
12293  * <br><br>
12294  * <li>Finally, the driver can bring the port online (ocs_hw_port_control()).<br>
12295  * When the link comes up, the HW determines if a domain is present and notifies the
12296  * driver using the domain callback function. This is the starting point of the driver's
12297  * interaction with the common objects.<br><br>
12298  * @b Note: For FCoE, there may be more than one domain available and, therefore,
12299  * more than one callback.</li>
12300  * </ol>
12301  *
12302  * <h3>Allocating and Using Common Objects</h3>
12303  * Common objects provide a mechanism through which the various OneCore Storage
12304  * driver components share and track information. These data structures are primarily
12305  * used to track SLI component information but can be extended by other components, if
12306  * needed. The main objects are:
12307  *
12308  * <ul><li>DMA – the ocs_dma_t object describes a memory region suitable for direct
12309  * memory access (DMA) transactions.</li>
12310  * <li>SCSI domain – the ocs_domain_t object represents the SCSI domain, including
12311  * any infrastructure devices such as FC switches and FC forwarders. The domain
12312  * object contains both an FCFI and a VFI.</li>
12313  * <li>SLI Port (sport) – the ocs_sli_port_t object represents the connection between
12314  * the driver and the SCSI domain. The SLI Port object contains a VPI.</li>
12315  * <li>Remote node – the ocs_remote_node_t represents a connection between the SLI
12316  * Port and another device in the SCSI domain. The node object contains an RPI.</li></ul>
12317  *
12318  * Before the driver can send I/Os, it must allocate the SCSI domain, SLI Port, and remote
12319  * node common objects and establish the connections between them. The goal is to
12320  * connect the driver to the SCSI domain to exchange I/Os with other devices. These
12321  * common object connections are shown in the following figure, FC Driver Common Objects:
12322  * <img src="elx_fc_common_objects.jpg"
12323  * alt="FC Driver Common Objects" title="FC Driver Common Objects" align="center"/>
12324  *
12325  * The first step is to create a connection to the domain by allocating an SLI Port object.
12326  * The SLI Port object represents a particular FC ID and must be initialized with one. With
12327  * the SLI Port object, the driver can discover the available SCSI domain(s). On identifying
12328  * a domain, the driver allocates a domain object and attaches to it using the previous SLI
12329  * port object.<br><br>
12330  *
12331  * @b Note: In some cases, the driver may need to negotiate service parameters (that is,
12332  * FLOGI) with the domain before attaching.<br><br>
12333  *
12334  * Once attached to the domain, the driver can discover and attach to other devices
12335  * (remote nodes). The exact discovery method depends on the driver, but it typically
12336  * includes using a position map, querying the fabric name server, or an out-of-band
12337  * method. In most cases, it is necessary to log in with devices before performing I/Os.
12338  * Prior to sending login-related ELS commands (ocs_hw_srrs_send()), the driver must
12339  * allocate a remote node object (ocs_hw_node_alloc()). If the login negotiation is
12340  * successful, the driver must attach the nodes (ocs_hw_node_attach()) to the SLI Port
12341  * before exchanging FCP I/O.<br><br>
12342  *
12343  * @b Note: The HW manages both the well known fabric address and the name server as
12344  * nodes in the domain. Therefore, the driver must allocate node objects prior to
12345  * communicating with either of these entities.
12346  *
12347  * <h3>Sending and Receiving I/Os</h3>
12348  * The HW provides separate interfaces for sending BLS/ ELS/ FC-CT and FCP, but the
12349  * commands are conceptually similar. Since the commands complete asynchronously,
12350  * the caller must provide a HW I/O object that maintains the I/O state, as well as
12351  * provide a callback function. The driver may use the same callback function for all I/O
12352  * operations, but each operation must use a unique HW I/O object. In the SLI-4
12353  * architecture, there is a direct association between the HW I/O object and the SGL used
12354  * to describe the data. Therefore, a driver typically performs the following operations:
12355  *
12356  * <ul><li>Allocates a HW I/O object (ocs_hw_io_alloc()).</li>
12357  * <li>Formats the SGL, specifying both the HW I/O object and the SGL.
12358  * (ocs_hw_io_init_sges() and ocs_hw_io_add_sge()).</li>
12359  * <li>Sends the HW I/O (ocs_hw_io_send()).</li></ul>
12360  *
12361  * <h3>HW Tear Down</h3>
12362  * To tear-down the HW:
12363  *
12364  * <ol><li>Take the port offline (ocs_hw_port_control()) to prevent receiving further
12365  * data andevents.</li>
12366  * <li>Destroy the HW object (ocs_hw_teardown()).</li>
12367  * <li>Free any memory used by the HW, such as buffers for unsolicited data.</li></ol>
12368  * <br>
12369  * </div><!-- overview -->
12370  *
12371  */
12372 
12373 /**
12374  * This contains all hw runtime workaround code.  Based on the asic type,
12375  * asic revision, and range of fw revisions, a particular workaround may be enabled.
12376  *
12377  * A workaround may consist of overriding a particular HW/SLI4 value that was initialized
12378  * during ocs_hw_setup() (for example the MAX_QUEUE overrides for mis-reported queue
12379  * sizes). Or if required, elements of the ocs_hw_workaround_t structure may be set to
12380  * control specific runtime behavior.
12381  *
12382  * It is intended that the controls in ocs_hw_workaround_t be defined functionally.  So we
12383  * would have the driver look like:  "if (hw->workaround.enable_xxx) then ...", rather than
12384  * what we might previously see as "if this is a BE3, then do xxx"
12385  *
12386  */
12387 
12388 #define HW_FWREV_ZERO		(0ull)
12389 #define HW_FWREV_MAX		(~0ull)
12390 
12391 #define SLI4_ASIC_TYPE_ANY	0
12392 #define SLI4_ASIC_REV_ANY	0
12393 
12394 /**
12395  * @brief Internal definition of workarounds
12396  */
12397 
12398 typedef enum {
12399 	HW_WORKAROUND_TEST = 1,
12400 	HW_WORKAROUND_MAX_QUEUE,	/**< Limits all queues */
12401 	HW_WORKAROUND_MAX_RQ,		/**< Limits only the RQ */
12402 	HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
12403 	HW_WORKAROUND_WQE_COUNT_METHOD,
12404 	HW_WORKAROUND_RQE_COUNT_METHOD,
12405 	HW_WORKAROUND_USE_UNREGISTERD_RPI,
12406 	HW_WORKAROUND_DISABLE_AR_TGT_DIF, /**< Disable of auto-response target DIF */
12407 	HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
12408 	HW_WORKAROUND_USE_DIF_QUARANTINE,
12409 	HW_WORKAROUND_USE_DIF_SEC_XRI,		/**< Use secondary xri for multiple data phases */
12410 	HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB,	/**< FCFI reported in SRB not correct, use "first" registered domain */
12411 	HW_WORKAROUND_FW_VERSION_TOO_LOW,	/**< The FW version is not the min version supported by this driver */
12412 	HW_WORKAROUND_SGLC_MISREPORTED,	/**< Chip supports SGL Chaining but SGLC is not set in SLI4_PARAMS */
12413 	HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE,	/**< Don't use SEND_FRAME capable if FW version is too old */
12414 } hw_workaround_e;
12415 
12416 /**
12417  * @brief Internal workaround structure instance
12418  */
12419 
12420 typedef struct {
12421 	sli4_asic_type_e asic_type;
12422 	sli4_asic_rev_e asic_rev;
12423 	uint64_t fwrev_low;
12424 	uint64_t fwrev_high;
12425 
12426 	hw_workaround_e workaround;
12427 	uint32_t value;
12428 } hw_workaround_t;
12429 
12430 static hw_workaround_t hw_workarounds[] = {
12431 	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12432 		HW_WORKAROUND_TEST, 999},
12433 
12434 	/* Bug: 127585: if_type == 2 returns 0 for total length placed on
12435 	 * FCP_TSEND64_WQE completions.   Note, original driver code enables this
12436 	 * workaround for all asic types
12437 	 */
12438 	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12439 		HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
12440 
12441 	/* Bug: unknown, Lancer A0 has mis-reported max queue depth */
12442 	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
12443 		HW_WORKAROUND_MAX_QUEUE, 2048},
12444 
12445 	/* Bug: 143399, BE3 has mis-reported max RQ queue depth */
12446 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
12447 		HW_WORKAROUND_MAX_RQ, 2048},
12448 
12449 	/* Bug: 143399, skyhawk has mis-reported max RQ queue depth */
12450 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
12451 		HW_WORKAROUND_MAX_RQ, 2048},
12452 
12453 	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported WQE count method */
12454 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12455 		HW_WORKAROUND_WQE_COUNT_METHOD, 1},
12456 
12457 	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported RQE count method */
12458 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12459 		HW_WORKAROUND_RQE_COUNT_METHOD, 1},
12460 
12461 	/* Bug: 142968, BE3 UE with RPI == 0xffff */
12462 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12463 		HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
12464 
12465 	/* Bug: unknown, Skyhawk won't support auto-response on target T10-PI  */
12466 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12467 		HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
12468 
12469 	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
12470 		HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
12471 
12472 	/* Bug: 160124, Skyhawk quarantine DIF XRIs  */
12473 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12474 		HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
12475 
12476 	/* Bug: 161832, Skyhawk use secondary XRI for multiple data phase TRECV */
12477 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12478 		HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
12479 
12480 	/* Bug: xxxxxx, FCFI reported in SRB not corrrect */
12481 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12482 		HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
12483 #if 0
12484 	/* Bug: 165642, FW version check for driver */
12485 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
12486 		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12487 #endif
12488 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
12489 		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12490 
12491 	/* Bug 177061, Lancer FW does not set the SGLC bit */
12492 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12493 		HW_WORKAROUND_SGLC_MISREPORTED, 0},
12494 
12495 	/* BZ 181208/183914, enable this workaround for ALL revisions */
12496 	{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12497 		HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
12498 };
12499 
12500 /**
12501  * @brief Function prototypes
12502  */
12503 
12504 static int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
12505 
12506 /**
12507  * @brief Parse the firmware version (name)
12508  *
12509  * Parse a string of the form a.b.c.d, returning a uint64_t packed as defined
12510  * by the HW_FWREV() macro
12511  *
12512  * @param fwrev_string pointer to the firmware string
12513  *
12514  * @return packed firmware revision value
12515  */
12516 
12517 static uint64_t
12518 parse_fw_version(const char *fwrev_string)
12519 {
12520 	int v[4] = {0};
12521 	const char *p;
12522 	int i;
12523 
12524 	for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
12525 		v[i] = ocs_strtoul(p, 0, 0);
12526 		while(*p && *p != '.') {
12527 			p ++;
12528 		}
12529 		if (*p) {
12530 			p ++;
12531 		}
12532 	}
12533 
12534 	/* Special case for bootleg releases with f/w rev 0.0.9999.0, set to max value */
12535 	if (v[2] == 9999) {
12536 		return HW_FWREV_MAX;
12537 	} else {
12538 		return HW_FWREV(v[0], v[1], v[2], v[3]);
12539 	}
12540 }
12541 
12542 /**
12543  * @brief Test for a workaround match
12544  *
12545  * Looks at the asic type, asic revision, and fw revision, and returns TRUE if match.
12546  *
12547  * @param hw Pointer to the HW structure
12548  * @param w Pointer to a workaround structure entry
12549  *
12550  * @return Return TRUE for a match
12551  */
12552 
12553 static int32_t
12554 ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
12555 {
12556 	return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
12557 		    ((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
12558 		    (w->fwrev_low <= hw->workaround.fwrev) &&
12559 		    ((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
12560 }
12561 
12562 /**
12563  * @brief Setup HW runtime workarounds
12564  *
12565  * The function is called at the end of ocs_hw_setup() to setup any runtime workarounds
12566  * based on the HW/SLI setup.
12567  *
12568  * @param hw Pointer to HW structure
12569  *
12570  * @return none
12571  */
12572 
12573 void
12574 ocs_hw_workaround_setup(struct ocs_hw_s *hw)
12575 {
12576 	hw_workaround_t *w;
12577 	sli4_t *sli4 = &hw->sli;
12578 	uint32_t i;
12579 
12580 	/* Initialize the workaround settings */
12581 	ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
12582 
12583 	/* If hw_war_version is non-null, then its a value that was set by a module parameter
12584 	 * (sorry for the break in abstraction, but workarounds are ... well, workarounds)
12585 	 */
12586 
12587 	if (hw->hw_war_version) {
12588 		hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
12589 	} else {
12590 		hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
12591 	}
12592 
12593 	/* Walk the workaround list, if a match is found, then handle it */
12594 	for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
12595 		if (ocs_hw_workaround_match(hw, w)) {
12596 			switch(w->workaround) {
12597 			case HW_WORKAROUND_TEST: {
12598 				ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
12599 				break;
12600 			}
12601 
12602 			case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
12603 				ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
12604 				hw->workaround.retain_tsend_io_length = 1;
12605 				break;
12606 			}
12607 			case HW_WORKAROUND_MAX_QUEUE: {
12608 				sli4_qtype_e q;
12609 
12610 				ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
12611 				for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
12612 					if (hw->num_qentries[q] > w->value) {
12613 						hw->num_qentries[q] = w->value;
12614 					}
12615 				}
12616 				break;
12617 			}
12618 			case HW_WORKAROUND_MAX_RQ: {
12619 				ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
12620 				if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
12621 					hw->num_qentries[SLI_QTYPE_RQ] = w->value;
12622 				}
12623 				break;
12624 			}
12625 			case HW_WORKAROUND_WQE_COUNT_METHOD: {
12626 				ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
12627 				sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
12628 				sli_calc_max_qentries(sli4);
12629 				break;
12630 			}
12631 			case HW_WORKAROUND_RQE_COUNT_METHOD: {
12632 				ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
12633 				sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
12634 				sli_calc_max_qentries(sli4);
12635 				break;
12636 			}
12637 			case HW_WORKAROUND_USE_UNREGISTERD_RPI:
12638 				ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
12639 				hw->workaround.use_unregistered_rpi = TRUE;
12640 				/*
12641 				 * Allocate an RPI that is never registered, to be used in the case where
12642 				 * a node has been unregistered, and its indicator (RPI) value is set to 0xFFFF
12643 				 */
12644 				if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
12645 					&hw->workaround.unregistered_index)) {
12646 					ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
12647 					hw->workaround.use_unregistered_rpi = FALSE;
12648 				}
12649 				break;
12650 			case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
12651 				ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
12652 				hw->workaround.disable_ar_tgt_dif = TRUE;
12653 				break;
12654 			case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
12655 				ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
12656 				hw->workaround.disable_dump_loc = TRUE;
12657 				break;
12658 			case HW_WORKAROUND_USE_DIF_QUARANTINE:
12659 				ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
12660 				hw->workaround.use_dif_quarantine = TRUE;
12661 				break;
12662 			case HW_WORKAROUND_USE_DIF_SEC_XRI:
12663 				ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
12664 				hw->workaround.use_dif_sec_xri = TRUE;
12665 				break;
12666 			case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
12667 				ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
12668 				hw->workaround.override_fcfi = TRUE;
12669 				break;
12670 
12671 			case HW_WORKAROUND_FW_VERSION_TOO_LOW:
12672 				ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
12673 				hw->workaround.fw_version_too_low = TRUE;
12674 				break;
12675 			case HW_WORKAROUND_SGLC_MISREPORTED:
12676 				ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
12677 				hw->workaround.sglc_misreported = TRUE;
12678 				break;
12679 			case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
12680 				ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
12681 				hw->workaround.ignore_send_frame = TRUE;
12682 				break;
12683 			} /* switch(w->workaround) */
12684 		}
12685 	}
12686 }
12687