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