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