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