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