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 */
72ocs_hw_global_t hw_global;
73
74static void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
75static void ocs_hw_adjust_wqs(ocs_hw_t *hw);
76static uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
77static int32_t ocs_hw_cb_link(void *, void *);
78static int32_t ocs_hw_cb_fip(void *, void *);
79static int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
80static int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
81static int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
82static int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
83static int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
84static int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
85static ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
86static ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
87static int32_t ocs_hw_flush(ocs_hw_t *);
88static int32_t ocs_hw_command_cancel(ocs_hw_t *);
89static int32_t ocs_hw_io_cancel(ocs_hw_t *);
90static void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
91static void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
92static int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
93static 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);
94static int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void  *);
95static int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void  *);
96static int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void  *);
97static int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void  *);
98static int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
99static void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
100static 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);
101typedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
102static 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);
103static void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
104
105static int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
106static ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
107static ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
108static ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
109static ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
110static 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 *);
111static 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 *);
112static void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
113static ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
114static ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
115static ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
116static void ocs_hw_io_free_internal(void *arg);
117static void ocs_hw_io_free_port_owned(void *arg);
118static ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
119static ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
120static void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
121static int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
122static ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
123static 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 */
126static int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
127static int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
128
129/* Port state machine */
130static void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
131static void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
132static void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
133static void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
134static void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
135
136/* Domain state machine */
137static void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
138static void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
139static void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
140static void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
141static void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
142static int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
143static int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
144static int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
145
146/* BZ 161832 */
147static void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
148
149/* WQE timeouts */
150static void target_wqe_timer_cb(void *arg);
151static void shutdown_target_wqe_timer(ocs_hw_t *hw);
152
153static inline void
154ocs_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
168static inline void
169ocs_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
184static 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
198static 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 */
228static uint32_t
229ocs_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
241static ocs_hw_rtn_e
242ocs_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 */
267static ocs_hw_rtn_e
268ocs_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 */
327ocs_hw_rtn_e
328ocs_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 */
520ocs_hw_rtn_e
521ocs_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 */
1032static int32_t
1033ocs_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
1079issue_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 */
1121static void
1122ocs_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 */
1145ocs_hw_rtn_e
1146ocs_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
1300ocs_hw_rtn_e
1301ocs_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
1452int32_t
1453ocs_hw_get_num_eq(ocs_hw_t *hw)
1454{
1455	return hw->eq_count;
1456}
1457
1458static int32_t
1459ocs_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
1468ocs_hw_rtn_e
1469ocs_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
1756void *
1757ocs_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
1796ocs_hw_rtn_e
1797ocs_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
2018ocs_hw_rtn_e
2019ocs_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 */
2061int32_t
2062ocs_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
2099void
2100ocs_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
2111int32_t
2112ocs_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 */
2165int32_t
2166ocs_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 */
2236static int32_t
2237ocs_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 */
2283ocs_hw_rtn_e
2284ocs_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 */
2378ocs_hw_rtn_e
2379ocs_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 */
2432ocs_hw_rtn_e
2433ocs_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
2491ocs_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 */
2515ocs_hw_rtn_e
2516ocs_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 */
2560static int32_t
2561ocs_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 */
2609ocs_hw_rtn_e
2610ocs_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 */
2724ocs_hw_rtn_e
2725ocs_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 */
2769ocs_hw_rtn_e
2770ocs_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 */
2826ocs_hw_rtn_e
2827ocs_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 */
2870ocs_hw_rtn_e
2871ocs_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 */
2908ocs_hw_rtn_e
2909ocs_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 */
2932ocs_hw_rtn_e
2933ocs_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 */
2976ocs_hw_rtn_e
2977ocs_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 */
3059ocs_hw_rtn_e
3060ocs_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 */
3102ocs_hw_rtn_e
3103ocs_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 */
3180ocs_hw_rtn_e
3181ocs_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
3220ocs_hw_rtn_e
3221ocs_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
3240ocs_hw_rtn_e
3241ocs_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
3268ocs_hw_rtn_e
3269ocs_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 */
3310static inline void
3311ocs_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 */
3341static 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 */
3371ocs_hw_io_t *
3372ocs_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 */
3399ocs_hw_io_t *
3400ocs_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 */
3430static void
3431ocs_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 */
3457static inline void
3458ocs_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 */
3473static void
3474ocs_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 */
3502static void
3503ocs_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 */
3531int32_t
3532ocs_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 */
3558uint8_t
3559ocs_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 */
3577static 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 */
3621int32_t
3622hw_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 */
3670static void
3671hw_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 */
3708static void
3709ocs_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 */
3823ocs_hw_rtn_e
3824ocs_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 */
4035ocs_hw_rtn_e
4036ocs_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 */
4331ocs_hw_rtn_e
4332ocs_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
4381ocs_hw_rtn_e
4382ocs_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
4395static void
4396ocs_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 */
4431ocs_hw_rtn_e
4432ocs_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 */
4543ocs_hw_rtn_e
4544ocs_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
4645static ocs_hw_rtn_e
4646ocs_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 */
4728ocs_hw_rtn_e
4729ocs_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 */
4797ocs_hw_rtn_e
4798ocs_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 */
4857ocs_hw_rtn_e
4858ocs_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 */
5002int32_t
5003ocs_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
5014typedef 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
5019typedef 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
5025typedef struct ocs_hw_temp_cb_arg {
5026	ocs_hw_temp_cb_t cb;
5027	void *arg;
5028} ocs_hw_temp_cb_arg_t;
5029
5030typedef 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
5035typedef 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
5040typedef 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
5046typedef 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 */
5071ocs_hw_rtn_e
5072ocs_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 */
5103ocs_hw_rtn_e
5104ocs_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 */
5163static int32_t
5164ocs_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 */
5211static int32_t
5212ocs_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 */
5250ocs_hw_rtn_e
5251ocs_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 */
5310ocs_hw_rtn_e
5311ocs_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 */
5362static int32_t
5363ocs_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 */
5409ocs_hw_rtn_e
5410ocs_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 */
5468static int32_t
5469ocs_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 */
5550ocs_hw_rtn_e
5551ocs_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 */
5604static int32_t
5605ocs_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 */
5657typedef 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 */
5666static 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 */
5682typedef 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 */
5690static 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 */
5703static ocs_hw_linkcfg_e
5704ocs_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 */
5724static const char *
5725ocs_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 */
5745static uint32_t
5746ocs_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 */
5765static ocs_hw_linkcfg_e
5766ocs_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 */
5780typedef 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 */
5802static ocs_hw_rtn_e
5803ocs_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 */
5833static ocs_hw_rtn_e
5834ocs_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 */
5900static void
5901ocs_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 */
5932static ocs_hw_rtn_e
5933ocs_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 */
5996static ocs_hw_rtn_e
5997ocs_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 */
6025static ocs_hw_rtn_e
6026ocs_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 */
6091static void
6092ocs_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 */
6127static ocs_hw_rtn_e
6128ocs_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 */
6191static ocs_hw_rtn_e
6192ocs_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 */
6227static ocs_hw_rtn_e
6228ocs_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
6256static void
6257ocs_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
6265static void
6266ocs_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 */
6293static ocs_hw_rtn_e
6294ocs_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 */
6321static ocs_hw_rtn_e
6322ocs_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 */
6376static ocs_hw_rtn_e
6377ocs_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 */
6411static ocs_hw_rtn_e
6412ocs_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 */
6446static void
6447ocs_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 */
6496ocs_hw_rtn_e
6497ocs_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 */
6586static ocs_hw_rtn_e
6587ocs_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 */
6630typedef 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 */
6651static ocs_hw_rtn_e
6652ocs_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 */
6722static void
6723ocs_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
6784ocs_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 */
6813static int32_t
6814ocs_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 */
6870ocs_hw_rtn_e
6871ocs_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 */
6902static int32_t
6903ocs_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 */
6949ocs_hw_rtn_e
6950ocs_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 */
7022static int32_t
7023ocs_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 */
7060ocs_hw_rtn_e
7061ocs_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
7108typedef 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 */
7126static int32_t
7127ocs_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 */
7198ocs_hw_rtn_e
7199ocs_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
7253typedef 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 */
7278static int32_t
7279ocs_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 */
7317static int32_t
7318ocs_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 */
7491ocs_hw_rtn_e
7492ocs_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
7546typedef 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 */
7569static int32_t
7570ocs_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 */
7627ocs_hw_rtn_e
7628ocs_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
7679typedef 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 */
7695static int32_t
7696ocs_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 */
7736int32_t
7737ocs_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
7779typedef 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 */
7795static int32_t
7796ocs_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 */
7833int32_t
7834ocs_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
7871typedef 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 */
7887static int32_t
7888ocs_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 */
7930int32_t
7931ocs_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 */
7977uint32_t
7978ocs_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 */
8023uint32_t
8024ocs_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
8044typedef 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 */
8060static int32_t
8061ocs_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 */
8095int32_t
8096ocs_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 */
8149static void
8150ocs_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 */
8176int32_t
8177ocs_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
8199static int32_t
8200ocs_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
8247static int32_t
8248ocs_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
8304ocs_domain_t *
8305ocs_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
8322static ocs_domain_t *
8323ocs_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 */
8354static void
8355ocs_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 */
8395void
8396ocs_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 */
8512void
8513ocs_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 */
8554static void
8555ocs_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
8802exit_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 */
8817static void
8818ocs_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 */
8889void
8890ocs_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 */
8971static void
8972ocs_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
9030static int32_t
9031ocs_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 */
9071static int32_t
9072ocs_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 */
9093static ocs_hw_rtn_e
9094ocs_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 */
9136static int32_t
9137ocs_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 */
9217static int32_t
9218ocs_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
9281static int32_t
9282ocs_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
9356static int32_t
9357ocs_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
9384static int32_t
9385ocs_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
9428static int32_t
9429ocs_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 */
9470static ocs_hw_rtn_e
9471ocs_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;
9574error:
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
9583static ocs_hw_rtn_e
9584ocs_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
9660static int32_t
9661ocs_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
9673static int32_t
9674ocs_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 */
9709ocs_hw_io_t *
9710ocs_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 */
9723static void
9724ocs_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
9758static int32_t
9759ocs_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
9829static int32_t
9830ocs_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
9856static 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
9931static 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
9958static 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
9983static 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
10014static 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
10048static 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
10095static 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
10123static 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
10156static 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
10196static 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
10222static 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
10276static 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
10312static 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
10376static 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
10405static 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
10459static 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
10479static 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
10515static 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
10536static 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
10568static 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
10600static 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
10633static 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
10672static 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
10714static 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
10755static 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
10794static 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
10886static 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
10915static 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
10951static 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
10991static 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
11032static 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
11085static 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 */
11144static 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
11164static int32_t
11165target_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
11226static void
11227target_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
11241static void
11242shutdown_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 */
11280uint8_t
11281ocs_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
11299uint8_t
11300ocs_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 */
11316static void
11317ocs_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 */
11354static int32_t
11355ocs_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
11380static ocs_hw_rtn_e
11381ocs_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
11417uint32_t
11418ocs_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 */
11484static int32_t
11485ocs_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
11516ocs_hw_rtn_e
11517ocs_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 */
11556static ocs_hw_rq_buffer_t *
11557ocs_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 */
11596static void
11597ocs_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 */
11618ocs_hw_rtn_e
11619ocs_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 */
11669ocs_hw_rtn_e
11670ocs_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 */
11711void
11712ocs_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 */
11732typedef 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 */
11752static void
11753ocs_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 */
11778int32_t
11779ocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
11780{
11781	int32_t rc = 0;
11782	ocs_hw_async_call_ctx_t *ctx;
11783
11784	/*
11785	 * Allocate a callback context (which includes the mailbox command buffer), we need
11786	 * this to be persistent as the mailbox command submission may be queued and executed later
11787	 * execution.
11788	 */
11789	ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
11790	if (ctx == NULL) {
11791		ocs_log_err(hw->os, "failed to malloc async call context\n");
11792		return OCS_HW_RTN_NO_MEMORY;
11793	}
11794	ctx->callback = callback;
11795	ctx->arg = arg;
11796
11797	/* Build and send a NOP mailbox command */
11798	if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
11799		ocs_log_err(hw->os, "COMMON_NOP format failure\n");
11800		ocs_free(hw->os, ctx, sizeof(*ctx));
11801		rc = -1;
11802	}
11803
11804	if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
11805		ocs_log_err(hw->os, "COMMON_NOP command failure\n");
11806		ocs_free(hw->os, ctx, sizeof(*ctx));
11807		rc = -1;
11808	}
11809	return rc;
11810}
11811
11812/**
11813 * @brief Initialize the reqtag pool.
11814 *
11815 * @par Description
11816 * The WQ request tag pool is initialized.
11817 *
11818 * @param hw Pointer to HW object.
11819 *
11820 * @return Returns 0 on success, or a negative error code value on failure.
11821 */
11822ocs_hw_rtn_e
11823ocs_hw_reqtag_init(ocs_hw_t *hw)
11824{
11825	if (hw->wq_reqtag_pool == NULL) {
11826		hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
11827		if (hw->wq_reqtag_pool == NULL) {
11828			ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
11829			return OCS_HW_RTN_NO_MEMORY;
11830		}
11831	}
11832	ocs_hw_reqtag_reset(hw);
11833	return OCS_HW_RTN_SUCCESS;
11834}
11835
11836/**
11837 * @brief Allocate a WQ request tag.
11838 *
11839 * Allocate and populate a WQ request tag from the WQ request tag pool.
11840 *
11841 * @param hw Pointer to HW object.
11842 * @param callback Callback function.
11843 * @param arg Pointer to callback argument.
11844 *
11845 * @return Returns pointer to allocated WQ request tag, or NULL if object cannot be allocated.
11846 */
11847hw_wq_callback_t *
11848ocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
11849{
11850	hw_wq_callback_t *wqcb;
11851
11852	ocs_hw_assert(callback != NULL);
11853
11854	wqcb = ocs_pool_get(hw->wq_reqtag_pool);
11855	if (wqcb != NULL) {
11856		ocs_hw_assert(wqcb->callback == NULL);
11857		wqcb->callback = callback;
11858		wqcb->arg = arg;
11859	}
11860	return wqcb;
11861}
11862
11863/**
11864 * @brief Free a WQ request tag.
11865 *
11866 * Free the passed in WQ request tag.
11867 *
11868 * @param hw Pointer to HW object.
11869 * @param wqcb Pointer to WQ request tag object to free.
11870 *
11871 * @return None.
11872 */
11873void
11874ocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
11875{
11876	ocs_hw_assert(wqcb->callback != NULL);
11877	wqcb->callback = NULL;
11878	wqcb->arg = NULL;
11879	ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11880}
11881
11882/**
11883 * @brief Return WQ request tag by index.
11884 *
11885 * @par Description
11886 * Return pointer to WQ request tag object given an index.
11887 *
11888 * @param hw Pointer to HW object.
11889 * @param instance_index Index of WQ request tag to return.
11890 *
11891 * @return Pointer to WQ request tag, or NULL.
11892 */
11893hw_wq_callback_t *
11894ocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
11895{
11896	hw_wq_callback_t *wqcb;
11897
11898	wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
11899	if (wqcb == NULL) {
11900		ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
11901	}
11902	return wqcb;
11903}
11904
11905/**
11906 * @brief Reset the WQ request tag pool.
11907 *
11908 * @par Description
11909 * Reset the WQ request tag pool, returning all to the free list.
11910 *
11911 * @param hw pointer to HW object.
11912 *
11913 * @return None.
11914 */
11915void
11916ocs_hw_reqtag_reset(ocs_hw_t *hw)
11917{
11918	hw_wq_callback_t *wqcb;
11919	uint32_t i;
11920
11921	/* Remove all from freelist */
11922	while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
11923		;
11924	}
11925
11926	/* Put them all back */
11927	for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
11928		wqcb->instance_index = i;
11929		wqcb->callback = NULL;
11930		wqcb->arg = NULL;
11931		ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11932	}
11933}
11934
11935/**
11936 * @brief Handle HW assertion
11937 *
11938 * HW assert, display diagnostic message, and abort.
11939 *
11940 * @param cond string describing failing assertion condition
11941 * @param filename file name
11942 * @param linenum line number
11943 *
11944 * @return none
11945 */
11946void
11947_ocs_hw_assert(const char *cond, const char *filename, int linenum)
11948{
11949	ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
11950	ocs_abort();
11951		/* no return */
11952}
11953
11954/**
11955 * @brief Handle HW verify
11956 *
11957 * HW verify, display diagnostic message, dump stack and return.
11958 *
11959 * @param cond string describing failing verify condition
11960 * @param filename file name
11961 * @param linenum line number
11962 *
11963 * @return none
11964 */
11965void
11966_ocs_hw_verify(const char *cond, const char *filename, int linenum)
11967{
11968	ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
11969	ocs_print_stack();
11970}
11971
11972/**
11973 * @brief Reque XRI
11974 *
11975 * @par Description
11976 * Reque XRI
11977 *
11978 * @param hw Pointer to HW object.
11979 * @param io Pointer to HW IO
11980 *
11981 * @return Return 0 if successful else returns -1
11982 */
11983int32_t
11984ocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
11985{
11986	int32_t rc = 0;
11987
11988	rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
11989	if (rc) {
11990		ocs_list_add_tail(&hw->io_port_dnrx, io);
11991		rc = -1;
11992		goto exit_ocs_hw_reque_xri;
11993	}
11994
11995	io->auto_xfer_rdy_dnrx = 0;
11996	io->type = OCS_HW_IO_DNRX_REQUEUE;
11997	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)) {
11998		/* Clear buffer from XRI */
11999		ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
12000		io->axr_buf = NULL;
12001
12002		ocs_log_err(hw->os, "requeue_xri WQE error\n");
12003		ocs_list_add_tail(&hw->io_port_dnrx, io);
12004
12005		rc = -1;
12006		goto exit_ocs_hw_reque_xri;
12007	}
12008
12009	if (io->wq == NULL) {
12010		io->wq = ocs_hw_queue_next_wq(hw, io);
12011		ocs_hw_assert(io->wq != NULL);
12012	}
12013
12014	/*
12015	 * Add IO to active io wqe list before submitting, in case the
12016	 * wcqe processing preempts this thread.
12017	 */
12018	OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
12019	OCS_STAT(io->wq->use_count++);
12020
12021	rc = hw_wq_write(io->wq, &io->wqe);
12022	if (rc < 0) {
12023		ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
12024		rc = -1;
12025	}
12026
12027exit_ocs_hw_reque_xri:
12028	return 0;
12029}
12030
12031uint32_t
12032ocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
12033{
12034	sli4_t *sli4 = &ocs->hw.sli;
12035	ocs_dma_t       dma;
12036	uint8_t		*payload = NULL;
12037
12038	int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
12039
12040	/* allocate memory for the service parameters */
12041	if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
12042		ocs_log_err(ocs, "Failed to allocate DMA memory\n");
12043		return 1;
12044	}
12045
12046	if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
12047				&dma, indicator)) {
12048		ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
12049		ocs_dma_free(ocs, &dma);
12050		return 1;
12051	}
12052
12053	if (sli_bmbx_command(sli4)) {
12054		ocs_log_err(ocs, "READ_SPARM64 command failure\n");
12055		ocs_dma_free(ocs, &dma);
12056		return 1;
12057	}
12058
12059	payload = dma.virt;
12060	ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
12061	ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
12062	ocs_dma_free(ocs, &dma);
12063	return 0;
12064}
12065
12066/**
12067 * @page fc_hw_api_overview HW APIs
12068 * - @ref devInitShutdown
12069 * - @ref domain
12070 * - @ref port
12071 * - @ref node
12072 * - @ref io
12073 * - @ref interrupt
12074 *
12075 * <div class="overview">
12076 * The Hardware Abstraction Layer (HW) insulates the higher-level code from the SLI-4
12077 * message details, but the higher level code must still manage domains, ports,
12078 * IT nexuses, and IOs. The HW API is designed to help the higher level manage
12079 * these objects.<br><br>
12080 *
12081 * The HW uses function callbacks to notify the higher-level code of events
12082 * that are received from the chip. There are currently three types of
12083 * functions that may be registered:
12084 *
12085 * <ul><li>domain ��� This function is called whenever a domain event is generated
12086 * within the HW. Examples include a new FCF is discovered, a connection
12087 * to a domain is disrupted, and allocation callbacks.</li>
12088 * <li>unsolicited ��� This function is called whenever new data is received in
12089 * the SLI-4 receive queue.</li>
12090 * <li>rnode ��� This function is called for remote node events, such as attach status
12091 * and  allocation callbacks.</li></ul>
12092 *
12093 * Upper layer functions may be registered by using the ocs_hw_callback() function.
12094 *
12095 * <img src="elx_fc_hw.jpg" alt="FC/FCoE HW" title="FC/FCoE HW" align="right"/>
12096 * <h2>FC/FCoE HW API</h2>
12097 * The FC/FCoE HW component builds upon the SLI-4 component to establish a flexible
12098 * interface for creating the necessary common objects and sending I/Os. It may be used
12099 * ���as is��� in customer implementations or it can serve as an example of typical interactions
12100 * between a driver and the SLI-4 hardware. The broad categories of functionality include:
12101 *
12102 * <ul><li>Setting-up and tearing-down of the HW.</li>
12103 * <li>Allocating and using the common objects (SLI Port, domain, remote node).</li>
12104 * <li>Sending and receiving I/Os.</li></ul>
12105 *
12106 * <h3>HW Setup</h3>
12107 * To set up the HW:
12108 *
12109 * <ol>
12110 * <li>Set up the HW object using ocs_hw_setup().<br>
12111 * This step performs a basic configuration of the SLI-4 component and the HW to
12112 * enable querying the hardware for its capabilities. At this stage, the HW is not
12113 * capable of general operations (such as, receiving events or sending I/Os).</li><br><br>
12114 * <li>Configure the HW according to the driver requirements.<br>
12115 * The HW provides functions to discover hardware capabilities (ocs_hw_get()), as
12116 * well as configures the amount of resources required (ocs_hw_set()). The driver
12117 * must also register callback functions (ocs_hw_callback()) to receive notification of
12118 * various asynchronous events.<br><br>
12119 * @b Note: Once configured, the driver must initialize the HW (ocs_hw_init()). This
12120 * step creates the underlying queues, commits resources to the hardware, and
12121 * prepares the hardware for operation. While the hardware is operational, the
12122 * port is not online, and cannot send or receive data.</li><br><br>
12123 * <br><br>
12124 * <li>Finally, the driver can bring the port online (ocs_hw_port_control()).<br>
12125 * When the link comes up, the HW determines if a domain is present and notifies the
12126 * driver using the domain callback function. This is the starting point of the driver's
12127 * interaction with the common objects.<br><br>
12128 * @b Note: For FCoE, there may be more than one domain available and, therefore,
12129 * more than one callback.</li>
12130 * </ol>
12131 *
12132 * <h3>Allocating and Using Common Objects</h3>
12133 * Common objects provide a mechanism through which the various OneCore Storage
12134 * driver components share and track information. These data structures are primarily
12135 * used to track SLI component information but can be extended by other components, if
12136 * needed. The main objects are:
12137 *
12138 * <ul><li>DMA ��� the ocs_dma_t object describes a memory region suitable for direct
12139 * memory access (DMA) transactions.</li>
12140 * <li>SCSI domain ��� the ocs_domain_t object represents the SCSI domain, including
12141 * any infrastructure devices such as FC switches and FC forwarders. The domain
12142 * object contains both an FCFI and a VFI.</li>
12143 * <li>SLI Port (sport) ��� the ocs_sli_port_t object represents the connection between
12144 * the driver and the SCSI domain. The SLI Port object contains a VPI.</li>
12145 * <li>Remote node ��� the ocs_remote_node_t represents a connection between the SLI
12146 * Port and another device in the SCSI domain. The node object contains an RPI.</li></ul>
12147 *
12148 * Before the driver can send I/Os, it must allocate the SCSI domain, SLI Port, and remote
12149 * node common objects and establish the connections between them. The goal is to
12150 * connect the driver to the SCSI domain to exchange I/Os with other devices. These
12151 * common object connections are shown in the following figure, FC Driver Common Objects:
12152 * <img src="elx_fc_common_objects.jpg"
12153 * alt="FC Driver Common Objects" title="FC Driver Common Objects" align="center"/>
12154 *
12155 * The first step is to create a connection to the domain by allocating an SLI Port object.
12156 * The SLI Port object represents a particular FC ID and must be initialized with one. With
12157 * the SLI Port object, the driver can discover the available SCSI domain(s). On identifying
12158 * a domain, the driver allocates a domain object and attaches to it using the previous SLI
12159 * port object.<br><br>
12160 *
12161 * @b Note: In some cases, the driver may need to negotiate service parameters (that is,
12162 * FLOGI) with the domain before attaching.<br><br>
12163 *
12164 * Once attached to the domain, the driver can discover and attach to other devices
12165 * (remote nodes). The exact discovery method depends on the driver, but it typically
12166 * includes using a position map, querying the fabric name server, or an out-of-band
12167 * method. In most cases, it is necessary to log in with devices before performing I/Os.
12168 * Prior to sending login-related ELS commands (ocs_hw_srrs_send()), the driver must
12169 * allocate a remote node object (ocs_hw_node_alloc()). If the login negotiation is
12170 * successful, the driver must attach the nodes (ocs_hw_node_attach()) to the SLI Port
12171 * before exchanging FCP I/O.<br><br>
12172 *
12173 * @b Note: The HW manages both the well known fabric address and the name server as
12174 * nodes in the domain. Therefore, the driver must allocate node objects prior to
12175 * communicating with either of these entities.
12176 *
12177 * <h3>Sending and Receiving I/Os</h3>
12178 * The HW provides separate interfaces for sending BLS/ ELS/ FC-CT and FCP, but the
12179 * commands are conceptually similar. Since the commands complete asynchronously,
12180 * the caller must provide a HW I/O object that maintains the I/O state, as well as
12181 * provide a callback function. The driver may use the same callback function for all I/O
12182 * operations, but each operation must use a unique HW I/O object. In the SLI-4
12183 * architecture, there is a direct association between the HW I/O object and the SGL used
12184 * to describe the data. Therefore, a driver typically performs the following operations:
12185 *
12186 * <ul><li>Allocates a HW I/O object (ocs_hw_io_alloc()).</li>
12187 * <li>Formats the SGL, specifying both the HW I/O object and the SGL.
12188 * (ocs_hw_io_init_sges() and ocs_hw_io_add_sge()).</li>
12189 * <li>Sends the HW I/O (ocs_hw_io_send()).</li></ul>
12190 *
12191 * <h3>HW Tear Down</h3>
12192 * To tear-down the HW:
12193 *
12194 * <ol><li>Take the port offline (ocs_hw_port_control()) to prevent receiving further
12195 * data andevents.</li>
12196 * <li>Destroy the HW object (ocs_hw_teardown()).</li>
12197 * <li>Free any memory used by the HW, such as buffers for unsolicited data.</li></ol>
12198 * <br>
12199 * </div><!-- overview -->
12200 *
12201 */
12202
12203/**
12204 * This contains all hw runtime workaround code.  Based on the asic type,
12205 * asic revision, and range of fw revisions, a particular workaround may be enabled.
12206 *
12207 * A workaround may consist of overriding a particular HW/SLI4 value that was initialized
12208 * during ocs_hw_setup() (for example the MAX_QUEUE overrides for mis-reported queue
12209 * sizes). Or if required, elements of the ocs_hw_workaround_t structure may be set to
12210 * control specific runtime behavior.
12211 *
12212 * It is intended that the controls in ocs_hw_workaround_t be defined functionally.  So we
12213 * would have the driver look like:  "if (hw->workaround.enable_xxx) then ...", rather than
12214 * what we might previously see as "if this is a BE3, then do xxx"
12215 *
12216 */
12217
12218#define HW_FWREV_ZERO		(0ull)
12219#define HW_FWREV_MAX		(~0ull)
12220
12221#define SLI4_ASIC_TYPE_ANY	0
12222#define SLI4_ASIC_REV_ANY	0
12223
12224/**
12225 * @brief Internal definition of workarounds
12226 */
12227
12228typedef enum {
12229	HW_WORKAROUND_TEST = 1,
12230	HW_WORKAROUND_MAX_QUEUE,	/**< Limits all queues */
12231	HW_WORKAROUND_MAX_RQ,		/**< Limits only the RQ */
12232	HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
12233	HW_WORKAROUND_WQE_COUNT_METHOD,
12234	HW_WORKAROUND_RQE_COUNT_METHOD,
12235	HW_WORKAROUND_USE_UNREGISTERD_RPI,
12236	HW_WORKAROUND_DISABLE_AR_TGT_DIF, /**< Disable of auto-response target DIF */
12237	HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
12238	HW_WORKAROUND_USE_DIF_QUARANTINE,
12239	HW_WORKAROUND_USE_DIF_SEC_XRI,		/**< Use secondary xri for multiple data phases */
12240	HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB,	/**< FCFI reported in SRB not correct, use "first" registered domain */
12241	HW_WORKAROUND_FW_VERSION_TOO_LOW,	/**< The FW version is not the min version supported by this driver */
12242	HW_WORKAROUND_SGLC_MISREPORTED,	/**< Chip supports SGL Chaining but SGLC is not set in SLI4_PARAMS */
12243	HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE,	/**< Don't use SEND_FRAME capable if FW version is too old */
12244} hw_workaround_e;
12245
12246/**
12247 * @brief Internal workaround structure instance
12248 */
12249
12250typedef struct {
12251	sli4_asic_type_e asic_type;
12252	sli4_asic_rev_e asic_rev;
12253	uint64_t fwrev_low;
12254	uint64_t fwrev_high;
12255
12256	hw_workaround_e workaround;
12257	uint32_t value;
12258} hw_workaround_t;
12259
12260static hw_workaround_t hw_workarounds[] = {
12261	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12262		HW_WORKAROUND_TEST, 999},
12263
12264	/* Bug: 127585: if_type == 2 returns 0 for total length placed on
12265	 * FCP_TSEND64_WQE completions.   Note, original driver code enables this
12266	 * workaround for all asic types
12267	 */
12268	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12269		HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
12270
12271	/* Bug: unknown, Lancer A0 has mis-reported max queue depth */
12272	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
12273		HW_WORKAROUND_MAX_QUEUE, 2048},
12274
12275	/* Bug: 143399, BE3 has mis-reported max RQ queue depth */
12276	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
12277		HW_WORKAROUND_MAX_RQ, 2048},
12278
12279	/* Bug: 143399, skyhawk has mis-reported max RQ queue depth */
12280	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
12281		HW_WORKAROUND_MAX_RQ, 2048},
12282
12283	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported WQE count method */
12284	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12285		HW_WORKAROUND_WQE_COUNT_METHOD, 1},
12286
12287	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported RQE count method */
12288	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12289		HW_WORKAROUND_RQE_COUNT_METHOD, 1},
12290
12291	/* Bug: 142968, BE3 UE with RPI == 0xffff */
12292	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12293		HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
12294
12295	/* Bug: unknown, Skyhawk won't support auto-response on target T10-PI  */
12296	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12297		HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
12298
12299	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
12300		HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
12301
12302	/* Bug: 160124, Skyhawk quarantine DIF XRIs  */
12303	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12304		HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
12305
12306	/* Bug: 161832, Skyhawk use secondary XRI for multiple data phase TRECV */
12307	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12308		HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
12309
12310	/* Bug: xxxxxx, FCFI reported in SRB not corrrect */
12311	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12312		HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
12313#if 0
12314	/* Bug: 165642, FW version check for driver */
12315	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
12316		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12317#endif
12318	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
12319		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12320
12321	/* Bug 177061, Lancer FW does not set the SGLC bit */
12322	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12323		HW_WORKAROUND_SGLC_MISREPORTED, 0},
12324
12325	/* BZ 181208/183914, enable this workaround for ALL revisions */
12326	{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12327		HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
12328};
12329
12330/**
12331 * @brief Function prototypes
12332 */
12333
12334static int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
12335
12336/**
12337 * @brief Parse the firmware version (name)
12338 *
12339 * Parse a string of the form a.b.c.d, returning a uint64_t packed as defined
12340 * by the HW_FWREV() macro
12341 *
12342 * @param fwrev_string pointer to the firmware string
12343 *
12344 * @return packed firmware revision value
12345 */
12346
12347static uint64_t
12348parse_fw_version(const char *fwrev_string)
12349{
12350	int v[4] = {0};
12351	const char *p;
12352	int i;
12353
12354	for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
12355		v[i] = ocs_strtoul(p, 0, 0);
12356		while(*p && *p != '.') {
12357			p ++;
12358		}
12359		if (*p) {
12360			p ++;
12361		}
12362	}
12363
12364	/* Special case for bootleg releases with f/w rev 0.0.9999.0, set to max value */
12365	if (v[2] == 9999) {
12366		return HW_FWREV_MAX;
12367	} else {
12368		return HW_FWREV(v[0], v[1], v[2], v[3]);
12369	}
12370}
12371
12372/**
12373 * @brief Test for a workaround match
12374 *
12375 * Looks at the asic type, asic revision, and fw revision, and returns TRUE if match.
12376 *
12377 * @param hw Pointer to the HW structure
12378 * @param w Pointer to a workaround structure entry
12379 *
12380 * @return Return TRUE for a match
12381 */
12382
12383static int32_t
12384ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
12385{
12386	return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
12387		    ((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
12388		    (w->fwrev_low <= hw->workaround.fwrev) &&
12389		    ((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
12390}
12391
12392/**
12393 * @brief Setup HW runtime workarounds
12394 *
12395 * The function is called at the end of ocs_hw_setup() to setup any runtime workarounds
12396 * based on the HW/SLI setup.
12397 *
12398 * @param hw Pointer to HW structure
12399 *
12400 * @return none
12401 */
12402
12403void
12404ocs_hw_workaround_setup(struct ocs_hw_s *hw)
12405{
12406	hw_workaround_t *w;
12407	sli4_t *sli4 = &hw->sli;
12408	uint32_t i;
12409
12410	/* Initialize the workaround settings */
12411	ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
12412
12413	/* If hw_war_version is non-null, then its a value that was set by a module parameter
12414	 * (sorry for the break in abstraction, but workarounds are ... well, workarounds)
12415	 */
12416
12417	if (hw->hw_war_version) {
12418		hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
12419	} else {
12420		hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
12421	}
12422
12423	/* Walk the workaround list, if a match is found, then handle it */
12424	for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
12425		if (ocs_hw_workaround_match(hw, w)) {
12426			switch(w->workaround) {
12427			case HW_WORKAROUND_TEST: {
12428				ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
12429				break;
12430			}
12431
12432			case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
12433				ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
12434				hw->workaround.retain_tsend_io_length = 1;
12435				break;
12436			}
12437			case HW_WORKAROUND_MAX_QUEUE: {
12438				sli4_qtype_e q;
12439
12440				ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
12441				for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
12442					if (hw->num_qentries[q] > w->value) {
12443						hw->num_qentries[q] = w->value;
12444					}
12445				}
12446				break;
12447			}
12448			case HW_WORKAROUND_MAX_RQ: {
12449				ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
12450				if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
12451					hw->num_qentries[SLI_QTYPE_RQ] = w->value;
12452				}
12453				break;
12454			}
12455			case HW_WORKAROUND_WQE_COUNT_METHOD: {
12456				ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
12457				sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
12458				sli_calc_max_qentries(sli4);
12459				break;
12460			}
12461			case HW_WORKAROUND_RQE_COUNT_METHOD: {
12462				ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
12463				sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
12464				sli_calc_max_qentries(sli4);
12465				break;
12466			}
12467			case HW_WORKAROUND_USE_UNREGISTERD_RPI:
12468				ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
12469				hw->workaround.use_unregistered_rpi = TRUE;
12470				/*
12471				 * Allocate an RPI that is never registered, to be used in the case where
12472				 * a node has been unregistered, and its indicator (RPI) value is set to 0xFFFF
12473				 */
12474				if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
12475					&hw->workaround.unregistered_index)) {
12476					ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
12477					hw->workaround.use_unregistered_rpi = FALSE;
12478				}
12479				break;
12480			case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
12481				ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
12482				hw->workaround.disable_ar_tgt_dif = TRUE;
12483				break;
12484			case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
12485				ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
12486				hw->workaround.disable_dump_loc = TRUE;
12487				break;
12488			case HW_WORKAROUND_USE_DIF_QUARANTINE:
12489				ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
12490				hw->workaround.use_dif_quarantine = TRUE;
12491				break;
12492			case HW_WORKAROUND_USE_DIF_SEC_XRI:
12493				ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
12494				hw->workaround.use_dif_sec_xri = TRUE;
12495				break;
12496			case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
12497				ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
12498				hw->workaround.override_fcfi = TRUE;
12499				break;
12500
12501			case HW_WORKAROUND_FW_VERSION_TOO_LOW:
12502				ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
12503				hw->workaround.fw_version_too_low = TRUE;
12504				break;
12505			case HW_WORKAROUND_SGLC_MISREPORTED:
12506				ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
12507				hw->workaround.sglc_misreported = TRUE;
12508				break;
12509			case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
12510				ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
12511				hw->workaround.ignore_send_frame = TRUE;
12512				break;
12513			} /* switch(w->workaround) */
12514		}
12515	}
12516}
12517