1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $FreeBSD: stable/12/sys/dev/ocs_fc/ocs_hw.c 372539 2022-09-13 05:28:59Z git2svn $
32  */
33 
34 /**
35  * @file
36  * Defines and implements the Hardware Abstraction Layer (HW).
37  * All interaction with the hardware is performed through the HW, which abstracts
38  * the details of the underlying SLI-4 implementation.
39  */
40 
41 /**
42  * @defgroup devInitShutdown Device Initialization and Shutdown
43  * @defgroup domain Domain Functions
44  * @defgroup port Port Functions
45  * @defgroup node Remote Node Functions
46  * @defgroup io IO Functions
47  * @defgroup interrupt Interrupt handling
48  * @defgroup os OS Required Functions
49  */
50 
51 #include "ocs.h"
52 #include "ocs_os.h"
53 #include "ocs_hw.h"
54 #include "ocs_hw_queues.h"
55 
56 #define OCS_HW_MQ_DEPTH	128
57 #define OCS_HW_READ_FCF_SIZE	4096
58 #define OCS_HW_DEFAULT_AUTO_XFER_RDY_IOS	256
59 #define OCS_HW_WQ_TIMER_PERIOD_MS	500
60 
61 /* values used for setting the auto xfer rdy parameters */
62 #define OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT		0 /* 512 bytes */
63 #define OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT	TRUE
64 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT	FALSE
65 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT	0
66 #define OCS_HW_REQUE_XRI_REGTAG			65534
67 /* max command and response buffer lengths -- arbitrary at the moment */
68 #define OCS_HW_DMTF_CLP_CMD_MAX	256
69 #define OCS_HW_DMTF_CLP_RSP_MAX	256
70 
71 /* HW global data */
72 ocs_hw_global_t hw_global;
73 
74 static void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
75 static void ocs_hw_adjust_wqs(ocs_hw_t *hw);
76 static uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
77 static int32_t ocs_hw_cb_link(void *, void *);
78 static int32_t ocs_hw_cb_fip(void *, void *);
79 static int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
80 static int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
81 static int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
82 static int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
83 static int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
84 static int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
85 static ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
86 static ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
87 static int32_t ocs_hw_flush(ocs_hw_t *);
88 static int32_t ocs_hw_command_cancel(ocs_hw_t *);
89 static int32_t ocs_hw_io_cancel(ocs_hw_t *);
90 static void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
91 static void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
92 static int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
93 static ocs_hw_rtn_e ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg);
94 static int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void  *);
95 static int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void  *);
96 static int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void  *);
97 static int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void  *);
98 static int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
99 static void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg);
100 static int32_t ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len);
101 typedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
102 static ocs_hw_rtn_e ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg);
103 static void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
104 
105 static int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
106 static ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
107 static ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
108 static ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
109 static ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
110 static ocs_hw_rtn_e ocs_hw_set_linkcfg_lancer(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
111 static ocs_hw_rtn_e ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
112 static void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
113 static ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
114 static ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
115 static ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
116 static void ocs_hw_io_free_internal(void *arg);
117 static void ocs_hw_io_free_port_owned(void *arg);
118 static ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
119 static ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
120 static void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
121 static int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
122 static ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
123 static ocs_hw_rtn_e ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable);
124 
125 /* HW domain database operations */
126 static int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
127 static int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
128 
129 
130 /* Port state machine */
131 static void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
132 static void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
133 static void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
134 static void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
135 static void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
136 
137 /* Domain state machine */
138 static void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
139 static void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
140 static void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
141 static void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
142 static void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
143 static int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
144 static int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
145 static int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
146 
147 /* BZ 161832 */
148 static void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
149 
150 /* WQE timeouts */
151 static void target_wqe_timer_cb(void *arg);
152 static void shutdown_target_wqe_timer(ocs_hw_t *hw);
153 
154 static inline void
ocs_hw_add_io_timed_wqe(ocs_hw_t * hw,ocs_hw_io_t * io)155 ocs_hw_add_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
156 {
157 	if (hw->config.emulate_tgt_wqe_timeout && io->tgt_wqe_timeout) {
158 		/*
159 		 * Active WQE list currently only used for
160 		 * target WQE timeouts.
161 		 */
162 		ocs_lock(&hw->io_lock);
163 			ocs_list_add_tail(&hw->io_timed_wqe, io);
164 			io->submit_ticks = ocs_get_os_ticks();
165 		ocs_unlock(&hw->io_lock);
166 	}
167 }
168 
169 static inline void
ocs_hw_remove_io_timed_wqe(ocs_hw_t * hw,ocs_hw_io_t * io)170 ocs_hw_remove_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
171 {
172 	if (hw->config.emulate_tgt_wqe_timeout) {
173 		/*
174 		 * If target wqe timeouts are enabled,
175 		 * remove from active wqe list.
176 		 */
177 		ocs_lock(&hw->io_lock);
178 			if (ocs_list_on_list(&io->wqe_link)) {
179 				ocs_list_remove(&hw->io_timed_wqe, io);
180 			}
181 		ocs_unlock(&hw->io_lock);
182 	}
183 }
184 
ocs_hw_iotype_is_originator(uint16_t io_type)185 static uint8_t ocs_hw_iotype_is_originator(uint16_t io_type)
186 {
187 	switch (io_type) {
188 	case OCS_HW_IO_INITIATOR_READ:
189 	case OCS_HW_IO_INITIATOR_WRITE:
190 	case OCS_HW_IO_INITIATOR_NODATA:
191 	case OCS_HW_FC_CT:
192 	case OCS_HW_ELS_REQ:
193 		return 1;
194 	default:
195 		return 0;
196 	}
197 }
198 
ocs_hw_wcqe_abort_needed(uint16_t status,uint8_t ext,uint8_t xb)199 static uint8_t ocs_hw_wcqe_abort_needed(uint16_t status, uint8_t ext, uint8_t xb)
200 {
201 	/* if exchange not active, nothing to abort */
202 	if (!xb) {
203 		return FALSE;
204 	}
205 	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT) {
206 		switch (ext) {
207 		/* exceptions where abort is not needed */
208 		case SLI4_FC_LOCAL_REJECT_INVALID_RPI: /* lancer returns this after unreg_rpi */
209 		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED: /* abort already in progress */
210 			return FALSE;
211 		default:
212 			break;
213 		}
214 	}
215 	return TRUE;
216 }
217 
218 /**
219  * @brief Determine the number of chutes on the device.
220  *
221  * @par Description
222  * Some devices require queue resources allocated per protocol processor
223  * (chute). This function returns the number of chutes on this device.
224  *
225  * @param hw Hardware context allocated by the caller.
226  *
227  * @return Returns the number of chutes on the device for protocol.
228  */
229 static uint32_t
ocs_hw_get_num_chutes(ocs_hw_t * hw)230 ocs_hw_get_num_chutes(ocs_hw_t *hw)
231 {
232 	uint32_t num_chutes = 1;
233 
234 	if (sli_get_is_dual_ulp_capable(&hw->sli) &&
235 	    sli_get_is_ulp_enabled(&hw->sli, 0) &&
236 	    sli_get_is_ulp_enabled(&hw->sli, 1)) {
237 		num_chutes = 2;
238 	}
239 	return num_chutes;
240 }
241 
242 static ocs_hw_rtn_e
ocs_hw_link_event_init(ocs_hw_t * hw)243 ocs_hw_link_event_init(ocs_hw_t *hw)
244 {
245 	if (hw == NULL) {
246 		ocs_log_err(hw->os, "bad parameter hw=%p\n", hw);
247 		return OCS_HW_RTN_ERROR;
248 	}
249 
250 	hw->link.status = SLI_LINK_STATUS_MAX;
251 	hw->link.topology = SLI_LINK_TOPO_NONE;
252 	hw->link.medium = SLI_LINK_MEDIUM_MAX;
253 	hw->link.speed = 0;
254 	hw->link.loop_map = NULL;
255 	hw->link.fc_id = UINT32_MAX;
256 
257 	return OCS_HW_RTN_SUCCESS;
258 }
259 
260 /**
261  * @ingroup devInitShutdown
262  * @brief If this is physical port 0, then read the max dump size.
263  *
264  * @par Description
265  * Queries the FW for the maximum dump size
266  *
267  * @param hw Hardware context allocated by the caller.
268  *
269  * @return Returns 0 on success, or a non-zero value on failure.
270  */
271 static ocs_hw_rtn_e
ocs_hw_read_max_dump_size(ocs_hw_t * hw)272 ocs_hw_read_max_dump_size(ocs_hw_t *hw)
273 {
274 	uint8_t	buf[SLI4_BMBX_SIZE];
275 	uint8_t bus, dev, func;
276 	int 	rc;
277 
278 	/* lancer only */
279 	if ((SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) &&
280 	    (SLI4_IF_TYPE_LANCER_G7 != sli_get_if_type(&hw->sli))) {
281 		ocs_log_debug(hw->os, "Function only supported for I/F type 2\n");
282 		return OCS_HW_RTN_ERROR;
283 	}
284 
285 	/*
286 	 * Make sure the FW is new enough to support this command. If the FW
287 	 * is too old, the FW will UE.
288 	 */
289 	if (hw->workaround.disable_dump_loc) {
290 		ocs_log_test(hw->os, "FW version is too old for this feature\n");
291 		return OCS_HW_RTN_ERROR;
292 	}
293 
294 	/* attempt to detemine the dump size for function 0 only. */
295 	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
296 	if (func == 0) {
297 		if (sli_cmd_common_set_dump_location(&hw->sli, buf,
298 							SLI4_BMBX_SIZE, 1, 0, NULL, 0)) {
299 			sli4_res_common_set_dump_location_t *rsp =
300 				(sli4_res_common_set_dump_location_t *)
301 				(buf + offsetof(sli4_cmd_sli_config_t,
302 						payload.embed));
303 
304 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
305 			if (rc != OCS_HW_RTN_SUCCESS) {
306 				ocs_log_test(hw->os, "set dump location command failed\n");
307 				return rc;
308 			} else {
309 				hw->dump_size = rsp->buffer_length;
310 				ocs_log_debug(hw->os, "Dump size %x\n", rsp->buffer_length);
311 			}
312 		}
313 	}
314 	return OCS_HW_RTN_SUCCESS;
315 }
316 
317 /**
318  * @ingroup devInitShutdown
319  * @brief Set up the Hardware Abstraction Layer module.
320  *
321  * @par Description
322  * Calls set up to configure the hardware.
323  *
324  * @param hw Hardware context allocated by the caller.
325  * @param os Device abstraction.
326  * @param port_type Protocol type of port, such as FC and NIC.
327  *
328  * @todo Why is port_type a parameter?
329  *
330  * @return Returns 0 on success, or a non-zero value on failure.
331  */
332 ocs_hw_rtn_e
ocs_hw_setup(ocs_hw_t * hw,ocs_os_handle_t os,sli4_port_type_e port_type)333 ocs_hw_setup(ocs_hw_t *hw, ocs_os_handle_t os, sli4_port_type_e port_type)
334 {
335 	uint32_t i;
336 	char prop_buf[32];
337 
338 	if (hw == NULL) {
339 		ocs_log_err(os, "bad parameter(s) hw=%p\n", hw);
340 		return OCS_HW_RTN_ERROR;
341 	}
342 
343 	if (hw->hw_setup_called) {
344 		/* Setup run-time workarounds.
345 		 * Call for each setup, to allow for hw_war_version
346 		 */
347 		ocs_hw_workaround_setup(hw);
348 		return OCS_HW_RTN_SUCCESS;
349 	}
350 
351 	/*
352 	 * ocs_hw_init() relies on NULL pointers indicating that a structure
353 	 * needs allocation. If a structure is non-NULL, ocs_hw_init() won't
354 	 * free/realloc that memory
355 	 */
356 	ocs_memset(hw, 0, sizeof(ocs_hw_t));
357 
358 	hw->hw_setup_called = TRUE;
359 
360 	hw->os = os;
361 
362 	ocs_lock_init(hw->os, &hw->cmd_lock, "HW_cmd_lock[%d]", ocs_instance(hw->os));
363 	ocs_list_init(&hw->cmd_head, ocs_command_ctx_t, link);
364 	ocs_list_init(&hw->cmd_pending, ocs_command_ctx_t, link);
365 	hw->cmd_head_count = 0;
366 
367 	ocs_lock_init(hw->os, &hw->io_lock, "HW_io_lock[%d]", ocs_instance(hw->os));
368 	ocs_lock_init(hw->os, &hw->io_abort_lock, "HW_io_abort_lock[%d]", ocs_instance(hw->os));
369 
370 	ocs_atomic_init(&hw->io_alloc_failed_count, 0);
371 
372 	hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
373 	hw->config.dif_seed = 0;
374 	hw->config.auto_xfer_rdy_blk_size_chip = OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT;
375 	hw->config.auto_xfer_rdy_ref_tag_is_lba = OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT;
376 	hw->config.auto_xfer_rdy_app_tag_valid =  OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT;
377 	hw->config.auto_xfer_rdy_app_tag_value = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT;
378 
379 
380 	if (sli_setup(&hw->sli, hw->os, port_type)) {
381 		ocs_log_err(hw->os, "SLI setup failed\n");
382 		return OCS_HW_RTN_ERROR;
383 	}
384 
385 	ocs_memset(hw->domains, 0, sizeof(hw->domains));
386 
387 	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
388 
389 	ocs_hw_link_event_init(hw);
390 
391 	sli_callback(&hw->sli, SLI4_CB_LINK, ocs_hw_cb_link, hw);
392 	sli_callback(&hw->sli, SLI4_CB_FIP, ocs_hw_cb_fip, hw);
393 
394 	/*
395 	 * Set all the queue sizes to the maximum allowed. These values may
396 	 * be changes later by the adjust and workaround functions.
397 	 */
398 	for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) {
399 		hw->num_qentries[i] = sli_get_max_qentries(&hw->sli, i);
400 	}
401 
402 	/*
403 	 * The RQ assignment for RQ pair mode.
404 	 */
405 	hw->config.rq_default_buffer_size = OCS_HW_RQ_SIZE_PAYLOAD;
406 	hw->config.n_io = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
407 	if (ocs_get_property("auto_xfer_rdy_xri_cnt", prop_buf, sizeof(prop_buf)) == 0) {
408 		hw->config.auto_xfer_rdy_xri_cnt = ocs_strtoul(prop_buf, 0, 0);
409 	}
410 
411 	/* by default, enable initiator-only auto-ABTS emulation */
412 	hw->config.i_only_aab = TRUE;
413 
414 	/* Setup run-time workarounds */
415 	ocs_hw_workaround_setup(hw);
416 
417 	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
418 	if (hw->workaround.override_fcfi) {
419 		hw->first_domain_idx = -1;
420 	}
421 
422 	/* Must be done after the workaround setup */
423 	if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
424 	    (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))) {
425 
426 		(void)ocs_hw_read_max_dump_size(hw);
427 	}
428 
429 	/* calculate the number of WQs required. */
430 	ocs_hw_adjust_wqs(hw);
431 
432 	/* Set the default dif mode */
433 	if (! sli_is_dif_inline_capable(&hw->sli)) {
434 		ocs_log_test(hw->os, "not inline capable, setting mode to separate\n");
435 		hw->config.dif_mode = OCS_HW_DIF_MODE_SEPARATE;
436 	}
437 	/* Workaround: BZ 161832 */
438 	if (hw->workaround.use_dif_sec_xri) {
439 		ocs_list_init(&hw->sec_hio_wait_list, ocs_hw_io_t, link);
440 	}
441 
442 	/*
443 	 * Figure out the starting and max ULP to spread the WQs across the
444 	 * ULPs.
445 	 */
446 	if (sli_get_is_dual_ulp_capable(&hw->sli)) {
447 		if (sli_get_is_ulp_enabled(&hw->sli, 0) &&
448 		    sli_get_is_ulp_enabled(&hw->sli, 1)) {
449 			hw->ulp_start = 0;
450 			hw->ulp_max   = 1;
451 		} else if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
452 			hw->ulp_start = 0;
453 			hw->ulp_max   = 0;
454 		} else {
455 			hw->ulp_start = 1;
456 			hw->ulp_max   = 1;
457 		}
458 	} else {
459 		if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
460 			hw->ulp_start = 0;
461 			hw->ulp_max   = 0;
462 		} else {
463 			hw->ulp_start = 1;
464 			hw->ulp_max   = 1;
465 		}
466 	}
467 	ocs_log_debug(hw->os, "ulp_start %d, ulp_max %d\n",
468 		hw->ulp_start, hw->ulp_max);
469 	hw->config.queue_topology = hw_global.queue_topology_string;
470 
471 	hw->qtop = ocs_hw_qtop_parse(hw, hw->config.queue_topology);
472 
473 	hw->config.n_eq = hw->qtop->entry_counts[QTOP_EQ];
474 	hw->config.n_cq = hw->qtop->entry_counts[QTOP_CQ];
475 	hw->config.n_rq = hw->qtop->entry_counts[QTOP_RQ];
476 	hw->config.n_wq = hw->qtop->entry_counts[QTOP_WQ];
477 	hw->config.n_mq = hw->qtop->entry_counts[QTOP_MQ];
478 
479 	/* Verify qtop configuration against driver supported configuration */
480 	if (hw->config.n_rq > OCE_HW_MAX_NUM_MRQ_PAIRS) {
481 		ocs_log_crit(hw->os, "Max supported MRQ pairs = %d\n",
482 				OCE_HW_MAX_NUM_MRQ_PAIRS);
483 		return OCS_HW_RTN_ERROR;
484 	}
485 
486 	if (hw->config.n_eq > OCS_HW_MAX_NUM_EQ) {
487 		ocs_log_crit(hw->os, "Max supported EQs = %d\n",
488 				OCS_HW_MAX_NUM_EQ);
489 		return OCS_HW_RTN_ERROR;
490 	}
491 
492 	if (hw->config.n_cq > OCS_HW_MAX_NUM_CQ) {
493 		ocs_log_crit(hw->os, "Max supported CQs = %d\n",
494 				OCS_HW_MAX_NUM_CQ);
495 		return OCS_HW_RTN_ERROR;
496 	}
497 
498 	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
499 		ocs_log_crit(hw->os, "Max supported WQs = %d\n",
500 				OCS_HW_MAX_NUM_WQ);
501 		return OCS_HW_RTN_ERROR;
502 	}
503 
504 	if (hw->config.n_mq > OCS_HW_MAX_NUM_MQ) {
505 		ocs_log_crit(hw->os, "Max supported MQs = %d\n",
506 				OCS_HW_MAX_NUM_MQ);
507 		return OCS_HW_RTN_ERROR;
508 	}
509 
510 	return OCS_HW_RTN_SUCCESS;
511 }
512 
513 /**
514  * @ingroup devInitShutdown
515  * @brief Allocate memory structures to prepare for the device operation.
516  *
517  * @par Description
518  * Allocates memory structures needed by the device and prepares the device
519  * for operation.
520  * @n @n @b Note: This function may be called more than once (for example, at
521  * initialization and then after a reset), but the size of the internal resources
522  * may not be changed without tearing down the HW (ocs_hw_teardown()).
523  *
524  * @param hw Hardware context allocated by the caller.
525  *
526  * @return Returns 0 on success, or a non-zero value on failure.
527  */
528 ocs_hw_rtn_e
ocs_hw_init(ocs_hw_t * hw)529 ocs_hw_init(ocs_hw_t *hw)
530 {
531 	ocs_hw_rtn_e	rc;
532 	uint32_t	i = 0;
533 	uint8_t		buf[SLI4_BMBX_SIZE];
534 	uint32_t	max_rpi;
535 	int		rem_count;
536 	int	        written_size = 0;
537 	uint32_t	count;
538 	char		prop_buf[32];
539 	uint32_t ramdisc_blocksize = 512;
540 	uint32_t q_count = 0;
541 	/*
542 	 * Make sure the command lists are empty. If this is start-of-day,
543 	 * they'll be empty since they were just initialized in ocs_hw_setup.
544 	 * If we've just gone through a reset, the command and command pending
545 	 * lists should have been cleaned up as part of the reset (ocs_hw_reset()).
546 	 */
547 	ocs_lock(&hw->cmd_lock);
548 		if (!ocs_list_empty(&hw->cmd_head)) {
549 			ocs_log_test(hw->os, "command found on cmd list\n");
550 			ocs_unlock(&hw->cmd_lock);
551 			return OCS_HW_RTN_ERROR;
552 		}
553 		if (!ocs_list_empty(&hw->cmd_pending)) {
554 			ocs_log_test(hw->os, "command found on pending list\n");
555 			ocs_unlock(&hw->cmd_lock);
556 			return OCS_HW_RTN_ERROR;
557 		}
558 	ocs_unlock(&hw->cmd_lock);
559 
560 	/* Free RQ buffers if prevously allocated */
561 	ocs_hw_rx_free(hw);
562 
563 	/*
564 	 * The IO queues must be initialized here for the reset case. The
565 	 * ocs_hw_init_io() function will re-add the IOs to the free list.
566 	 * The cmd_head list should be OK since we free all entries in
567 	 * ocs_hw_command_cancel() that is called in the ocs_hw_reset().
568 	 */
569 
570 	/* If we are in this function due to a reset, there may be stale items
571 	 * on lists that need to be removed.  Clean them up.
572 	 */
573 	rem_count=0;
574 	if (ocs_list_valid(&hw->io_wait_free)) {
575 		while ((!ocs_list_empty(&hw->io_wait_free))) {
576 			rem_count++;
577 			ocs_list_remove_head(&hw->io_wait_free);
578 		}
579 		if (rem_count > 0) {
580 			ocs_log_debug(hw->os, "removed %d items from io_wait_free list\n", rem_count);
581 		}
582 	}
583 	rem_count=0;
584 	if (ocs_list_valid(&hw->io_inuse)) {
585 		while ((!ocs_list_empty(&hw->io_inuse))) {
586 			rem_count++;
587 			ocs_list_remove_head(&hw->io_inuse);
588 		}
589 		if (rem_count > 0) {
590 			ocs_log_debug(hw->os, "removed %d items from io_inuse list\n", rem_count);
591 		}
592 	}
593 	rem_count=0;
594 	if (ocs_list_valid(&hw->io_free)) {
595 		while ((!ocs_list_empty(&hw->io_free))) {
596 			rem_count++;
597 			ocs_list_remove_head(&hw->io_free);
598 		}
599 		if (rem_count > 0) {
600 			ocs_log_debug(hw->os, "removed %d items from io_free list\n", rem_count);
601 		}
602 	}
603 	if (ocs_list_valid(&hw->io_port_owned)) {
604 		while ((!ocs_list_empty(&hw->io_port_owned))) {
605 			ocs_list_remove_head(&hw->io_port_owned);
606 		}
607 	}
608 	ocs_list_init(&hw->io_inuse, ocs_hw_io_t, link);
609 	ocs_list_init(&hw->io_free, ocs_hw_io_t, link);
610 	ocs_list_init(&hw->io_port_owned, ocs_hw_io_t, link);
611 	ocs_list_init(&hw->io_wait_free, ocs_hw_io_t, link);
612 	ocs_list_init(&hw->io_timed_wqe, ocs_hw_io_t, wqe_link);
613 	ocs_list_init(&hw->io_port_dnrx, ocs_hw_io_t, dnrx_link);
614 
615 	/* If MRQ not required, Make sure we dont request feature. */
616 	if (hw->config.n_rq == 1) {
617 		hw->sli.config.features.flag.mrqp = FALSE;
618 	}
619 
620 	if (sli_init(&hw->sli)) {
621 		ocs_log_err(hw->os, "SLI failed to initialize\n");
622 		return OCS_HW_RTN_ERROR;
623 	}
624 
625 	/*
626 	 * Enable the auto xfer rdy feature if requested.
627 	 */
628 	hw->auto_xfer_rdy_enabled = FALSE;
629 	if (sli_get_auto_xfer_rdy_capable(&hw->sli) &&
630 	    hw->config.auto_xfer_rdy_size > 0) {
631 		if (hw->config.esoc){
632 			if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
633 				ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
634 			}
635 			written_size = sli_cmd_config_auto_xfer_rdy_hp(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size, 1, ramdisc_blocksize);
636 		} else {
637 			written_size = sli_cmd_config_auto_xfer_rdy(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size);
638 		}
639 		if (written_size) {
640 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
641 			if (rc != OCS_HW_RTN_SUCCESS) {
642 				ocs_log_err(hw->os, "config auto xfer rdy failed\n");
643 				return rc;
644 			}
645 		}
646 		hw->auto_xfer_rdy_enabled = TRUE;
647 
648 		if (hw->config.auto_xfer_rdy_t10_enable) {
649 			rc = ocs_hw_config_auto_xfer_rdy_t10pi(hw, buf);
650 			if (rc != OCS_HW_RTN_SUCCESS) {
651 				ocs_log_err(hw->os, "set parameters auto xfer rdy T10 PI failed\n");
652 				return rc;
653 			}
654 		}
655 	}
656 
657 	if(hw->sliport_healthcheck) {
658 		rc = ocs_hw_config_sli_port_health_check(hw, 0, 1);
659 		if (rc != OCS_HW_RTN_SUCCESS) {
660 			ocs_log_err(hw->os, "Enabling Sliport Health check failed \n");
661 			return rc;
662 		}
663 	}
664 
665 	/*
666 	 * Set FDT transfer hint, only works on Lancer
667 	 */
668 	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) && (OCS_HW_FDT_XFER_HINT != 0)) {
669 		/*
670 		 * Non-fatal error. In particular, we can disregard failure to set OCS_HW_FDT_XFER_HINT on
671 		 * devices with legacy firmware that do not support OCS_HW_FDT_XFER_HINT feature.
672 		 */
673 		ocs_hw_config_set_fdt_xfer_hint(hw, OCS_HW_FDT_XFER_HINT);
674 	}
675 
676 	/*
677 	 * Verify that we have not exceeded any queue sizes
678 	 */
679 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_EQ),
680 					OCS_HW_MAX_NUM_EQ);
681 	if (hw->config.n_eq > q_count) {
682 		ocs_log_err(hw->os, "requested %d EQ but %d allowed\n",
683 			    hw->config.n_eq, q_count);
684 		return OCS_HW_RTN_ERROR;
685 	}
686 
687 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_CQ),
688 					OCS_HW_MAX_NUM_CQ);
689 	if (hw->config.n_cq > q_count) {
690 		ocs_log_err(hw->os, "requested %d CQ but %d allowed\n",
691 			    hw->config.n_cq, q_count);
692 		return OCS_HW_RTN_ERROR;
693 	}
694 
695 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_MQ),
696 					OCS_HW_MAX_NUM_MQ);
697 	if (hw->config.n_mq > q_count) {
698 		ocs_log_err(hw->os, "requested %d MQ but %d allowed\n",
699 			    hw->config.n_mq, q_count);
700 		return OCS_HW_RTN_ERROR;
701 	}
702 
703 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_RQ),
704 					OCS_HW_MAX_NUM_RQ);
705 	if (hw->config.n_rq > q_count) {
706 		ocs_log_err(hw->os, "requested %d RQ but %d allowed\n",
707 			    hw->config.n_rq, q_count);
708 		return OCS_HW_RTN_ERROR;
709 	}
710 
711 	q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ),
712 					OCS_HW_MAX_NUM_WQ);
713 	if (hw->config.n_wq > q_count) {
714 		ocs_log_err(hw->os, "requested %d WQ but %d allowed\n",
715 			    hw->config.n_wq, q_count);
716 		return OCS_HW_RTN_ERROR;
717 	}
718 
719 	/* zero the hashes */
720 	ocs_memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
721 	ocs_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
722 			OCS_HW_MAX_NUM_CQ, OCS_HW_Q_HASH_SIZE);
723 
724 	ocs_memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
725 	ocs_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
726 			OCS_HW_MAX_NUM_RQ, OCS_HW_Q_HASH_SIZE);
727 
728 	ocs_memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
729 	ocs_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
730 			OCS_HW_MAX_NUM_WQ, OCS_HW_Q_HASH_SIZE);
731 
732 
733 	rc = ocs_hw_init_queues(hw, hw->qtop);
734 	if (rc != OCS_HW_RTN_SUCCESS) {
735 		return rc;
736 	}
737 
738 	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
739 	i = sli_fc_get_rpi_requirements(&hw->sli, max_rpi);
740 	if (i) {
741 		ocs_dma_t payload_memory;
742 
743 		rc = OCS_HW_RTN_ERROR;
744 
745 		if (hw->rnode_mem.size) {
746 			ocs_dma_free(hw->os, &hw->rnode_mem);
747 		}
748 
749 		if (ocs_dma_alloc(hw->os, &hw->rnode_mem, i, 4096)) {
750 			ocs_log_err(hw->os, "remote node memory allocation fail\n");
751 			return OCS_HW_RTN_NO_MEMORY;
752 		}
753 
754 		payload_memory.size = 0;
755 		if (sli_cmd_fcoe_post_hdr_templates(&hw->sli, buf, SLI4_BMBX_SIZE,
756 					&hw->rnode_mem, UINT16_MAX, &payload_memory)) {
757 			rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
758 
759 			if (payload_memory.size != 0) {
760 				/* The command was non-embedded - need to free the dma buffer */
761 				ocs_dma_free(hw->os, &payload_memory);
762 			}
763 		}
764 
765 		if (rc != OCS_HW_RTN_SUCCESS) {
766 			ocs_log_err(hw->os, "header template registration failed\n");
767 			return rc;
768 		}
769 	}
770 
771 	/* Allocate and post RQ buffers */
772 	rc = ocs_hw_rx_allocate(hw);
773 	if (rc) {
774 		ocs_log_err(hw->os, "rx_allocate failed\n");
775 		return rc;
776 	}
777 
778 	/* Populate hw->seq_free_list */
779 	if (hw->seq_pool == NULL) {
780 		uint32_t count = 0;
781 		uint32_t i;
782 
783 		/* Sum up the total number of RQ entries, to use to allocate the sequence object pool */
784 		for (i = 0; i < hw->hw_rq_count; i++) {
785 			count += hw->hw_rq[i]->entry_count;
786 		}
787 
788 		hw->seq_pool = ocs_array_alloc(hw->os, sizeof(ocs_hw_sequence_t), count);
789 		if (hw->seq_pool == NULL) {
790 			ocs_log_err(hw->os, "malloc seq_pool failed\n");
791 			return OCS_HW_RTN_NO_MEMORY;
792 		}
793 	}
794 
795 	if(ocs_hw_rx_post(hw)) {
796 		ocs_log_err(hw->os, "WARNING - error posting RQ buffers\n");
797 	}
798 
799 	/* Allocate rpi_ref if not previously allocated */
800 	if (hw->rpi_ref == NULL) {
801 		hw->rpi_ref = ocs_malloc(hw->os, max_rpi * sizeof(*hw->rpi_ref),
802 					  OCS_M_ZERO | OCS_M_NOWAIT);
803 		if (hw->rpi_ref == NULL) {
804 			ocs_log_err(hw->os, "rpi_ref allocation failure (%d)\n", i);
805 			return OCS_HW_RTN_NO_MEMORY;
806 		}
807 	}
808 
809 	for (i = 0; i < max_rpi; i ++) {
810 		ocs_atomic_init(&hw->rpi_ref[i].rpi_count, 0);
811 		ocs_atomic_init(&hw->rpi_ref[i].rpi_attached, 0);
812 	}
813 
814 	ocs_memset(hw->domains, 0, sizeof(hw->domains));
815 
816 	/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
817 	if (hw->workaround.override_fcfi) {
818 		hw->first_domain_idx = -1;
819 	}
820 
821 	ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
822 
823 	/* Register a FCFI to allow unsolicited frames to be routed to the driver */
824 	if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
825 
826 		if (hw->hw_mrq_count) {
827 			ocs_log_debug(hw->os, "using REG_FCFI MRQ\n");
828 
829 			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0, 0);
830 			if (rc != OCS_HW_RTN_SUCCESS) {
831 				ocs_log_err(hw->os, "REG_FCFI_MRQ FCFI registration failed\n");
832 				return rc;
833 			}
834 
835 			rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0, 0);
836 			if (rc != OCS_HW_RTN_SUCCESS) {
837 				ocs_log_err(hw->os, "REG_FCFI_MRQ MRQ registration failed\n");
838 				return rc;
839 			}
840 		} else {
841 			sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
842 
843 			ocs_log_debug(hw->os, "using REG_FCFI standard\n");
844 
845 			/* Set the filter match/mask values from hw's filter_def values */
846 			for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
847 				rq_cfg[i].rq_id = 0xffff;
848 				rq_cfg[i].r_ctl_mask =	(uint8_t)  hw->config.filter_def[i];
849 				rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
850 				rq_cfg[i].type_mask =	(uint8_t) (hw->config.filter_def[i] >> 16);
851 				rq_cfg[i].type_match =	(uint8_t) (hw->config.filter_def[i] >> 24);
852 			}
853 
854 			/*
855 			 * Update the rq_id's of the FCF configuration (don't update more than the number
856 			 * of rq_cfg elements)
857 			 */
858 			for (i = 0; i < OCS_MIN(hw->hw_rq_count, SLI4_CMD_REG_FCFI_NUM_RQ_CFG); i++) {
859 				hw_rq_t *rq = hw->hw_rq[i];
860 				uint32_t j;
861 				for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
862 					uint32_t mask = (rq->filter_mask != 0) ? rq->filter_mask : 1;
863 					if (mask & (1U << j)) {
864 						rq_cfg[j].rq_id = rq->hdr->id;
865 						ocs_log_debug(hw->os, "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
866 							j, hw->config.filter_def[j], i, rq->hdr->id);
867 					}
868 				}
869 			}
870 
871 			rc = OCS_HW_RTN_ERROR;
872 
873 			if (sli_cmd_reg_fcfi(&hw->sli, buf, SLI4_BMBX_SIZE, 0, rq_cfg, 0)) {
874 				rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
875 			}
876 
877 			if (rc != OCS_HW_RTN_SUCCESS) {
878 				ocs_log_err(hw->os, "FCFI registration failed\n");
879 				return rc;
880 			}
881 			hw->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)buf)->fcfi;
882 		}
883 
884 	}
885 
886 	/*
887 	 * Allocate the WQ request tag pool, if not previously allocated (the request tag value is 16 bits,
888 	 * thus the pool allocation size of 64k)
889 	 */
890 	rc = ocs_hw_reqtag_init(hw);
891 	if (rc) {
892 		ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed: %d\n", rc);
893 		return rc;
894 	}
895 
896 	rc = ocs_hw_setup_io(hw);
897 	if (rc) {
898 		ocs_log_err(hw->os, "IO allocation failure\n");
899 		return rc;
900 	}
901 
902 	rc = ocs_hw_init_io(hw);
903 	if (rc) {
904 		ocs_log_err(hw->os, "IO initialization failure\n");
905 		return rc;
906 	}
907 
908 	ocs_queue_history_init(hw->os, &hw->q_hist);
909 
910 	/* get hw link config; polling, so callback will be called immediately */
911 	hw->linkcfg = OCS_HW_LINKCFG_NA;
912 	ocs_hw_get_linkcfg(hw, OCS_CMD_POLL, ocs_hw_init_linkcfg_cb, hw);
913 
914 	/* if lancer ethernet, ethernet ports need to be enabled */
915 	if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) &&
916 	    (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_ETHERNET)) {
917 		if (ocs_hw_set_eth_license(hw, hw->eth_license)) {
918 			/* log warning but continue */
919 			ocs_log_err(hw->os, "Failed to set ethernet license\n");
920 		}
921 	}
922 
923 	/* Set the DIF seed - only for lancer right now */
924 	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli) &&
925 	    ocs_hw_set_dif_seed(hw) != OCS_HW_RTN_SUCCESS) {
926 		ocs_log_err(hw->os, "Failed to set DIF seed value\n");
927 		return rc;
928 	}
929 
930 	/* Set the DIF mode - skyhawk only */
931 	if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli) &&
932 	    sli_get_dif_capable(&hw->sli)) {
933 		rc = ocs_hw_set_dif_mode(hw);
934 		if (rc != OCS_HW_RTN_SUCCESS) {
935 			ocs_log_err(hw->os, "Failed to set DIF mode value\n");
936 			return rc;
937 		}
938 	}
939 
940 	/*
941 	 * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ entries
942 	 */
943 	for (i = 0; i < hw->eq_count; i++) {
944 		sli_queue_arm(&hw->sli, &hw->eq[i], TRUE);
945 	}
946 
947 	/*
948 	 * Initialize RQ hash
949 	 */
950 	for (i = 0; i < hw->rq_count; i++) {
951 		ocs_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
952 	}
953 
954 	/*
955 	 * Initialize WQ hash
956 	 */
957 	for (i = 0; i < hw->wq_count; i++) {
958 		ocs_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
959 	}
960 
961 	/*
962 	 * Arming the CQ allows (e.g.) MQ completions to write CQ entries
963 	 */
964 	for (i = 0; i < hw->cq_count; i++) {
965 		ocs_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
966 		sli_queue_arm(&hw->sli, &hw->cq[i], TRUE);
967 	}
968 
969 	/* record the fact that the queues are functional */
970 	hw->state = OCS_HW_STATE_ACTIVE;
971 
972 	/* Note: Must be after the IOs are setup and the state is active*/
973 	if (ocs_hw_rqpair_init(hw)) {
974 		ocs_log_err(hw->os, "WARNING - error initializing RQ pair\n");
975 	}
976 
977 	/* finally kick off periodic timer to check for timed out target WQEs */
978 	if (hw->config.emulate_tgt_wqe_timeout) {
979 		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw,
980 				OCS_HW_WQ_TIMER_PERIOD_MS);
981 	}
982 
983 	/*
984 	 * Allocate a HW IOs for send frame.  Allocate one for each Class 1 WQ, or if there
985 	 * are none of those, allocate one for WQ[0]
986 	 */
987 	if ((count = ocs_varray_get_count(hw->wq_class_array[1])) > 0) {
988 		for (i = 0; i < count; i++) {
989 			hw_wq_t *wq = ocs_varray_iter_next(hw->wq_class_array[1]);
990 			wq->send_frame_io = ocs_hw_io_alloc(hw);
991 			if (wq->send_frame_io == NULL) {
992 				ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
993 			}
994 		}
995 	} else {
996 		hw->hw_wq[0]->send_frame_io = ocs_hw_io_alloc(hw);
997 		if (hw->hw_wq[0]->send_frame_io == NULL) {
998 			ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
999 		}
1000 	}
1001 
1002 	/* Initialize send frame frame sequence id */
1003 	ocs_atomic_init(&hw->send_frame_seq_id, 0);
1004 
1005 	/* Initialize watchdog timer if enabled by user */
1006 	hw->expiration_logged = 0;
1007 	if(hw->watchdog_timeout) {
1008 		if((hw->watchdog_timeout < 1) || (hw->watchdog_timeout > 65534)) {
1009 			ocs_log_err(hw->os, "watchdog_timeout out of range: Valid range is 1 - 65534\n");
1010 		}else if(!ocs_hw_config_watchdog_timer(hw)) {
1011 			ocs_log_info(hw->os, "watchdog timer configured with timeout = %d seconds \n", hw->watchdog_timeout);
1012 		}
1013 	}
1014 
1015 	if (ocs_dma_alloc(hw->os, &hw->domain_dmem, 112, 4)) {
1016 	   ocs_log_err(hw->os, "domain node memory allocation fail\n");
1017 	   return OCS_HW_RTN_NO_MEMORY;
1018 	}
1019 
1020 	if (ocs_dma_alloc(hw->os, &hw->fcf_dmem, OCS_HW_READ_FCF_SIZE, OCS_HW_READ_FCF_SIZE)) {
1021 	   ocs_log_err(hw->os, "domain fcf memory allocation fail\n");
1022 	   return OCS_HW_RTN_NO_MEMORY;
1023 	}
1024 
1025 	if ((0 == hw->loop_map.size) &&	ocs_dma_alloc(hw->os, &hw->loop_map,
1026 				SLI4_MIN_LOOP_MAP_BYTES, 4)) {
1027 		ocs_log_err(hw->os, "Loop dma alloc failed size:%d \n", hw->loop_map.size);
1028 	}
1029 
1030 	return OCS_HW_RTN_SUCCESS;
1031 }
1032 
1033 /**
1034  * @brief Configure Multi-RQ
1035  *
1036  * @param hw	Hardware context allocated by the caller.
1037  * @param mode	1 to set MRQ filters and 0 to set FCFI index
1038  * @param vlanid    valid in mode 0
1039  * @param fcf_index valid in mode 0
1040  *
1041  * @return Returns 0 on success, or a non-zero value on failure.
1042  */
1043 static int32_t
ocs_hw_config_mrq(ocs_hw_t * hw,uint8_t mode,uint16_t vlanid,uint16_t fcf_index)1044 ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t mode, uint16_t vlanid, uint16_t fcf_index)
1045 {
1046 	uint8_t buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
1047 	hw_rq_t *rq;
1048 	sli4_cmd_reg_fcfi_mrq_t *rsp = NULL;
1049 	uint32_t i, j;
1050 	sli4_cmd_rq_cfg_t rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
1051 	int32_t rc;
1052 
1053 	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1054 		goto issue_cmd;
1055 	}
1056 
1057 	/* Set the filter match/mask values from hw's filter_def values */
1058 	for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
1059 		rq_filter[i].rq_id = 0xffff;
1060 		rq_filter[i].r_ctl_mask  = (uint8_t)  hw->config.filter_def[i];
1061 		rq_filter[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
1062 		rq_filter[i].type_mask   = (uint8_t) (hw->config.filter_def[i] >> 16);
1063 		rq_filter[i].type_match  = (uint8_t) (hw->config.filter_def[i] >> 24);
1064 	}
1065 
1066 	/* Accumulate counts for each filter type used, build rq_ids[] list */
1067 	for (i = 0; i < hw->hw_rq_count; i++) {
1068 		rq = hw->hw_rq[i];
1069 		for (j = 0; j < SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG; j++) {
1070 			if (rq->filter_mask & (1U << j)) {
1071 				if (rq_filter[j].rq_id != 0xffff) {
1072 					/* Already used. Bailout ifts not RQset case */
1073 					if (!rq->is_mrq || (rq_filter[j].rq_id != rq->base_mrq_id)) {
1074 						ocs_log_err(hw->os, "Wrong queue topology.\n");
1075 						return OCS_HW_RTN_ERROR;
1076 					}
1077 					continue;
1078 				}
1079 
1080 				if (rq->is_mrq) {
1081 					rq_filter[j].rq_id = rq->base_mrq_id;
1082 					mrq_bitmask |= (1U << j);
1083 				} else {
1084 					rq_filter[j].rq_id = rq->hdr->id;
1085 				}
1086 			}
1087 		}
1088 	}
1089 
1090 issue_cmd:
1091 	/* Invoke REG_FCFI_MRQ */
1092 	rc = sli_cmd_reg_fcfi_mrq(&hw->sli,
1093 				 buf,					/* buf */
1094 				 SLI4_BMBX_SIZE,			/* size */
1095 				 mode,					/* mode 1 */
1096 				 fcf_index,				/* fcf_index */
1097 				 vlanid,				/* vlan_id */
1098 				 hw->config.rq_selection_policy,	/* RQ selection policy*/
1099 				 mrq_bitmask,				/* MRQ bitmask */
1100 				 hw->hw_mrq_count,			/* num_mrqs */
1101 				 rq_filter);				/* RQ filter */
1102 	if (rc == 0) {
1103 		ocs_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed: %d\n", rc);
1104 		return OCS_HW_RTN_ERROR;
1105 	}
1106 
1107 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
1108 
1109 	rsp = (sli4_cmd_reg_fcfi_mrq_t *)buf;
1110 
1111 	if ((rc != OCS_HW_RTN_SUCCESS) || (rsp->hdr.status)) {
1112 		ocs_log_err(hw->os, "FCFI MRQ registration failed. cmd = %x status = %x\n",
1113 			    rsp->hdr.command, rsp->hdr.status);
1114 		return OCS_HW_RTN_ERROR;
1115 	}
1116 
1117 	if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1118 		hw->fcf_indicator = rsp->fcfi;
1119 	}
1120 	return 0;
1121 }
1122 
1123 /**
1124  * @brief Callback function for getting linkcfg during HW initialization.
1125  *
1126  * @param status Status of the linkcfg get operation.
1127  * @param value Link configuration enum to which the link configuration is set.
1128  * @param arg Callback argument (ocs_hw_t *).
1129  *
1130  * @return None.
1131  */
1132 static void
ocs_hw_init_linkcfg_cb(int32_t status,uintptr_t value,void * arg)1133 ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
1134 {
1135 	ocs_hw_t *hw = (ocs_hw_t *)arg;
1136 	if (status == 0) {
1137 		hw->linkcfg = (ocs_hw_linkcfg_e)value;
1138 	} else {
1139 		hw->linkcfg = OCS_HW_LINKCFG_NA;
1140 	}
1141 	ocs_log_debug(hw->os, "linkcfg=%d\n", hw->linkcfg);
1142 }
1143 
1144 /**
1145  * @ingroup devInitShutdown
1146  * @brief Tear down the Hardware Abstraction Layer module.
1147  *
1148  * @par Description
1149  * Frees memory structures needed by the device, and shuts down the device. Does
1150  * not free the HW context memory (which is done by the caller).
1151  *
1152  * @param hw Hardware context allocated by the caller.
1153  *
1154  * @return Returns 0 on success, or a non-zero value on failure.
1155  */
1156 ocs_hw_rtn_e
ocs_hw_teardown(ocs_hw_t * hw)1157 ocs_hw_teardown(ocs_hw_t *hw)
1158 {
1159 	uint32_t	i = 0;
1160 	uint32_t	iters = 10;/*XXX*/
1161 	uint32_t	max_rpi;
1162 	uint32_t destroy_queues;
1163 	uint32_t free_memory;
1164 
1165 	if (!hw) {
1166 		ocs_log_err(NULL, "bad parameter(s) hw=%p\n", hw);
1167 		return OCS_HW_RTN_ERROR;
1168 	}
1169 
1170 	destroy_queues = (hw->state == OCS_HW_STATE_ACTIVE);
1171 	free_memory = (hw->state != OCS_HW_STATE_UNINITIALIZED);
1172 
1173 	/* shutdown target wqe timer */
1174 	shutdown_target_wqe_timer(hw);
1175 
1176 	/* Cancel watchdog timer if enabled */
1177 	if(hw->watchdog_timeout) {
1178 		hw->watchdog_timeout = 0;
1179 		ocs_hw_config_watchdog_timer(hw);
1180 	}
1181 
1182 	/* Cancel Sliport Healthcheck */
1183 	if(hw->sliport_healthcheck) {
1184 		hw->sliport_healthcheck = 0;
1185 		ocs_hw_config_sli_port_health_check(hw, 0, 0);
1186 	}
1187 
1188 	if (hw->state != OCS_HW_STATE_QUEUES_ALLOCATED) {
1189 
1190 		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1191 
1192 		ocs_hw_flush(hw);
1193 
1194 		/* If there are outstanding commands, wait for them to complete */
1195 		while (!ocs_list_empty(&hw->cmd_head) && iters) {
1196 			ocs_udelay(10000);
1197 			ocs_hw_flush(hw);
1198 			iters--;
1199 		}
1200 
1201 		if (ocs_list_empty(&hw->cmd_head)) {
1202 			ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1203 		} else {
1204 			ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1205 		}
1206 
1207 		/* Cancel any remaining commands */
1208 		ocs_hw_command_cancel(hw);
1209 	} else {
1210 		hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1211 	}
1212 
1213 	ocs_lock_free(&hw->cmd_lock);
1214 
1215 	/* Free unregistered RPI if workaround is in force */
1216 	if (hw->workaround.use_unregistered_rpi) {
1217 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, hw->workaround.unregistered_rid);
1218 	}
1219 
1220 	max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1221 	if (hw->rpi_ref) {
1222 		for (i = 0; i < max_rpi; i++) {
1223 			if (ocs_atomic_read(&hw->rpi_ref[i].rpi_count)) {
1224 				ocs_log_debug(hw->os, "non-zero ref [%d]=%d\n",
1225 						i, ocs_atomic_read(&hw->rpi_ref[i].rpi_count));
1226 			}
1227 		}
1228 		ocs_free(hw->os, hw->rpi_ref, max_rpi * sizeof(*hw->rpi_ref));
1229 		hw->rpi_ref = NULL;
1230 	}
1231 
1232 	ocs_dma_free(hw->os, &hw->rnode_mem);
1233 
1234 	if (hw->io) {
1235 		for (i = 0; i < hw->config.n_io; i++) {
1236 			if (hw->io[i] && (hw->io[i]->sgl != NULL) &&
1237 			    (hw->io[i]->sgl->virt != NULL)) {
1238 				if(hw->io[i]->is_port_owned) {
1239 					ocs_lock_free(&hw->io[i]->axr_lock);
1240 				}
1241 				ocs_dma_free(hw->os, hw->io[i]->sgl);
1242 			}
1243 			ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
1244 			hw->io[i] = NULL;
1245 		}
1246 		ocs_free(hw->os, hw->wqe_buffs, hw->config.n_io * hw->sli.config.wqe_size);
1247 		hw->wqe_buffs = NULL;
1248 		ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t *));
1249 		hw->io = NULL;
1250 	}
1251 
1252 	ocs_dma_free(hw->os, &hw->xfer_rdy);
1253 	ocs_dma_free(hw->os, &hw->dump_sges);
1254 	ocs_dma_free(hw->os, &hw->loop_map);
1255 
1256 	ocs_lock_free(&hw->io_lock);
1257 	ocs_lock_free(&hw->io_abort_lock);
1258 
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 
1265 	for (i = 0; i < hw->rq_count; i++) {
1266 		sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, free_memory);
1267 	}
1268 
1269 	for (i = 0; i < hw->mq_count; i++) {
1270 		sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, free_memory);
1271 	}
1272 
1273 	for (i = 0; i < hw->cq_count; i++) {
1274 		sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, free_memory);
1275 	}
1276 
1277 	for (i = 0; i < hw->eq_count; i++) {
1278 		sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, free_memory);
1279 	}
1280 
1281 	ocs_hw_qtop_free(hw->qtop);
1282 
1283 	/* Free rq buffers */
1284 	ocs_hw_rx_free(hw);
1285 
1286 	hw_queue_teardown(hw);
1287 
1288 	ocs_hw_rqpair_teardown(hw);
1289 
1290 	if (sli_teardown(&hw->sli)) {
1291 		ocs_log_err(hw->os, "SLI teardown failed\n");
1292 	}
1293 
1294 	ocs_queue_history_free(&hw->q_hist);
1295 
1296 	/* record the fact that the queues are non-functional */
1297 	hw->state = OCS_HW_STATE_UNINITIALIZED;
1298 
1299 	/* free sequence free pool */
1300 	ocs_array_free(hw->seq_pool);
1301 	hw->seq_pool = NULL;
1302 
1303 	/* free hw_wq_callback pool */
1304 	ocs_pool_free(hw->wq_reqtag_pool);
1305 
1306 	ocs_dma_free(hw->os, &hw->domain_dmem);
1307 	ocs_dma_free(hw->os, &hw->fcf_dmem);
1308 	/* Mark HW setup as not having been called */
1309 	hw->hw_setup_called = FALSE;
1310 
1311 	return OCS_HW_RTN_SUCCESS;
1312 }
1313 
1314 ocs_hw_rtn_e
ocs_hw_reset(ocs_hw_t * hw,ocs_hw_reset_e reset)1315 ocs_hw_reset(ocs_hw_t *hw, ocs_hw_reset_e reset)
1316 {
1317 	uint32_t	i;
1318 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1319 	uint32_t	iters;
1320 	ocs_hw_state_e prev_state = hw->state;
1321 
1322 	if (hw->state != OCS_HW_STATE_ACTIVE) {
1323 		ocs_log_test(hw->os, "HW state %d is not active\n", hw->state);
1324 	}
1325 
1326 	hw->state = OCS_HW_STATE_RESET_IN_PROGRESS;
1327 
1328 	/* shutdown target wqe timer */
1329 	shutdown_target_wqe_timer(hw);
1330 
1331 	ocs_hw_flush(hw);
1332 
1333 	/*
1334 	 * If an mailbox command requiring a DMA is outstanding (i.e. SFP/DDM),
1335 	 * then the FW will UE when the reset is issued. So attempt to complete
1336 	 * all mailbox commands.
1337 	 */
1338 	iters = 10;
1339 	while (!ocs_list_empty(&hw->cmd_head) && iters) {
1340 		ocs_udelay(10000);
1341 		ocs_hw_flush(hw);
1342 		iters--;
1343 	}
1344 
1345 	if (ocs_list_empty(&hw->cmd_head)) {
1346 		ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1347 	} else {
1348 		ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1349 	}
1350 
1351 	/* Reset the chip */
1352 	switch(reset) {
1353 	case OCS_HW_RESET_FUNCTION:
1354 		ocs_log_debug(hw->os, "issuing function level reset\n");
1355 		if (sli_reset(&hw->sli)) {
1356 			ocs_log_err(hw->os, "sli_reset failed\n");
1357 			rc = OCS_HW_RTN_ERROR;
1358 		}
1359 		break;
1360 	case OCS_HW_RESET_FIRMWARE:
1361 		ocs_log_debug(hw->os, "issuing firmware reset\n");
1362 		if (sli_fw_reset(&hw->sli)) {
1363 			ocs_log_err(hw->os, "sli_soft_reset failed\n");
1364 			rc = OCS_HW_RTN_ERROR;
1365 		}
1366 		/*
1367 		 * Because the FW reset leaves the FW in a non-running state,
1368 		 * follow that with a regular reset.
1369 		 */
1370 		ocs_log_debug(hw->os, "issuing function level reset\n");
1371 		if (sli_reset(&hw->sli)) {
1372 			ocs_log_err(hw->os, "sli_reset failed\n");
1373 			rc = OCS_HW_RTN_ERROR;
1374 		}
1375 		break;
1376 	default:
1377 		ocs_log_test(hw->os, "unknown reset type - no reset performed\n");
1378 		hw->state = prev_state;
1379 		return OCS_HW_RTN_ERROR;
1380 	}
1381 
1382 	/* Not safe to walk command/io lists unless they've been initialized */
1383 	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1384 		ocs_hw_command_cancel(hw);
1385 
1386 		/* Clean up the inuse list, the free list and the wait free list */
1387 		ocs_hw_io_cancel(hw);
1388 
1389 		ocs_memset(hw->domains, 0, sizeof(hw->domains));
1390 		ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
1391 
1392 		ocs_hw_link_event_init(hw);
1393 
1394 		ocs_lock(&hw->io_lock);
1395 			/* The io lists should be empty, but remove any that didn't get cleaned up. */
1396 			while (!ocs_list_empty(&hw->io_timed_wqe)) {
1397 				ocs_list_remove_head(&hw->io_timed_wqe);
1398 			}
1399 			/* Don't clean up the io_inuse list, the backend will do that when it finishes the IO */
1400 
1401 			while (!ocs_list_empty(&hw->io_free)) {
1402 				ocs_list_remove_head(&hw->io_free);
1403 			}
1404 			while (!ocs_list_empty(&hw->io_wait_free)) {
1405 				ocs_list_remove_head(&hw->io_wait_free);
1406 			}
1407 
1408 			/* Reset the request tag pool, the HW IO request tags are reassigned in ocs_hw_setup_io() */
1409 			ocs_hw_reqtag_reset(hw);
1410 
1411 		ocs_unlock(&hw->io_lock);
1412 	}
1413 
1414 	if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1415 		for (i = 0; i < hw->wq_count; i++) {
1416 			sli_queue_reset(&hw->sli, &hw->wq[i]);
1417 		}
1418 
1419 		for (i = 0; i < hw->rq_count; i++) {
1420 			sli_queue_reset(&hw->sli, &hw->rq[i]);
1421 		}
1422 
1423 		for (i = 0; i < hw->hw_rq_count; i++) {
1424 			hw_rq_t *rq = hw->hw_rq[i];
1425 			if (rq->rq_tracker != NULL) {
1426 				uint32_t j;
1427 
1428 				for (j = 0; j < rq->entry_count; j++) {
1429 					rq->rq_tracker[j] = NULL;
1430 				}
1431 			}
1432 		}
1433 
1434 		for (i = 0; i < hw->mq_count; i++) {
1435 			sli_queue_reset(&hw->sli, &hw->mq[i]);
1436 		}
1437 
1438 		for (i = 0; i < hw->cq_count; i++) {
1439 			sli_queue_reset(&hw->sli, &hw->cq[i]);
1440 		}
1441 
1442 		for (i = 0; i < hw->eq_count; i++) {
1443 			sli_queue_reset(&hw->sli, &hw->eq[i]);
1444 		}
1445 
1446 		/* Free rq buffers */
1447 		ocs_hw_rx_free(hw);
1448 
1449 		/* Teardown the HW queue topology */
1450 		hw_queue_teardown(hw);
1451 	} else {
1452 
1453 		/* Free rq buffers */
1454 		ocs_hw_rx_free(hw);
1455 	}
1456 
1457 	/*
1458 	 * Re-apply the run-time workarounds after clearing the SLI config
1459 	 * fields in sli_reset.
1460 	 */
1461 	ocs_hw_workaround_setup(hw);
1462 	hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
1463 
1464 	return rc;
1465 }
1466 
1467 int32_t
ocs_hw_get_num_eq(ocs_hw_t * hw)1468 ocs_hw_get_num_eq(ocs_hw_t *hw)
1469 {
1470 	return hw->eq_count;
1471 }
1472 
1473 static int32_t
ocs_hw_get_fw_timed_out(ocs_hw_t * hw)1474 ocs_hw_get_fw_timed_out(ocs_hw_t *hw)
1475 {
1476 	/* The error values below are taken from LOWLEVEL_SET_WATCHDOG_TIMER_rev1.pdf
1477 	* No further explanation is given in the document.
1478 	* */
1479 	return (sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1) == 0x2 &&
1480 		sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2) == 0x10);
1481 }
1482 
1483 
1484 ocs_hw_rtn_e
ocs_hw_get(ocs_hw_t * hw,ocs_hw_property_e prop,uint32_t * value)1485 ocs_hw_get(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t *value)
1486 {
1487 	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1488 	int32_t			tmp;
1489 
1490 	if (!value) {
1491 		return OCS_HW_RTN_ERROR;
1492 	}
1493 
1494 	*value = 0;
1495 
1496 	switch (prop) {
1497 	case OCS_HW_N_IO:
1498 		*value = hw->config.n_io;
1499 		break;
1500 	case OCS_HW_N_SGL:
1501 		*value = (hw->config.n_sgl - SLI4_SGE_MAX_RESERVED);
1502 		break;
1503 	case OCS_HW_MAX_IO:
1504 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
1505 		break;
1506 	case OCS_HW_MAX_NODES:
1507 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1508 		break;
1509 	case OCS_HW_MAX_RQ_ENTRIES:
1510 		*value = hw->num_qentries[SLI_QTYPE_RQ];
1511 		break;
1512 	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1513 		*value = hw->config.rq_default_buffer_size;
1514 		break;
1515 	case OCS_HW_AUTO_XFER_RDY_CAPABLE:
1516 		*value = sli_get_auto_xfer_rdy_capable(&hw->sli);
1517 		break;
1518 	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1519 		*value = hw->config.auto_xfer_rdy_xri_cnt;
1520 		break;
1521 	case OCS_HW_AUTO_XFER_RDY_SIZE:
1522 		*value = hw->config.auto_xfer_rdy_size;
1523 		break;
1524 	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1525 		switch (hw->config.auto_xfer_rdy_blk_size_chip) {
1526 		case 0:
1527 			*value = 512;
1528 			break;
1529 		case 1:
1530 			*value = 1024;
1531 			break;
1532 		case 2:
1533 			*value = 2048;
1534 			break;
1535 		case 3:
1536 			*value = 4096;
1537 			break;
1538 		case 4:
1539 			*value = 520;
1540 			break;
1541 		default:
1542 			*value = 0;
1543 			rc = OCS_HW_RTN_ERROR;
1544 			break;
1545 		}
1546 		break;
1547 	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1548 		*value = hw->config.auto_xfer_rdy_t10_enable;
1549 		break;
1550 	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1551 		*value = hw->config.auto_xfer_rdy_p_type;
1552 		break;
1553 	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1554 		*value = hw->config.auto_xfer_rdy_ref_tag_is_lba;
1555 		break;
1556 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1557 		*value = hw->config.auto_xfer_rdy_app_tag_valid;
1558 		break;
1559 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1560 		*value = hw->config.auto_xfer_rdy_app_tag_value;
1561 		break;
1562 	case OCS_HW_MAX_SGE:
1563 		*value = sli_get_max_sge(&hw->sli);
1564 		break;
1565 	case OCS_HW_MAX_SGL:
1566 		*value = sli_get_max_sgl(&hw->sli);
1567 		break;
1568 	case OCS_HW_TOPOLOGY:
1569 		/*
1570 		 * Infer link.status based on link.speed.
1571 		 * Report OCS_HW_TOPOLOGY_NONE if the link is down.
1572 		 */
1573 		if (hw->link.speed == 0) {
1574 			*value = OCS_HW_TOPOLOGY_NONE;
1575 			break;
1576 		}
1577 		switch (hw->link.topology) {
1578 		case SLI_LINK_TOPO_NPORT:
1579 			*value = OCS_HW_TOPOLOGY_NPORT;
1580 			break;
1581 		case SLI_LINK_TOPO_LOOP:
1582 			*value = OCS_HW_TOPOLOGY_LOOP;
1583 			break;
1584 		case SLI_LINK_TOPO_NONE:
1585 			*value = OCS_HW_TOPOLOGY_NONE;
1586 			break;
1587 		default:
1588 			ocs_log_test(hw->os, "unsupported topology %#x\n", hw->link.topology);
1589 			rc = OCS_HW_RTN_ERROR;
1590 			break;
1591 		}
1592 		break;
1593 	case OCS_HW_CONFIG_TOPOLOGY:
1594 		*value = hw->config.topology;
1595 		break;
1596 	case OCS_HW_LINK_SPEED:
1597 		*value = hw->link.speed;
1598 		break;
1599 	case OCS_HW_LINK_CONFIG_SPEED:
1600 		switch (hw->config.speed) {
1601 		case FC_LINK_SPEED_10G:
1602 			*value = 10000;
1603 			break;
1604 		case FC_LINK_SPEED_AUTO_16_8_4:
1605 			*value = 0;
1606 			break;
1607 		case FC_LINK_SPEED_2G:
1608 			*value = 2000;
1609 			break;
1610 		case FC_LINK_SPEED_4G:
1611 			*value = 4000;
1612 			break;
1613 		case FC_LINK_SPEED_8G:
1614 			*value = 8000;
1615 			break;
1616 		case FC_LINK_SPEED_16G:
1617 			*value = 16000;
1618 			break;
1619 		case FC_LINK_SPEED_32G:
1620 			*value = 32000;
1621 			break;
1622 		default:
1623 			ocs_log_test(hw->os, "unsupported speed %#x\n", hw->config.speed);
1624 			rc = OCS_HW_RTN_ERROR;
1625 			break;
1626 		}
1627 		break;
1628 	case OCS_HW_IF_TYPE:
1629 		*value = sli_get_if_type(&hw->sli);
1630 		break;
1631 	case OCS_HW_SLI_REV:
1632 		*value = sli_get_sli_rev(&hw->sli);
1633 		break;
1634 	case OCS_HW_SLI_FAMILY:
1635 		*value = sli_get_sli_family(&hw->sli);
1636 		break;
1637 	case OCS_HW_DIF_CAPABLE:
1638 		*value = sli_get_dif_capable(&hw->sli);
1639 		break;
1640 	case OCS_HW_DIF_SEED:
1641 		*value = hw->config.dif_seed;
1642 		break;
1643 	case OCS_HW_DIF_MODE:
1644 		*value = hw->config.dif_mode;
1645 		break;
1646 	case OCS_HW_DIF_MULTI_SEPARATE:
1647 		/* Lancer supports multiple DIF separates */
1648 		if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
1649 			*value = TRUE;
1650 		} else {
1651 			*value = FALSE;
1652 		}
1653 		break;
1654 	case OCS_HW_DUMP_MAX_SIZE:
1655 		*value = hw->dump_size;
1656 		break;
1657 	case OCS_HW_DUMP_READY:
1658 		*value = sli_dump_is_ready(&hw->sli);
1659 		break;
1660 	case OCS_HW_DUMP_PRESENT:
1661 		*value = sli_dump_is_present(&hw->sli);
1662 		break;
1663 	case OCS_HW_RESET_REQUIRED:
1664 		tmp = sli_reset_required(&hw->sli);
1665 		if(tmp < 0) {
1666 			rc = OCS_HW_RTN_ERROR;
1667 		} else {
1668 			*value = tmp;
1669 		}
1670 		break;
1671 	case OCS_HW_FW_ERROR:
1672 		*value = sli_fw_error_status(&hw->sli);
1673 		break;
1674 	case OCS_HW_FW_READY:
1675 		*value = sli_fw_ready(&hw->sli);
1676 		break;
1677 	case OCS_HW_FW_TIMED_OUT:
1678 		*value = ocs_hw_get_fw_timed_out(hw);
1679 		break;
1680 	case OCS_HW_HIGH_LOGIN_MODE:
1681 		*value = sli_get_hlm_capable(&hw->sli);
1682 		break;
1683 	case OCS_HW_PREREGISTER_SGL:
1684 		*value = sli_get_sgl_preregister_required(&hw->sli);
1685 		break;
1686 	case OCS_HW_HW_REV1:
1687 		*value = sli_get_hw_revision(&hw->sli, 0);
1688 		break;
1689 	case OCS_HW_HW_REV2:
1690 		*value = sli_get_hw_revision(&hw->sli, 1);
1691 		break;
1692 	case OCS_HW_HW_REV3:
1693 		*value = sli_get_hw_revision(&hw->sli, 2);
1694 		break;
1695 	case OCS_HW_LINKCFG:
1696 		*value = hw->linkcfg;
1697 		break;
1698 	case OCS_HW_ETH_LICENSE:
1699 		*value = hw->eth_license;
1700 		break;
1701 	case OCS_HW_LINK_MODULE_TYPE:
1702 		*value = sli_get_link_module_type(&hw->sli);
1703 		break;
1704 	case OCS_HW_NUM_CHUTES:
1705 		*value = ocs_hw_get_num_chutes(hw);
1706 		break;
1707 	case OCS_HW_DISABLE_AR_TGT_DIF:
1708 		*value = hw->workaround.disable_ar_tgt_dif;
1709 		break;
1710 	case OCS_HW_EMULATE_I_ONLY_AAB:
1711 		*value = hw->config.i_only_aab;
1712 		break;
1713 	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
1714 		*value = hw->config.emulate_tgt_wqe_timeout;
1715 		break;
1716 	case OCS_HW_VPD_LEN:
1717 		*value = sli_get_vpd_len(&hw->sli);
1718 		break;
1719 	case OCS_HW_SGL_CHAINING_CAPABLE:
1720 		*value = sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported;
1721 		break;
1722 	case OCS_HW_SGL_CHAINING_ALLOWED:
1723 		/*
1724 		 * SGL Chaining is allowed in the following cases:
1725 		 *   1. Lancer with host SGL Lists
1726 		 *   2. Skyhawk with pre-registered SGL Lists
1727 		 */
1728 		*value = FALSE;
1729 		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1730 		    !sli_get_sgl_preregister(&hw->sli) &&
1731 		    SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)) {
1732 			*value = TRUE;
1733 		}
1734 
1735 		if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1736 		    sli_get_sgl_preregister(&hw->sli) &&
1737 		    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
1738 			(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
1739 			*value = TRUE;
1740 		}
1741 		break;
1742 	case OCS_HW_SGL_CHAINING_HOST_ALLOCATED:
1743 		/* Only lancer supports host allocated SGL Chaining buffers. */
1744 		*value = ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1745 			  (SLI4_IF_TYPE_LANCER_FC_ETH  == sli_get_if_type(&hw->sli)));
1746 		break;
1747 	case OCS_HW_SEND_FRAME_CAPABLE:
1748 		if (hw->workaround.ignore_send_frame) {
1749 			*value = 0;
1750 		} else {
1751 			/* Only lancer is capable */
1752 			*value = sli_get_if_type(&hw->sli) == SLI4_IF_TYPE_LANCER_FC_ETH;
1753 		}
1754 		break;
1755 	case OCS_HW_RQ_SELECTION_POLICY:
1756 		*value = hw->config.rq_selection_policy;
1757 		break;
1758 	case OCS_HW_RR_QUANTA:
1759 		*value = hw->config.rr_quanta;
1760 		break;
1761 	case OCS_HW_MAX_VPORTS:
1762 		*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_VPI);
1763 	default:
1764 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1765 		rc = OCS_HW_RTN_ERROR;
1766 	}
1767 
1768 	return rc;
1769 }
1770 
1771 void *
ocs_hw_get_ptr(ocs_hw_t * hw,ocs_hw_property_e prop)1772 ocs_hw_get_ptr(ocs_hw_t *hw, ocs_hw_property_e prop)
1773 {
1774 	void	*rc = NULL;
1775 
1776 	switch (prop) {
1777 	case OCS_HW_WWN_NODE:
1778 		rc = sli_get_wwn_node(&hw->sli);
1779 		break;
1780 	case OCS_HW_WWN_PORT:
1781 		rc = sli_get_wwn_port(&hw->sli);
1782 		break;
1783 	case OCS_HW_VPD:
1784 		/* make sure VPD length is non-zero */
1785 		if (sli_get_vpd_len(&hw->sli)) {
1786 			rc = sli_get_vpd(&hw->sli);
1787 		}
1788 		break;
1789 	case OCS_HW_FW_REV:
1790 		rc = sli_get_fw_name(&hw->sli, 0);
1791 		break;
1792 	case OCS_HW_FW_REV2:
1793 		rc = sli_get_fw_name(&hw->sli, 1);
1794 		break;
1795 	case OCS_HW_IPL:
1796 		rc = sli_get_ipl_name(&hw->sli);
1797 		break;
1798 	case OCS_HW_PORTNUM:
1799 		rc = sli_get_portnum(&hw->sli);
1800 		break;
1801 	case OCS_HW_BIOS_VERSION_STRING:
1802 		rc = sli_get_bios_version_string(&hw->sli);
1803 		break;
1804 	default:
1805 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1806 	}
1807 
1808 	return rc;
1809 }
1810 
1811 
1812 
1813 ocs_hw_rtn_e
ocs_hw_set(ocs_hw_t * hw,ocs_hw_property_e prop,uint32_t value)1814 ocs_hw_set(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t value)
1815 {
1816 	ocs_hw_rtn_e		rc = OCS_HW_RTN_SUCCESS;
1817 
1818 	switch (prop) {
1819 	case OCS_HW_N_IO:
1820 		if (value > sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI) ||
1821 		    value == 0) {
1822 			ocs_log_test(hw->os, "IO value out of range %d vs %d\n",
1823 					value, sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI));
1824 			rc = OCS_HW_RTN_ERROR;
1825 		} else {
1826 			hw->config.n_io = value;
1827 		}
1828 		break;
1829 	case OCS_HW_N_SGL:
1830 		value += SLI4_SGE_MAX_RESERVED;
1831 		if (value > sli_get_max_sgl(&hw->sli)) {
1832 			ocs_log_test(hw->os, "SGL value out of range %d vs %d\n",
1833 					value, sli_get_max_sgl(&hw->sli));
1834 			rc = OCS_HW_RTN_ERROR;
1835 		} else {
1836 			hw->config.n_sgl = value;
1837 		}
1838 		break;
1839 	case OCS_HW_TOPOLOGY:
1840 		if ((sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) &&
1841 				(value != OCS_HW_TOPOLOGY_AUTO)) {
1842 			ocs_log_test(hw->os, "unsupported topology=%#x medium=%#x\n",
1843 					value, sli_get_medium(&hw->sli));
1844 			rc = OCS_HW_RTN_ERROR;
1845 			break;
1846 		}
1847 
1848 		switch (value) {
1849 		case OCS_HW_TOPOLOGY_AUTO:
1850 			if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
1851 				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC);
1852 			} else {
1853 				sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FCOE);
1854 			}
1855 			break;
1856 		case OCS_HW_TOPOLOGY_NPORT:
1857 			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_DA);
1858 			break;
1859 		case OCS_HW_TOPOLOGY_LOOP:
1860 			sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_AL);
1861 			break;
1862 		default:
1863 			ocs_log_test(hw->os, "unsupported topology %#x\n", value);
1864 			rc = OCS_HW_RTN_ERROR;
1865 		}
1866 		hw->config.topology = value;
1867 		break;
1868 	case OCS_HW_LINK_SPEED:
1869 		if (sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) {
1870 			switch (value) {
1871 			case 0: 	/* Auto-speed negotiation */
1872 			case 10000:	/* FCoE speed */
1873 				hw->config.speed = FC_LINK_SPEED_10G;
1874 				break;
1875 			default:
1876 				ocs_log_test(hw->os, "unsupported speed=%#x medium=%#x\n",
1877 						value, sli_get_medium(&hw->sli));
1878 				rc = OCS_HW_RTN_ERROR;
1879 			}
1880 			break;
1881 		}
1882 
1883 		switch (value) {
1884 		case 0:		/* Auto-speed negotiation */
1885 			hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
1886 			break;
1887 		case 2000:	/* FC speeds */
1888 			hw->config.speed = FC_LINK_SPEED_2G;
1889 			break;
1890 		case 4000:
1891 			hw->config.speed = FC_LINK_SPEED_4G;
1892 			break;
1893 		case 8000:
1894 			hw->config.speed = FC_LINK_SPEED_8G;
1895 			break;
1896 		case 16000:
1897 			hw->config.speed = FC_LINK_SPEED_16G;
1898 			break;
1899 		case 32000:
1900 			hw->config.speed = FC_LINK_SPEED_32G;
1901 			break;
1902 		default:
1903 			ocs_log_test(hw->os, "unsupported speed %d\n", value);
1904 			rc = OCS_HW_RTN_ERROR;
1905 		}
1906 		break;
1907 	case OCS_HW_DIF_SEED:
1908 		/* Set the DIF seed - only for lancer right now */
1909 		if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
1910 			ocs_log_test(hw->os, "DIF seed not supported for this device\n");
1911 			rc = OCS_HW_RTN_ERROR;
1912 		} else {
1913 			hw->config.dif_seed = value;
1914 		}
1915 		break;
1916 	case OCS_HW_DIF_MODE:
1917 		switch (value) {
1918 		case OCS_HW_DIF_MODE_INLINE:
1919 			/*
1920 			 *  Make sure we support inline DIF.
1921 			 *
1922 			 * Note: Having both bits clear means that we have old
1923 			 *	FW that doesn't set the bits.
1924 			 */
1925 			if (sli_is_dif_inline_capable(&hw->sli)) {
1926 				hw->config.dif_mode = value;
1927 			} else {
1928 				ocs_log_test(hw->os, "chip does not support DIF inline\n");
1929 				rc = OCS_HW_RTN_ERROR;
1930 			}
1931 			break;
1932 		case OCS_HW_DIF_MODE_SEPARATE:
1933 			/* Make sure we support DIF separates. */
1934 			if (sli_is_dif_separate_capable(&hw->sli)) {
1935 				hw->config.dif_mode = value;
1936 			} else {
1937 				ocs_log_test(hw->os, "chip does not support DIF separate\n");
1938 				rc = OCS_HW_RTN_ERROR;
1939 			}
1940 		}
1941 		break;
1942 	case OCS_HW_RQ_PROCESS_LIMIT: {
1943 		hw_rq_t *rq;
1944 		uint32_t i;
1945 
1946 		/* For each hw_rq object, set its parent CQ limit value */
1947 		for (i = 0; i < hw->hw_rq_count; i++) {
1948 			rq = hw->hw_rq[i];
1949 			hw->cq[rq->cq->instance].proc_limit = value;
1950 		}
1951 		break;
1952 	}
1953 	case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1954 		hw->config.rq_default_buffer_size = value;
1955 		break;
1956 	case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1957 		hw->config.auto_xfer_rdy_xri_cnt = value;
1958 		break;
1959 	case OCS_HW_AUTO_XFER_RDY_SIZE:
1960 		hw->config.auto_xfer_rdy_size = value;
1961 		break;
1962 	case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1963 		switch (value) {
1964 		case 512:
1965 			hw->config.auto_xfer_rdy_blk_size_chip = 0;
1966 			break;
1967 		case 1024:
1968 			hw->config.auto_xfer_rdy_blk_size_chip = 1;
1969 			break;
1970 		case 2048:
1971 			hw->config.auto_xfer_rdy_blk_size_chip = 2;
1972 			break;
1973 		case 4096:
1974 			hw->config.auto_xfer_rdy_blk_size_chip = 3;
1975 			break;
1976 		case 520:
1977 			hw->config.auto_xfer_rdy_blk_size_chip = 4;
1978 			break;
1979 		default:
1980 			ocs_log_err(hw->os, "Invalid block size %d\n",
1981 				    value);
1982 			rc = OCS_HW_RTN_ERROR;
1983 		}
1984 		break;
1985 	case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1986 		hw->config.auto_xfer_rdy_t10_enable = value;
1987 		break;
1988 	case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1989 		hw->config.auto_xfer_rdy_p_type = value;
1990 		break;
1991 	case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1992 		hw->config.auto_xfer_rdy_ref_tag_is_lba = value;
1993 		break;
1994 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1995 		hw->config.auto_xfer_rdy_app_tag_valid = value;
1996 		break;
1997 	case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1998 		hw->config.auto_xfer_rdy_app_tag_value = value;
1999 		break;
2000 	case OCS_ESOC:
2001 		hw->config.esoc = value;
2002 	case OCS_HW_HIGH_LOGIN_MODE:
2003 		rc = sli_set_hlm(&hw->sli, value);
2004 		break;
2005 	case OCS_HW_PREREGISTER_SGL:
2006 		rc = sli_set_sgl_preregister(&hw->sli, value);
2007 		break;
2008 	case OCS_HW_ETH_LICENSE:
2009 		hw->eth_license = value;
2010 		break;
2011 	case OCS_HW_EMULATE_I_ONLY_AAB:
2012 		hw->config.i_only_aab = value;
2013 		break;
2014 	case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
2015 		hw->config.emulate_tgt_wqe_timeout = value;
2016 		break;
2017 	case OCS_HW_BOUNCE:
2018 		hw->config.bounce = value;
2019 		break;
2020 	case OCS_HW_RQ_SELECTION_POLICY:
2021 		hw->config.rq_selection_policy = value;
2022 		break;
2023 	case OCS_HW_RR_QUANTA:
2024 		hw->config.rr_quanta = value;
2025 		break;
2026 	default:
2027 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2028 		rc = OCS_HW_RTN_ERROR;
2029 	}
2030 
2031 	return rc;
2032 }
2033 
2034 
2035 ocs_hw_rtn_e
ocs_hw_set_ptr(ocs_hw_t * hw,ocs_hw_property_e prop,void * value)2036 ocs_hw_set_ptr(ocs_hw_t *hw, ocs_hw_property_e prop, void *value)
2037 {
2038 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2039 
2040 	switch (prop) {
2041 	case OCS_HW_WAR_VERSION:
2042 		hw->hw_war_version = value;
2043 		break;
2044 	case OCS_HW_FILTER_DEF: {
2045 		char *p = value;
2046 		uint32_t idx = 0;
2047 
2048 		for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) {
2049 			hw->config.filter_def[idx] = 0;
2050 		}
2051 
2052 		for (idx = 0; (idx < ARRAY_SIZE(hw->config.filter_def)) && (p != NULL) && *p; ) {
2053 			hw->config.filter_def[idx++] = ocs_strtoul(p, 0, 0);
2054 			p = ocs_strchr(p, ',');
2055 			if (p != NULL) {
2056 				p++;
2057 			}
2058 		}
2059 
2060 		break;
2061 	}
2062 	default:
2063 		ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2064 		rc = OCS_HW_RTN_ERROR;
2065 		break;
2066 	}
2067 	return rc;
2068 }
2069 /**
2070  * @ingroup interrupt
2071  * @brief Check for the events associated with the interrupt vector.
2072  *
2073  * @param hw Hardware context.
2074  * @param vector Zero-based interrupt vector number.
2075  *
2076  * @return Returns 0 on success, or a non-zero value on failure.
2077  */
2078 int32_t
ocs_hw_event_check(ocs_hw_t * hw,uint32_t vector)2079 ocs_hw_event_check(ocs_hw_t *hw, uint32_t vector)
2080 {
2081 	int32_t rc = 0;
2082 
2083 	if (!hw) {
2084 		ocs_log_err(NULL, "HW context NULL?!?\n");
2085 		return -1;
2086 	}
2087 
2088 	if (vector > hw->eq_count) {
2089 		ocs_log_err(hw->os, "vector %d. max %d\n",
2090 				vector, hw->eq_count);
2091 		return -1;
2092 	}
2093 
2094 	/*
2095 	 * The caller should disable interrupts if they wish to prevent us
2096 	 * from processing during a shutdown. The following states are defined:
2097 	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2098 	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2099 	 *                                    queues are cleared.
2100 	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2101 	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2102 	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2103 	 *                                        completions.
2104 	 */
2105 	if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
2106 		rc = sli_queue_is_empty(&hw->sli, &hw->eq[vector]);
2107 
2108 		/* Re-arm queue if there are no entries */
2109 		if (rc != 0) {
2110 			sli_queue_arm(&hw->sli, &hw->eq[vector], TRUE);
2111 		}
2112 	}
2113 	return rc;
2114 }
2115 
2116 void
ocs_hw_unsol_process_bounce(void * arg)2117 ocs_hw_unsol_process_bounce(void *arg)
2118 {
2119 	ocs_hw_sequence_t *seq = arg;
2120 	ocs_hw_t *hw = seq->hw;
2121 
2122 	ocs_hw_assert(hw != NULL);
2123 	ocs_hw_assert(hw->callback.unsolicited != NULL);
2124 
2125 	hw->callback.unsolicited(hw->args.unsolicited, seq);
2126 }
2127 
2128 int32_t
ocs_hw_process(ocs_hw_t * hw,uint32_t vector,uint32_t max_isr_time_msec)2129 ocs_hw_process(ocs_hw_t *hw, uint32_t vector, uint32_t max_isr_time_msec)
2130 {
2131 	hw_eq_t *eq;
2132 	int32_t rc = 0;
2133 
2134 	CPUTRACE("");
2135 
2136 	/*
2137 	 * The caller should disable interrupts if they wish to prevent us
2138 	 * from processing during a shutdown. The following states are defined:
2139 	 *   OCS_HW_STATE_UNINITIALIZED - No queues allocated
2140 	 *   OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2141 	 *                                    queues are cleared.
2142 	 *   OCS_HW_STATE_ACTIVE - Chip and queues are operational
2143 	 *   OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2144 	 *   OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2145 	 *                                        completions.
2146 	 */
2147 	if (hw->state == OCS_HW_STATE_UNINITIALIZED) {
2148 		return 0;
2149 	}
2150 
2151 	/* Get pointer to hw_eq_t */
2152 	eq = hw->hw_eq[vector];
2153 
2154 	OCS_STAT(eq->use_count++);
2155 
2156 	rc = ocs_hw_eq_process(hw, eq, max_isr_time_msec);
2157 
2158 	return rc;
2159 }
2160 
2161 /**
2162  * @ingroup interrupt
2163  * @brief Process events associated with an EQ.
2164  *
2165  * @par Description
2166  * Loop termination:
2167  * @n @n Without a mechanism to terminate the completion processing loop, it
2168  * is possible under some workload conditions for the loop to never terminate
2169  * (or at least take longer than the OS is happy to have an interrupt handler
2170  * or kernel thread context hold a CPU without yielding).
2171  * @n @n The approach taken here is to periodically check how much time
2172  * we have been in this
2173  * processing loop, and if we exceed a predetermined time (multiple seconds), the
2174  * loop is terminated, and ocs_hw_process() returns.
2175  *
2176  * @param hw Hardware context.
2177  * @param eq Pointer to HW EQ object.
2178  * @param max_isr_time_msec Maximum time in msec to stay in this function.
2179  *
2180  * @return Returns 0 on success, or a non-zero value on failure.
2181  */
2182 int32_t
ocs_hw_eq_process(ocs_hw_t * hw,hw_eq_t * eq,uint32_t max_isr_time_msec)2183 ocs_hw_eq_process(ocs_hw_t *hw, hw_eq_t *eq, uint32_t max_isr_time_msec)
2184 {
2185 	uint8_t		eqe[sizeof(sli4_eqe_t)] = { 0 };
2186 	uint32_t	done = FALSE;
2187 	uint32_t	tcheck_count;
2188 	time_t		tstart;
2189 	time_t		telapsed;
2190 
2191 	tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2192 	tstart = ocs_msectime();
2193 
2194 	CPUTRACE("");
2195 
2196 	while (!done && !sli_queue_read(&hw->sli, eq->queue, eqe)) {
2197 		uint16_t	cq_id = 0;
2198 		int32_t		rc;
2199 
2200 		rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
2201 		if (unlikely(rc)) {
2202 			if (rc > 0) {
2203 				uint32_t i;
2204 
2205 				/*
2206 				 * Received a sentinel EQE indicating the EQ is full.
2207 				 * Process all CQs
2208 				 */
2209 				for (i = 0; i < hw->cq_count; i++) {
2210 					ocs_hw_cq_process(hw, hw->hw_cq[i]);
2211 				}
2212 				continue;
2213 			} else {
2214 				return rc;
2215 			}
2216 		} else {
2217 			int32_t index = ocs_hw_queue_hash_find(hw->cq_hash, cq_id);
2218 			if (likely(index >= 0)) {
2219 				ocs_hw_cq_process(hw, hw->hw_cq[index]);
2220 			} else {
2221 				ocs_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
2222 			}
2223 		}
2224 
2225 
2226 		if (eq->queue->n_posted > (eq->queue->posted_limit)) {
2227 			sli_queue_arm(&hw->sli, eq->queue, FALSE);
2228 		}
2229 
2230 		if (tcheck_count && (--tcheck_count == 0)) {
2231 			tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2232 			telapsed = ocs_msectime() - tstart;
2233 			if (telapsed >= max_isr_time_msec) {
2234 				done = TRUE;
2235 			}
2236 		}
2237 	}
2238 	sli_queue_eq_arm(&hw->sli, eq->queue, TRUE);
2239 
2240 	return 0;
2241 }
2242 
2243 /**
2244  * @brief Submit queued (pending) mbx commands.
2245  *
2246  * @par Description
2247  * Submit queued mailbox commands.
2248  * --- Assumes that hw->cmd_lock is held ---
2249  *
2250  * @param hw Hardware context.
2251  *
2252  * @return Returns 0 on success, or a negative error code value on failure.
2253  */
2254 static int32_t
ocs_hw_cmd_submit_pending(ocs_hw_t * hw)2255 ocs_hw_cmd_submit_pending(ocs_hw_t *hw)
2256 {
2257 	ocs_command_ctx_t *ctx;
2258 	int32_t rc = 0;
2259 
2260 	/* Assumes lock held */
2261 
2262 	/* Only submit MQE if there's room */
2263 	while (hw->cmd_head_count < (OCS_HW_MQ_DEPTH - 1)) {
2264 		ctx = ocs_list_remove_head(&hw->cmd_pending);
2265 		if (ctx == NULL) {
2266 			break;
2267 		}
2268 		ocs_list_add_tail(&hw->cmd_head, ctx);
2269 		hw->cmd_head_count++;
2270 		if (sli_queue_write(&hw->sli, hw->mq, ctx->buf) < 0) {
2271 			ocs_log_test(hw->os, "sli_queue_write failed: %d\n", rc);
2272 			rc = -1;
2273 			break;
2274 		}
2275 	}
2276 	return rc;
2277 }
2278 
2279 /**
2280  * @ingroup io
2281  * @brief Issue a SLI command.
2282  *
2283  * @par Description
2284  * Send a mailbox command to the hardware, and either wait for a completion
2285  * (OCS_CMD_POLL) or get an optional asynchronous completion (OCS_CMD_NOWAIT).
2286  *
2287  * @param hw Hardware context.
2288  * @param cmd Buffer containing a formatted command and results.
2289  * @param opts Command options:
2290  *  - OCS_CMD_POLL - Command executes synchronously and busy-waits for the completion.
2291  *  - OCS_CMD_NOWAIT - Command executes asynchronously. Uses callback.
2292  * @param cb Function callback used for asynchronous mode. May be NULL.
2293  * @n Prototype is <tt>(*cb)(void *arg, uint8_t *cmd)</tt>.
2294  * @n @n @b Note: If the
2295  * callback function pointer is NULL, the results of the command are silently
2296  * discarded, allowing this pointer to exist solely on the stack.
2297  * @param arg Argument passed to an asynchronous callback.
2298  *
2299  * @return Returns 0 on success, or a non-zero value on failure.
2300  */
2301 ocs_hw_rtn_e
ocs_hw_command(ocs_hw_t * hw,uint8_t * cmd,uint32_t opts,void * cb,void * arg)2302 ocs_hw_command(ocs_hw_t *hw, uint8_t *cmd, uint32_t opts, void *cb, void *arg)
2303 {
2304 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2305 
2306 	/*
2307 	 * If the chip is in an error state (UE'd) then reject this mailbox
2308 	 *  command.
2309 	 */
2310 	if (sli_fw_error_status(&hw->sli) > 0) {
2311 		uint32_t err1 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1);
2312 		uint32_t err2 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2);
2313 		if (hw->expiration_logged == 0 && err1 == 0x2 && err2 == 0x10) {
2314 			hw->expiration_logged = 1;
2315 			ocs_log_crit(hw->os,"Emulex: Heartbeat expired after %d seconds\n",
2316 					hw->watchdog_timeout);
2317 		}
2318 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2319 		ocs_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
2320 			sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_STATUS),
2321 			err1, err2);
2322 
2323 		return OCS_HW_RTN_ERROR;
2324 	}
2325 
2326 	if (OCS_CMD_POLL == opts) {
2327 
2328 		ocs_lock(&hw->cmd_lock);
2329 		if (hw->mq->length && !sli_queue_is_empty(&hw->sli, hw->mq)) {
2330 			/*
2331 			 * Can't issue Boot-strap mailbox command with other
2332 			 * mail-queue commands pending as this interaction is
2333 			 * undefined
2334 			 */
2335 			rc = OCS_HW_RTN_ERROR;
2336 		} else {
2337 			void *bmbx = hw->sli.bmbx.virt;
2338 
2339 			ocs_memset(bmbx, 0, SLI4_BMBX_SIZE);
2340 			ocs_memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
2341 
2342 			if (sli_bmbx_command(&hw->sli) == 0) {
2343 				rc = OCS_HW_RTN_SUCCESS;
2344 				ocs_memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
2345 			}
2346 		}
2347 		ocs_unlock(&hw->cmd_lock);
2348 	} else if (OCS_CMD_NOWAIT == opts) {
2349 		ocs_command_ctx_t	*ctx = NULL;
2350 
2351 		ctx = ocs_malloc(hw->os, sizeof(ocs_command_ctx_t), OCS_M_ZERO | OCS_M_NOWAIT);
2352 		if (!ctx) {
2353 			ocs_log_err(hw->os, "can't allocate command context\n");
2354 			return OCS_HW_RTN_NO_RESOURCES;
2355 		}
2356 
2357 		if (hw->state != OCS_HW_STATE_ACTIVE) {
2358 			ocs_log_err(hw->os, "Can't send command, HW state=%d\n", hw->state);
2359 			ocs_free(hw->os, ctx, sizeof(*ctx));
2360 			return OCS_HW_RTN_ERROR;
2361 		}
2362 
2363 		if (cb) {
2364 			ctx->cb = cb;
2365 			ctx->arg = arg;
2366 		}
2367 		ctx->buf = cmd;
2368 		ctx->ctx = hw;
2369 
2370 		ocs_lock(&hw->cmd_lock);
2371 
2372 			/* Add to pending list */
2373 			ocs_list_add_tail(&hw->cmd_pending, ctx);
2374 
2375 			/* Submit as much of the pending list as we can */
2376 			if (ocs_hw_cmd_submit_pending(hw) == 0) {
2377 				rc = OCS_HW_RTN_SUCCESS;
2378 			}
2379 
2380 		ocs_unlock(&hw->cmd_lock);
2381 	}
2382 
2383 	return rc;
2384 }
2385 
2386 /**
2387  * @ingroup devInitShutdown
2388  * @brief Register a callback for the given event.
2389  *
2390  * @param hw Hardware context.
2391  * @param which Event of interest.
2392  * @param func Function to call when the event occurs.
2393  * @param arg Argument passed to the callback function.
2394  *
2395  * @return Returns 0 on success, or a non-zero value on failure.
2396  */
2397 ocs_hw_rtn_e
ocs_hw_callback(ocs_hw_t * hw,ocs_hw_callback_e which,void * func,void * arg)2398 ocs_hw_callback(ocs_hw_t *hw, ocs_hw_callback_e which, void *func, void *arg)
2399 {
2400 
2401 	if (!hw || !func || (which >= OCS_HW_CB_MAX)) {
2402 		ocs_log_err(NULL, "bad parameter hw=%p which=%#x func=%p\n",
2403 			    hw, which, func);
2404 		return OCS_HW_RTN_ERROR;
2405 	}
2406 
2407 	switch (which) {
2408 	case OCS_HW_CB_DOMAIN:
2409 		hw->callback.domain = func;
2410 		hw->args.domain = arg;
2411 		break;
2412 	case OCS_HW_CB_PORT:
2413 		hw->callback.port = func;
2414 		hw->args.port = arg;
2415 		break;
2416 	case OCS_HW_CB_UNSOLICITED:
2417 		hw->callback.unsolicited = func;
2418 		hw->args.unsolicited = arg;
2419 		break;
2420 	case OCS_HW_CB_REMOTE_NODE:
2421 		hw->callback.rnode = func;
2422 		hw->args.rnode = arg;
2423 		break;
2424 	case OCS_HW_CB_BOUNCE:
2425 		hw->callback.bounce = func;
2426 		hw->args.bounce = arg;
2427 		break;
2428 	default:
2429 		ocs_log_test(hw->os, "unknown callback %#x\n", which);
2430 		return OCS_HW_RTN_ERROR;
2431 	}
2432 
2433 	return OCS_HW_RTN_SUCCESS;
2434 }
2435 
2436 /**
2437  * @ingroup port
2438  * @brief Allocate a port object.
2439  *
2440  * @par Description
2441  * This function allocates a VPI object for the port and stores it in the
2442  * indicator field of the port object.
2443  *
2444  * @param hw Hardware context.
2445  * @param sport SLI port object used to connect to the domain.
2446  * @param domain Domain object associated with this port (may be NULL).
2447  * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
2448  *
2449  * @return Returns 0 on success, or a non-zero value on failure.
2450  */
2451 ocs_hw_rtn_e
ocs_hw_port_alloc(ocs_hw_t * hw,ocs_sli_port_t * sport,ocs_domain_t * domain,uint8_t * wwpn)2452 ocs_hw_port_alloc(ocs_hw_t *hw, ocs_sli_port_t *sport, ocs_domain_t *domain,
2453 		uint8_t *wwpn)
2454 {
2455 	uint8_t	*cmd = NULL;
2456 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2457 	uint32_t index;
2458 
2459 	sport->indicator = UINT32_MAX;
2460 	sport->hw = hw;
2461 	sport->ctx.app = sport;
2462 	sport->sm_free_req_pending = 0;
2463 
2464 	/*
2465 	 * Check if the chip is in an error state (UE'd) before proceeding.
2466 	 */
2467 	if (sli_fw_error_status(&hw->sli) > 0) {
2468 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2469 		return OCS_HW_RTN_ERROR;
2470 	}
2471 
2472 	if (wwpn) {
2473 		ocs_memcpy(&sport->sli_wwpn, wwpn, sizeof(sport->sli_wwpn));
2474 	}
2475 
2476 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VPI, &sport->indicator, &index)) {
2477 		ocs_log_err(hw->os, "FCOE_VPI allocation failure\n");
2478 		return OCS_HW_RTN_ERROR;
2479 	}
2480 
2481 	if (domain != NULL) {
2482 		ocs_sm_function_t	next = NULL;
2483 
2484 		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2485 		if (!cmd) {
2486 			ocs_log_err(hw->os, "command memory allocation failed\n");
2487 			rc = OCS_HW_RTN_NO_MEMORY;
2488 			goto ocs_hw_port_alloc_out;
2489 		}
2490 
2491 		/* If the WWPN is NULL, fetch the default WWPN and WWNN before
2492 		 * initializing the VPI
2493 		 */
2494 		if (!wwpn) {
2495 			next = __ocs_hw_port_alloc_read_sparm64;
2496 		} else {
2497 			next = __ocs_hw_port_alloc_init_vpi;
2498 		}
2499 
2500 		ocs_sm_transition(&sport->ctx, next, cmd);
2501 	} else if (!wwpn) {
2502 		/* This is the convention for the HW, not SLI */
2503 		ocs_log_test(hw->os, "need WWN for physical port\n");
2504 		rc = OCS_HW_RTN_ERROR;
2505 	} else {
2506 		/* domain NULL and wwpn non-NULL */
2507 		ocs_sm_transition(&sport->ctx, __ocs_hw_port_alloc_init, NULL);
2508 	}
2509 
2510 ocs_hw_port_alloc_out:
2511 	if (rc != OCS_HW_RTN_SUCCESS) {
2512 		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2513 
2514 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
2515 	}
2516 
2517 	return rc;
2518 }
2519 
2520 /**
2521  * @ingroup port
2522  * @brief Attach a physical/virtual SLI port to a domain.
2523  *
2524  * @par Description
2525  * This function registers a previously-allocated VPI with the
2526  * device.
2527  *
2528  * @param hw Hardware context.
2529  * @param sport Pointer to the SLI port object.
2530  * @param fc_id Fibre Channel ID to associate with this port.
2531  *
2532  * @return Returns OCS_HW_RTN_SUCCESS on success, or an error code on failure.
2533  */
2534 ocs_hw_rtn_e
ocs_hw_port_attach(ocs_hw_t * hw,ocs_sli_port_t * sport,uint32_t fc_id)2535 ocs_hw_port_attach(ocs_hw_t *hw, ocs_sli_port_t *sport, uint32_t fc_id)
2536 {
2537 	uint8_t	*buf = NULL;
2538 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2539 
2540 	if (!hw || !sport) {
2541 		ocs_log_err(hw ? hw->os : NULL,
2542 			"bad parameter(s) hw=%p sport=%p\n", hw,
2543 			sport);
2544 		return OCS_HW_RTN_ERROR;
2545 	}
2546 
2547 	/*
2548 	 * Check if the chip is in an error state (UE'd) before proceeding.
2549 	 */
2550 	if (sli_fw_error_status(&hw->sli) > 0) {
2551 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2552 		return OCS_HW_RTN_ERROR;
2553 	}
2554 
2555 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2556 	if (!buf) {
2557 		ocs_log_err(hw->os, "no buffer for command\n");
2558 		return OCS_HW_RTN_NO_MEMORY;
2559 	}
2560 
2561 	sport->fc_id = fc_id;
2562 	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_ATTACH, buf);
2563 	return rc;
2564 }
2565 
2566 /**
2567  * @brief Called when the port control command completes.
2568  *
2569  * @par Description
2570  * We only need to free the mailbox command buffer.
2571  *
2572  * @param hw Hardware context.
2573  * @param status Status field from the mbox completion.
2574  * @param mqe Mailbox response structure.
2575  * @param arg Pointer to a callback function that signals the caller that the command is done.
2576  *
2577  * @return Returns 0.
2578  */
2579 static int32_t
ocs_hw_cb_port_control(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)2580 ocs_hw_cb_port_control(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
2581 {
2582 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
2583 	return 0;
2584 }
2585 
2586 /**
2587  * @ingroup port
2588  * @brief Control a port (initialize, shutdown, or set link configuration).
2589  *
2590  * @par Description
2591  * This function controls a port depending on the @c ctrl parameter:
2592  * - @b OCS_HW_PORT_INIT -
2593  * Issues the CONFIG_LINK and INIT_LINK commands for the specified port.
2594  * The HW generates an OCS_HW_DOMAIN_FOUND event when the link comes up.
2595  * .
2596  * - @b OCS_HW_PORT_SHUTDOWN -
2597  * Issues the DOWN_LINK command for the specified port.
2598  * The HW generates an OCS_HW_DOMAIN_LOST event when the link is down.
2599  * .
2600  * - @b OCS_HW_PORT_SET_LINK_CONFIG -
2601  * Sets the link configuration.
2602  *
2603  * @param hw Hardware context.
2604  * @param ctrl Specifies the operation:
2605  * - OCS_HW_PORT_INIT
2606  * - OCS_HW_PORT_SHUTDOWN
2607  * - OCS_HW_PORT_SET_LINK_CONFIG
2608  *
2609  * @param value Operation-specific value.
2610  * - OCS_HW_PORT_INIT - Selective reset AL_PA
2611  * - OCS_HW_PORT_SHUTDOWN - N/A
2612  * - OCS_HW_PORT_SET_LINK_CONFIG - An enum #ocs_hw_linkcfg_e value.
2613  *
2614  * @param cb Callback function to invoke the following operation.
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  * @param arg Callback argument invoked after the command completes.
2621  * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2622  * are handled by the OCS_HW_CB_DOMAIN callbacks).
2623  * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2624  * completes.
2625  *
2626  * @return Returns 0 on success, or a non-zero value on failure.
2627  */
2628 ocs_hw_rtn_e
ocs_hw_port_control(ocs_hw_t * hw,ocs_hw_port_e ctrl,uintptr_t value,ocs_hw_port_control_cb_t cb,void * arg)2629 ocs_hw_port_control(ocs_hw_t *hw, ocs_hw_port_e ctrl, uintptr_t value, ocs_hw_port_control_cb_t cb, void *arg)
2630 {
2631 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2632 
2633 	switch (ctrl) {
2634 	case OCS_HW_PORT_INIT:
2635 	{
2636 		uint8_t	*init_link;
2637 		uint32_t speed = 0;
2638 		uint8_t reset_alpa = 0;
2639 
2640 		if (SLI_LINK_MEDIUM_FC == sli_get_medium(&hw->sli)) {
2641 			uint8_t	*cfg_link;
2642 
2643 			cfg_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2644 			if (cfg_link == NULL) {
2645 				ocs_log_err(hw->os, "no buffer for command\n");
2646 				return OCS_HW_RTN_NO_MEMORY;
2647 			}
2648 
2649 			if (sli_cmd_config_link(&hw->sli, cfg_link, SLI4_BMBX_SIZE)) {
2650 				rc = ocs_hw_command(hw, cfg_link, OCS_CMD_NOWAIT,
2651 							ocs_hw_cb_port_control, NULL);
2652 			}
2653 
2654 			if (rc != OCS_HW_RTN_SUCCESS) {
2655 				ocs_free(hw->os, cfg_link, SLI4_BMBX_SIZE);
2656 				ocs_log_err(hw->os, "CONFIG_LINK failed\n");
2657 				break;
2658 			}
2659 			speed = hw->config.speed;
2660 			reset_alpa = (uint8_t)(value & 0xff);
2661 		} else {
2662 			speed = FC_LINK_SPEED_10G;
2663 		}
2664 
2665 		/*
2666 		 * Bring link up, unless FW version is not supported
2667 		 */
2668 		if (hw->workaround.fw_version_too_low) {
2669 			if (SLI4_IF_TYPE_LANCER_FC_ETH == hw->sli.if_type) {
2670 				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2671 					OCS_FW_VER_STR(OCS_MIN_FW_VER_LANCER), (char *) sli_get_fw_name(&hw->sli,0));
2672 			} else {
2673 				ocs_log_err(hw->os, "Cannot bring up link.  Please update firmware to %s or later (current version is %s)\n",
2674 					OCS_FW_VER_STR(OCS_MIN_FW_VER_SKYHAWK), (char *) sli_get_fw_name(&hw->sli, 0));
2675 			}
2676 
2677 			return OCS_HW_RTN_ERROR;
2678 		}
2679 
2680 		rc = OCS_HW_RTN_ERROR;
2681 
2682 		/* Allocate a new buffer for the init_link command */
2683 		init_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2684 		if (init_link == NULL) {
2685 			ocs_log_err(hw->os, "no buffer for command\n");
2686 			return OCS_HW_RTN_NO_MEMORY;
2687 		}
2688 
2689 
2690 		if (sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE, speed, reset_alpa)) {
2691 			rc = ocs_hw_command(hw, init_link, OCS_CMD_NOWAIT,
2692 						ocs_hw_cb_port_control, NULL);
2693 		}
2694 		/* Free buffer on error, since no callback is coming */
2695 		if (rc != OCS_HW_RTN_SUCCESS) {
2696 			ocs_free(hw->os, init_link, SLI4_BMBX_SIZE);
2697 			ocs_log_err(hw->os, "INIT_LINK failed\n");
2698 		}
2699 		break;
2700 	}
2701 	case OCS_HW_PORT_SHUTDOWN:
2702 	{
2703 		uint8_t	*down_link;
2704 
2705 		down_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2706 		if (down_link == NULL) {
2707 			ocs_log_err(hw->os, "no buffer for command\n");
2708 			return OCS_HW_RTN_NO_MEMORY;
2709 		}
2710 		if (sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE)) {
2711 			rc = ocs_hw_command(hw, down_link, OCS_CMD_NOWAIT,
2712 						ocs_hw_cb_port_control, NULL);
2713 		}
2714 		/* Free buffer on error, since no callback is coming */
2715 		if (rc != OCS_HW_RTN_SUCCESS) {
2716 			ocs_free(hw->os, down_link, SLI4_BMBX_SIZE);
2717 			ocs_log_err(hw->os, "DOWN_LINK failed\n");
2718 		}
2719 		break;
2720 	}
2721 	case OCS_HW_PORT_SET_LINK_CONFIG:
2722 		rc = ocs_hw_set_linkcfg(hw, (ocs_hw_linkcfg_e)value, OCS_CMD_NOWAIT, cb, arg);
2723 		break;
2724 	default:
2725 		ocs_log_test(hw->os, "unhandled control %#x\n", ctrl);
2726 		break;
2727 	}
2728 
2729 	return rc;
2730 }
2731 
2732 
2733 /**
2734  * @ingroup port
2735  * @brief Free port resources.
2736  *
2737  * @par Description
2738  * Issue the UNREG_VPI command to free the assigned VPI context.
2739  *
2740  * @param hw Hardware context.
2741  * @param sport SLI port object used to connect to the domain.
2742  *
2743  * @return Returns 0 on success, or a non-zero value on failure.
2744  */
2745 ocs_hw_rtn_e
ocs_hw_port_free(ocs_hw_t * hw,ocs_sli_port_t * sport)2746 ocs_hw_port_free(ocs_hw_t *hw, ocs_sli_port_t *sport)
2747 {
2748 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2749 
2750 	if (!hw || !sport) {
2751 		ocs_log_err(hw ? hw->os : NULL,
2752 			"bad parameter(s) hw=%p sport=%p\n", hw,
2753 			sport);
2754 		return OCS_HW_RTN_ERROR;
2755 	}
2756 
2757 	/*
2758 	 * Check if the chip is in an error state (UE'd) before proceeding.
2759 	 */
2760 	if (sli_fw_error_status(&hw->sli) > 0) {
2761 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2762 		return OCS_HW_RTN_ERROR;
2763 	}
2764 
2765 	ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_FREE, NULL);
2766 	return rc;
2767 }
2768 
2769 /**
2770  * @ingroup domain
2771  * @brief Allocate a fabric domain object.
2772  *
2773  * @par Description
2774  * This function starts a series of commands needed to connect to the domain, including
2775  *   - REG_FCFI
2776  *   - INIT_VFI
2777  *   - READ_SPARMS
2778  *   .
2779  * @b Note: Not all SLI interface types use all of the above commands.
2780  * @n @n Upon successful allocation, the HW generates a OCS_HW_DOMAIN_ALLOC_OK
2781  * event. On failure, it generates a OCS_HW_DOMAIN_ALLOC_FAIL event.
2782  *
2783  * @param hw Hardware context.
2784  * @param domain Pointer to the domain object.
2785  * @param fcf FCF index.
2786  * @param vlan VLAN ID.
2787  *
2788  * @return Returns 0 on success, or a non-zero value on failure.
2789  */
2790 ocs_hw_rtn_e
ocs_hw_domain_alloc(ocs_hw_t * hw,ocs_domain_t * domain,uint32_t fcf,uint32_t vlan)2791 ocs_hw_domain_alloc(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fcf, uint32_t vlan)
2792 {
2793 	uint8_t		*cmd = NULL;
2794 	uint32_t	index;
2795 
2796 	if (!hw || !domain || !domain->sport) {
2797 		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p sport=%p\n",
2798 				hw, domain, domain ? domain->sport : NULL);
2799 		return OCS_HW_RTN_ERROR;
2800 	}
2801 
2802 	/*
2803 	 * Check if the chip is in an error state (UE'd) before proceeding.
2804 	 */
2805 	if (sli_fw_error_status(&hw->sli) > 0) {
2806 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2807 		return OCS_HW_RTN_ERROR;
2808 	}
2809 
2810 	cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2811 	if (!cmd) {
2812 		ocs_log_err(hw->os, "command memory allocation failed\n");
2813 		return OCS_HW_RTN_NO_MEMORY;
2814 	}
2815 
2816 	domain->dma = hw->domain_dmem;
2817 
2818 	domain->hw = hw;
2819 	domain->sm.app = domain;
2820 	domain->fcf = fcf;
2821 	domain->fcf_indicator = UINT32_MAX;
2822 	domain->vlan_id = vlan;
2823 	domain->indicator = UINT32_MAX;
2824 
2825 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VFI, &domain->indicator, &index)) {
2826 		ocs_log_err(hw->os, "FCOE_VFI allocation failure\n");
2827 
2828 		ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2829 
2830 		return OCS_HW_RTN_ERROR;
2831 	}
2832 
2833 	ocs_sm_transition(&domain->sm, __ocs_hw_domain_init, cmd);
2834 	return OCS_HW_RTN_SUCCESS;
2835 }
2836 
2837 /**
2838  * @ingroup domain
2839  * @brief Attach a SLI port to a domain.
2840  *
2841  * @param hw Hardware context.
2842  * @param domain Pointer to the domain object.
2843  * @param fc_id Fibre Channel ID to associate with this port.
2844  *
2845  * @return Returns 0 on success, or a non-zero value on failure.
2846  */
2847 ocs_hw_rtn_e
ocs_hw_domain_attach(ocs_hw_t * hw,ocs_domain_t * domain,uint32_t fc_id)2848 ocs_hw_domain_attach(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fc_id)
2849 {
2850 	uint8_t	*buf = NULL;
2851 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2852 
2853 	if (!hw || !domain) {
2854 		ocs_log_err(hw ? hw->os : NULL,
2855 			"bad parameter(s) hw=%p domain=%p\n",
2856 			hw, domain);
2857 		return OCS_HW_RTN_ERROR;
2858 	}
2859 
2860 	/*
2861 	 * Check if the chip is in an error state (UE'd) before proceeding.
2862 	 */
2863 	if (sli_fw_error_status(&hw->sli) > 0) {
2864 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2865 		return OCS_HW_RTN_ERROR;
2866 	}
2867 
2868 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2869 	if (!buf) {
2870 		ocs_log_err(hw->os, "no buffer for command\n");
2871 		return OCS_HW_RTN_NO_MEMORY;
2872 	}
2873 
2874 	domain->sport->fc_id = fc_id;
2875 	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_ATTACH, buf);
2876 	return rc;
2877 }
2878 
2879 /**
2880  * @ingroup domain
2881  * @brief Free a fabric domain object.
2882  *
2883  * @par Description
2884  * Free both the driver and SLI port resources associated with the domain.
2885  *
2886  * @param hw Hardware context.
2887  * @param domain Pointer to the domain object.
2888  *
2889  * @return Returns 0 on success, or a non-zero value on failure.
2890  */
2891 ocs_hw_rtn_e
ocs_hw_domain_free(ocs_hw_t * hw,ocs_domain_t * domain)2892 ocs_hw_domain_free(ocs_hw_t *hw, ocs_domain_t *domain)
2893 {
2894 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
2895 
2896 	if (!hw || !domain) {
2897 		ocs_log_err(hw ? hw->os : NULL,
2898 			"bad parameter(s) hw=%p domain=%p\n",
2899 			hw, domain);
2900 		return OCS_HW_RTN_ERROR;
2901 	}
2902 
2903 	/*
2904 	 * Check if the chip is in an error state (UE'd) before proceeding.
2905 	 */
2906 	if (sli_fw_error_status(&hw->sli) > 0) {
2907 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2908 		return OCS_HW_RTN_ERROR;
2909 	}
2910 
2911 	ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_FREE, NULL);
2912 	return rc;
2913 }
2914 
2915 /**
2916  * @ingroup domain
2917  * @brief Free a fabric domain object.
2918  *
2919  * @par Description
2920  * Free the driver resources associated with the domain. The difference between
2921  * this call and ocs_hw_domain_free() is that this call assumes resources no longer
2922  * exist on the SLI port, due to a reset or after some error conditions.
2923  *
2924  * @param hw Hardware context.
2925  * @param domain Pointer to the domain object.
2926  *
2927  * @return Returns 0 on success, or a non-zero value on failure.
2928  */
2929 ocs_hw_rtn_e
ocs_hw_domain_force_free(ocs_hw_t * hw,ocs_domain_t * domain)2930 ocs_hw_domain_force_free(ocs_hw_t *hw, ocs_domain_t *domain)
2931 {
2932 	if (!hw || !domain) {
2933 		ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p\n", hw, domain);
2934 		return OCS_HW_RTN_ERROR;
2935 	}
2936 
2937 	sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
2938 
2939 	return OCS_HW_RTN_SUCCESS;
2940 }
2941 
2942 /**
2943  * @ingroup node
2944  * @brief Allocate a remote node object.
2945  *
2946  * @param hw Hardware context.
2947  * @param rnode Allocated remote node object to initialize.
2948  * @param fc_addr FC address of the remote node.
2949  * @param sport SLI port used to connect to remote node.
2950  *
2951  * @return Returns 0 on success, or a non-zero value on failure.
2952  */
2953 ocs_hw_rtn_e
ocs_hw_node_alloc(ocs_hw_t * hw,ocs_remote_node_t * rnode,uint32_t fc_addr,ocs_sli_port_t * sport)2954 ocs_hw_node_alloc(ocs_hw_t *hw, ocs_remote_node_t *rnode, uint32_t fc_addr,
2955 		ocs_sli_port_t *sport)
2956 {
2957 	/* Check for invalid indicator */
2958 	if (UINT32_MAX != rnode->indicator) {
2959 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x rpi=%#x\n",
2960 				fc_addr, rnode->indicator);
2961 		return OCS_HW_RTN_ERROR;
2962 	}
2963 
2964 	/*
2965 	 * Check if the chip is in an error state (UE'd) before proceeding.
2966 	 */
2967 	if (sli_fw_error_status(&hw->sli) > 0) {
2968 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2969 		return OCS_HW_RTN_ERROR;
2970 	}
2971 
2972 	/* NULL SLI port indicates an unallocated remote node */
2973 	rnode->sport = NULL;
2974 
2975 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &rnode->indicator, &rnode->index)) {
2976 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
2977 				fc_addr);
2978 		return OCS_HW_RTN_ERROR;
2979 	}
2980 
2981 	rnode->fc_id = fc_addr;
2982 	rnode->sport = sport;
2983 
2984 	return OCS_HW_RTN_SUCCESS;
2985 }
2986 
2987 /**
2988  * @ingroup node
2989  * @brief Update a remote node object with the remote port's service parameters.
2990  *
2991  * @param hw Hardware context.
2992  * @param rnode Allocated remote node object to initialize.
2993  * @param sparms DMA buffer containing the remote port's service parameters.
2994  *
2995  * @return Returns 0 on success, or a non-zero value on failure.
2996  */
2997 ocs_hw_rtn_e
ocs_hw_node_attach(ocs_hw_t * hw,ocs_remote_node_t * rnode,ocs_dma_t * sparms)2998 ocs_hw_node_attach(ocs_hw_t *hw, ocs_remote_node_t *rnode, ocs_dma_t *sparms)
2999 {
3000 	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
3001 	uint8_t		*buf = NULL;
3002 	uint32_t	count = 0;
3003 
3004 	if (!hw || !rnode || !sparms) {
3005 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p sparms=%p\n",
3006 			    hw, rnode, sparms);
3007 		return OCS_HW_RTN_ERROR;
3008 	}
3009 
3010 	/*
3011 	 * Check if the chip is in an error state (UE'd) before proceeding.
3012 	 */
3013 	if (sli_fw_error_status(&hw->sli) > 0) {
3014 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3015 		return OCS_HW_RTN_ERROR;
3016 	}
3017 
3018 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3019 	if (!buf) {
3020 		ocs_log_err(hw->os, "no buffer for command\n");
3021 		return OCS_HW_RTN_NO_MEMORY;
3022 	}
3023 
3024 	/*
3025 	 * If the attach count is non-zero, this RPI has already been registered.
3026 	 * Otherwise, register the RPI
3027 	 */
3028 	if (rnode->index == UINT32_MAX) {
3029 		ocs_log_err(NULL, "bad parameter rnode->index invalid\n");
3030 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3031 		return OCS_HW_RTN_ERROR;
3032 	}
3033 	count = ocs_atomic_add_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3034 	if (count) {
3035 		/*
3036 		 * Can't attach multiple FC_ID's to a node unless High Login
3037 		 * Mode is enabled
3038 		 */
3039 		if (sli_get_hlm(&hw->sli) == FALSE) {
3040 			ocs_log_test(hw->os, "attach to already attached node HLM=%d count=%d\n",
3041 					sli_get_hlm(&hw->sli), count);
3042 			rc = OCS_HW_RTN_SUCCESS;
3043 		} else {
3044 			rnode->node_group = TRUE;
3045 			rnode->attached = ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_attached);
3046 			rc = rnode->attached  ? OCS_HW_RTN_SUCCESS_SYNC : OCS_HW_RTN_SUCCESS;
3047 		}
3048 	} else {
3049 		rnode->node_group = FALSE;
3050 
3051 		ocs_display_sparams("", "reg rpi", 0, NULL, sparms->virt);
3052 		if (sli_cmd_reg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->fc_id,
3053 					rnode->indicator, rnode->sport->indicator,
3054 					sparms, 0, (hw->auto_xfer_rdy_enabled && hw->config.auto_xfer_rdy_t10_enable))) {
3055 			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT,
3056 					ocs_hw_cb_node_attach, rnode);
3057 		}
3058 	}
3059 
3060 	if (count || rc) {
3061 		if (rc < OCS_HW_RTN_SUCCESS) {
3062 			ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3063 			ocs_log_err(hw->os, "%s error\n", count ? "HLM" : "REG_RPI");
3064 		}
3065 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3066 	}
3067 
3068 	return rc;
3069 }
3070 
3071 /**
3072  * @ingroup node
3073  * @brief Free a remote node resource.
3074  *
3075  * @param hw Hardware context.
3076  * @param rnode Remote node object to free.
3077  *
3078  * @return Returns 0 on success, or a non-zero value on failure.
3079  */
3080 ocs_hw_rtn_e
ocs_hw_node_free_resources(ocs_hw_t * hw,ocs_remote_node_t * rnode)3081 ocs_hw_node_free_resources(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3082 {
3083 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3084 
3085 	if (!hw || !rnode) {
3086 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3087 			    hw, rnode);
3088 		return OCS_HW_RTN_ERROR;
3089 	}
3090 
3091 	if (rnode->sport) {
3092 		if (!rnode->attached) {
3093 			if (rnode->indicator != UINT32_MAX) {
3094 				if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3095 					ocs_log_err(hw->os, "FCOE_RPI free failure RPI %d addr=%#x\n",
3096 						    rnode->indicator, rnode->fc_id);
3097 					rc = OCS_HW_RTN_ERROR;
3098 				} else {
3099 					rnode->node_group = FALSE;
3100 					rnode->indicator = UINT32_MAX;
3101 					rnode->index = UINT32_MAX;
3102 					rnode->free_group = FALSE;
3103 				}
3104 			}
3105 		} else {
3106 			ocs_log_err(hw->os, "Error: rnode is still attached\n");
3107 			rc = OCS_HW_RTN_ERROR;
3108 		}
3109 	}
3110 
3111 	return rc;
3112 }
3113 
3114 
3115 /**
3116  * @ingroup node
3117  * @brief Free a remote node object.
3118  *
3119  * @param hw Hardware context.
3120  * @param rnode Remote node object to free.
3121  *
3122  * @return Returns 0 on success, or a non-zero value on failure.
3123  */
3124 ocs_hw_rtn_e
ocs_hw_node_detach(ocs_hw_t * hw,ocs_remote_node_t * rnode)3125 ocs_hw_node_detach(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3126 {
3127 	uint8_t	*buf = NULL;
3128 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS_SYNC;
3129 	uint32_t	index = UINT32_MAX;
3130 
3131 	if (!hw || !rnode) {
3132 		ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3133 			    hw, rnode);
3134 		return OCS_HW_RTN_ERROR;
3135 	}
3136 
3137 	/*
3138 	 * Check if the chip is in an error state (UE'd) before proceeding.
3139 	 */
3140 	if (sli_fw_error_status(&hw->sli) > 0) {
3141 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3142 		return OCS_HW_RTN_ERROR;
3143 	}
3144 
3145 	index = rnode->index;
3146 
3147 	if (rnode->sport) {
3148 		uint32_t	count = 0;
3149 		uint32_t	fc_id;
3150 
3151 		if (!rnode->attached) {
3152 			return OCS_HW_RTN_SUCCESS_SYNC;
3153 		}
3154 
3155 		buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3156 		if (!buf) {
3157 			ocs_log_err(hw->os, "no buffer for command\n");
3158 			return OCS_HW_RTN_NO_MEMORY;
3159 		}
3160 
3161 		count = ocs_atomic_sub_return(&hw->rpi_ref[index].rpi_count, 1);
3162 
3163 		if (count <= 1) {
3164 			/* There are no other references to this RPI
3165 			 * so unregister it and free the resource. */
3166 			fc_id = UINT32_MAX;
3167 			rnode->node_group = FALSE;
3168 			rnode->free_group = TRUE;
3169 		} else {
3170 			if (sli_get_hlm(&hw->sli) == FALSE) {
3171 				ocs_log_test(hw->os, "Invalid count with HLM disabled, count=%d\n",
3172 						count);
3173 			}
3174 			fc_id = rnode->fc_id & 0x00ffffff;
3175 		}
3176 
3177 		rc = OCS_HW_RTN_ERROR;
3178 
3179 		if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->indicator,
3180 					SLI_RSRC_FCOE_RPI, fc_id)) {
3181 			rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free, rnode);
3182 		}
3183 
3184 		if (rc != OCS_HW_RTN_SUCCESS) {
3185 			ocs_log_err(hw->os, "UNREG_RPI failed\n");
3186 			ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3187 			rc = OCS_HW_RTN_ERROR;
3188 		}
3189 	}
3190 
3191 	return rc;
3192 }
3193 
3194 /**
3195  * @ingroup node
3196  * @brief Free all remote node objects.
3197  *
3198  * @param hw Hardware context.
3199  *
3200  * @return Returns 0 on success, or a non-zero value on failure.
3201  */
3202 ocs_hw_rtn_e
ocs_hw_node_free_all(ocs_hw_t * hw)3203 ocs_hw_node_free_all(ocs_hw_t *hw)
3204 {
3205 	uint8_t	*buf = NULL;
3206 	ocs_hw_rtn_e	rc = OCS_HW_RTN_ERROR;
3207 
3208 	if (!hw) {
3209 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
3210 		return OCS_HW_RTN_ERROR;
3211 	}
3212 
3213 	/*
3214 	 * Check if the chip is in an error state (UE'd) before proceeding.
3215 	 */
3216 	if (sli_fw_error_status(&hw->sli) > 0) {
3217 		ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3218 		return OCS_HW_RTN_ERROR;
3219 	}
3220 
3221 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3222 	if (!buf) {
3223 		ocs_log_err(hw->os, "no buffer for command\n");
3224 		return OCS_HW_RTN_NO_MEMORY;
3225 	}
3226 
3227 	if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, 0xffff,
3228 				SLI_RSRC_FCOE_FCFI, UINT32_MAX)) {
3229 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free_all,
3230 				NULL);
3231 	}
3232 
3233 	if (rc != OCS_HW_RTN_SUCCESS) {
3234 		ocs_log_err(hw->os, "UNREG_RPI failed\n");
3235 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3236 		rc = OCS_HW_RTN_ERROR;
3237 	}
3238 
3239 	return rc;
3240 }
3241 
3242 ocs_hw_rtn_e
ocs_hw_node_group_alloc(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup)3243 ocs_hw_node_group_alloc(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3244 {
3245 
3246 	if (!hw || !ngroup) {
3247 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3248 				hw, ngroup);
3249 		return OCS_HW_RTN_ERROR;
3250 	}
3251 
3252 	if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &ngroup->indicator,
3253 				&ngroup->index)) {
3254 		ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
3255 				ngroup->indicator);
3256 		return OCS_HW_RTN_ERROR;
3257 	}
3258 
3259 	return OCS_HW_RTN_SUCCESS;
3260 }
3261 
3262 ocs_hw_rtn_e
ocs_hw_node_group_attach(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup,ocs_remote_node_t * rnode)3263 ocs_hw_node_group_attach(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup, ocs_remote_node_t *rnode)
3264 {
3265 
3266 	if (!hw || !ngroup || !rnode) {
3267 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p rnode=%p\n",
3268 			    hw, ngroup, rnode);
3269 		return OCS_HW_RTN_ERROR;
3270 	}
3271 
3272 	if (rnode->attached) {
3273 		ocs_log_err(hw->os, "node already attached RPI=%#x addr=%#x\n",
3274 			    rnode->indicator, rnode->fc_id);
3275 		return OCS_HW_RTN_ERROR;
3276 	}
3277 
3278 	if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3279 		ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3280 				rnode->indicator);
3281 		return OCS_HW_RTN_ERROR;
3282 	}
3283 
3284 	rnode->indicator = ngroup->indicator;
3285 	rnode->index = ngroup->index;
3286 
3287 	return OCS_HW_RTN_SUCCESS;
3288 }
3289 
3290 ocs_hw_rtn_e
ocs_hw_node_group_free(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup)3291 ocs_hw_node_group_free(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3292 {
3293 	int	ref;
3294 
3295 	if (!hw || !ngroup) {
3296 		ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3297 				hw, ngroup);
3298 		return OCS_HW_RTN_ERROR;
3299 	}
3300 
3301 	ref = ocs_atomic_read(&hw->rpi_ref[ngroup->index].rpi_count);
3302 	if (ref) {
3303 		/* Hmmm, the reference count is non-zero */
3304 		ocs_log_debug(hw->os, "node group reference=%d (RPI=%#x)\n",
3305 				ref, ngroup->indicator);
3306 
3307 		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, ngroup->indicator)) {
3308 			ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3309 				    ngroup->indicator);
3310 			return OCS_HW_RTN_ERROR;
3311 		}
3312 
3313 		ocs_atomic_set(&hw->rpi_ref[ngroup->index].rpi_count, 0);
3314 	}
3315 
3316 	ngroup->indicator = UINT32_MAX;
3317 	ngroup->index = UINT32_MAX;
3318 
3319 	return OCS_HW_RTN_SUCCESS;
3320 }
3321 
3322 /**
3323  * @brief Initialize IO fields on each free call.
3324  *
3325  * @n @b Note: This is done on each free call (as opposed to each
3326  * alloc call) because port-owned XRIs are not
3327  * allocated with ocs_hw_io_alloc() but are freed with this
3328  * function.
3329  *
3330  * @param io Pointer to HW IO.
3331  */
3332 static inline void
ocs_hw_init_free_io(ocs_hw_io_t * io)3333 ocs_hw_init_free_io(ocs_hw_io_t *io)
3334 {
3335 	/*
3336 	 * Set io->done to NULL, to avoid any callbacks, should
3337 	 * a completion be received for one of these IOs
3338 	 */
3339 	io->done = NULL;
3340 	io->abort_done = NULL;
3341 	io->status_saved = 0;
3342 	io->abort_in_progress = FALSE;
3343 	io->port_owned_abort_count = 0;
3344 	io->rnode = NULL;
3345 	io->type = 0xFFFF;
3346 	io->wq = NULL;
3347 	io->ul_io = NULL;
3348 	io->tgt_wqe_timeout = 0;
3349 }
3350 
3351 /**
3352  * @ingroup io
3353  * @brief Lockless allocate a HW IO object.
3354  *
3355  * @par Description
3356  * Assume that hw->ocs_lock is held. This function is only used if
3357  * use_dif_sec_xri workaround is being used.
3358  *
3359  * @param hw Hardware context.
3360  *
3361  * @return Returns a pointer to an object on success, or NULL on failure.
3362  */
3363 static inline ocs_hw_io_t *
_ocs_hw_io_alloc(ocs_hw_t * hw)3364 _ocs_hw_io_alloc(ocs_hw_t *hw)
3365 {
3366 	ocs_hw_io_t	*io = NULL;
3367 
3368 	if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
3369 		ocs_list_add_tail(&hw->io_inuse, io);
3370 		io->state = OCS_HW_IO_STATE_INUSE;
3371 		io->quarantine = FALSE;
3372 		io->quarantine_first_phase = TRUE;
3373 		io->abort_reqtag = UINT32_MAX;
3374 		ocs_ref_init(&io->ref, ocs_hw_io_free_internal, io);
3375 	} else {
3376 		ocs_atomic_add_return(&hw->io_alloc_failed_count, 1);
3377 	}
3378 
3379 	return io;
3380 }
3381 /**
3382  * @ingroup io
3383  * @brief Allocate a HW IO object.
3384  *
3385  * @par Description
3386  * @n @b Note: This function applies to non-port owned XRIs
3387  * only.
3388  *
3389  * @param hw Hardware context.
3390  *
3391  * @return Returns a pointer to an object on success, or NULL on failure.
3392  */
3393 ocs_hw_io_t *
ocs_hw_io_alloc(ocs_hw_t * hw)3394 ocs_hw_io_alloc(ocs_hw_t *hw)
3395 {
3396 	ocs_hw_io_t	*io = NULL;
3397 
3398 	ocs_lock(&hw->io_lock);
3399 		io = _ocs_hw_io_alloc(hw);
3400 	ocs_unlock(&hw->io_lock);
3401 
3402 	return io;
3403 }
3404 
3405 /**
3406  * @ingroup io
3407  * @brief Allocate/Activate a port owned HW IO object.
3408  *
3409  * @par Description
3410  * This function is called by the transport layer when an XRI is
3411  * allocated by the SLI-Port. This will "activate" the HW IO
3412  * associated with the XRI received from the SLI-Port to mirror
3413  * the state of the XRI.
3414  * @n @n @b Note: This function applies to port owned XRIs only.
3415  *
3416  * @param hw Hardware context.
3417  * @param io Pointer HW IO to activate/allocate.
3418  *
3419  * @return Returns a pointer to an object on success, or NULL on failure.
3420  */
3421 ocs_hw_io_t *
ocs_hw_io_activate_port_owned(ocs_hw_t * hw,ocs_hw_io_t * io)3422 ocs_hw_io_activate_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
3423 {
3424 	if (ocs_ref_read_count(&io->ref) > 0) {
3425 		ocs_log_err(hw->os, "Bad parameter: refcount > 0\n");
3426 		return NULL;
3427 	}
3428 
3429 	if (io->wq != NULL) {
3430 		ocs_log_err(hw->os, "XRI %x already in use\n", io->indicator);
3431 		return NULL;
3432 	}
3433 
3434 	ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3435 	io->xbusy = TRUE;
3436 
3437 	return io;
3438 }
3439 
3440 /**
3441  * @ingroup io
3442  * @brief When an IO is freed, depending on the exchange busy flag, and other
3443  * workarounds, move it to the correct list.
3444  *
3445  * @par Description
3446  * @n @b Note: Assumes that the hw->io_lock is held and the item has been removed
3447  * from the busy or wait_free list.
3448  *
3449  * @param hw Hardware context.
3450  * @param io Pointer to the IO object to move.
3451  */
3452 static void
ocs_hw_io_free_move_correct_list(ocs_hw_t * hw,ocs_hw_io_t * io)3453 ocs_hw_io_free_move_correct_list(ocs_hw_t *hw, ocs_hw_io_t *io)
3454 {
3455 	if (io->xbusy) {
3456 		/* add to wait_free list and wait for XRI_ABORTED CQEs to clean up */
3457 		ocs_list_add_tail(&hw->io_wait_free, io);
3458 		io->state = OCS_HW_IO_STATE_WAIT_FREE;
3459 	} else {
3460 		/* IO not busy, add to free list */
3461 		ocs_list_add_tail(&hw->io_free, io);
3462 		io->state = OCS_HW_IO_STATE_FREE;
3463 	}
3464 
3465 	/* BZ 161832 workaround */
3466 	if (hw->workaround.use_dif_sec_xri) {
3467 		ocs_hw_check_sec_hio_list(hw);
3468 	}
3469 }
3470 
3471 /**
3472  * @ingroup io
3473  * @brief Free a HW IO object. Perform cleanup common to
3474  * port and host-owned IOs.
3475  *
3476  * @param hw Hardware context.
3477  * @param io Pointer to the HW IO object.
3478  */
3479 static inline void
ocs_hw_io_free_common(ocs_hw_t * hw,ocs_hw_io_t * io)3480 ocs_hw_io_free_common(ocs_hw_t *hw, ocs_hw_io_t *io)
3481 {
3482 	/* initialize IO fields */
3483 	ocs_hw_init_free_io(io);
3484 
3485 	/* Restore default SGL */
3486 	ocs_hw_io_restore_sgl(hw, io);
3487 }
3488 
3489 /**
3490  * @ingroup io
3491  * @brief Free a HW IO object associated with a port-owned XRI.
3492  *
3493  * @param arg Pointer to the HW IO object.
3494  */
3495 static void
ocs_hw_io_free_port_owned(void * arg)3496 ocs_hw_io_free_port_owned(void *arg)
3497 {
3498 	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3499 	ocs_hw_t *hw = io->hw;
3500 
3501 	/*
3502 	 * For auto xfer rdy, if the dnrx bit is set, then add it to the list of XRIs
3503 	 * waiting for buffers.
3504 	 */
3505 	if (io->auto_xfer_rdy_dnrx) {
3506 		ocs_lock(&hw->io_lock);
3507 			/* take a reference count because we still own the IO until the buffer is posted */
3508 			ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3509 			ocs_list_add_tail(&hw->io_port_dnrx, io);
3510 		ocs_unlock(&hw->io_lock);
3511 	}
3512 
3513 	/* perform common cleanup */
3514 	ocs_hw_io_free_common(hw, io);
3515 }
3516 
3517 /**
3518  * @ingroup io
3519  * @brief Free a previously-allocated HW IO object. Called when
3520  * IO refcount goes to zero (host-owned IOs only).
3521  *
3522  * @param arg Pointer to the HW IO object.
3523  */
3524 static void
ocs_hw_io_free_internal(void * arg)3525 ocs_hw_io_free_internal(void *arg)
3526 {
3527 	ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3528 	ocs_hw_t *hw = io->hw;
3529 
3530 	/* perform common cleanup */
3531 	ocs_hw_io_free_common(hw, io);
3532 
3533 	ocs_lock(&hw->io_lock);
3534 		/* remove from in-use list */
3535 		ocs_list_remove(&hw->io_inuse, io);
3536 		ocs_hw_io_free_move_correct_list(hw, io);
3537 	ocs_unlock(&hw->io_lock);
3538 }
3539 
3540 /**
3541  * @ingroup io
3542  * @brief Free a previously-allocated HW IO object.
3543  *
3544  * @par Description
3545  * @n @b Note: This function applies to port and host owned XRIs.
3546  *
3547  * @param hw Hardware context.
3548  * @param io Pointer to the HW IO object.
3549  *
3550  * @return Returns a non-zero value if HW IO was freed, 0 if references
3551  * on the IO still exist, or a negative value if an error occurred.
3552  */
3553 int32_t
ocs_hw_io_free(ocs_hw_t * hw,ocs_hw_io_t * io)3554 ocs_hw_io_free(ocs_hw_t *hw, ocs_hw_io_t *io)
3555 {
3556 	/* just put refcount */
3557 	if (ocs_ref_read_count(&io->ref) <= 0) {
3558 		ocs_log_err(hw->os, "Bad parameter: refcount <= 0 xri=%x tag=%x\n",
3559 			    io->indicator, io->reqtag);
3560 		return -1;
3561 	}
3562 
3563 	return ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_hw_io_alloc() */
3564 }
3565 
3566 /**
3567  * @ingroup io
3568  * @brief Check if given HW IO is in-use
3569  *
3570  * @par Description
3571  * This function returns TRUE if the given HW IO has been
3572  * allocated and is in-use, and FALSE otherwise. It applies to
3573  * port and host owned XRIs.
3574  *
3575  * @param hw Hardware context.
3576  * @param io Pointer to the HW IO object.
3577  *
3578  * @return TRUE if an IO is in use, or FALSE otherwise.
3579  */
3580 uint8_t
ocs_hw_io_inuse(ocs_hw_t * hw,ocs_hw_io_t * io)3581 ocs_hw_io_inuse(ocs_hw_t *hw, ocs_hw_io_t *io)
3582 {
3583 	return (ocs_ref_read_count(&io->ref) > 0);
3584 }
3585 
3586 /**
3587  * @brief Write a HW IO to a work queue.
3588  *
3589  * @par Description
3590  * A HW IO is written to a work queue.
3591  *
3592  * @param wq Pointer to work queue.
3593  * @param wqe Pointer to WQ entry.
3594  *
3595  * @n @b Note: Assumes the SLI-4 queue lock is held.
3596  *
3597  * @return Returns 0 on success, or a negative error code value on failure.
3598  */
3599 static int32_t
_hw_wq_write(hw_wq_t * wq,ocs_hw_wqe_t * wqe)3600 _hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3601 {
3602 	int32_t rc;
3603 	int32_t queue_rc;
3604 
3605 	/* Every so often, set the wqec bit to generate comsummed completions */
3606 	if (wq->wqec_count) {
3607 		wq->wqec_count--;
3608 	}
3609 	if (wq->wqec_count == 0) {
3610 		sli4_generic_wqe_t *genwqe = (void*)wqe->wqebuf;
3611 		genwqe->wqec = 1;
3612 		wq->wqec_count = wq->wqec_set_count;
3613 	}
3614 
3615 	/* Decrement WQ free count */
3616 	wq->free_count--;
3617 
3618 	queue_rc = _sli_queue_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
3619 
3620 	if (queue_rc < 0) {
3621 		rc = -1;
3622 	} else {
3623 		rc = 0;
3624 		ocs_queue_history_wq(&wq->hw->q_hist, (void *) wqe->wqebuf, wq->queue->id, queue_rc);
3625 	}
3626 
3627 	return rc;
3628 }
3629 
3630 /**
3631  * @brief Write a HW IO to a work queue.
3632  *
3633  * @par Description
3634  * A HW IO is written to a work queue.
3635  *
3636  * @param wq Pointer to work queue.
3637  * @param wqe Pointer to WQE entry.
3638  *
3639  * @n @b Note: Takes the SLI-4 queue lock.
3640  *
3641  * @return Returns 0 on success, or a negative error code value on failure.
3642  */
3643 int32_t
hw_wq_write(hw_wq_t * wq,ocs_hw_wqe_t * wqe)3644 hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3645 {
3646 	int32_t rc = 0;
3647 
3648 	sli_queue_lock(wq->queue);
3649 		if ( ! ocs_list_empty(&wq->pending_list)) {
3650 			ocs_list_add_tail(&wq->pending_list, wqe);
3651 			OCS_STAT(wq->wq_pending_count++;)
3652 			while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3653 				rc = _hw_wq_write(wq, wqe);
3654 				if (rc < 0) {
3655 					break;
3656 				}
3657 				if (wqe->abort_wqe_submit_needed) {
3658 					wqe->abort_wqe_submit_needed = 0;
3659 					sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3660 							wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT );
3661 					ocs_list_add_tail(&wq->pending_list, wqe);
3662 					OCS_STAT(wq->wq_pending_count++;)
3663 				}
3664 			}
3665 		} else {
3666 			if (wq->free_count > 0) {
3667 				rc = _hw_wq_write(wq, wqe);
3668 			} else {
3669 				ocs_list_add_tail(&wq->pending_list, wqe);
3670 				OCS_STAT(wq->wq_pending_count++;)
3671 			}
3672 		}
3673 
3674 	sli_queue_unlock(wq->queue);
3675 
3676 	return rc;
3677 
3678 }
3679 
3680 /**
3681  * @brief Update free count and submit any pending HW IOs
3682  *
3683  * @par Description
3684  * The WQ free count is updated, and any pending HW IOs are submitted that
3685  * will fit in the queue.
3686  *
3687  * @param wq Pointer to work queue.
3688  * @param update_free_count Value added to WQs free count.
3689  *
3690  * @return None.
3691  */
3692 static void
hw_wq_submit_pending(hw_wq_t * wq,uint32_t update_free_count)3693 hw_wq_submit_pending(hw_wq_t *wq, uint32_t update_free_count)
3694 {
3695 	ocs_hw_wqe_t *wqe;
3696 
3697 	sli_queue_lock(wq->queue);
3698 
3699 		/* Update free count with value passed in */
3700 		wq->free_count += update_free_count;
3701 
3702 		while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3703 			_hw_wq_write(wq, wqe);
3704 
3705 			if (wqe->abort_wqe_submit_needed) {
3706 				wqe->abort_wqe_submit_needed = 0;
3707 				sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3708 						wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT);
3709 				ocs_list_add_tail(&wq->pending_list, wqe);
3710 				OCS_STAT(wq->wq_pending_count++;)
3711 			}
3712 		}
3713 
3714 	sli_queue_unlock(wq->queue);
3715 }
3716 
3717 /**
3718  * @brief Check to see if there are any BZ 161832 workaround waiting IOs
3719  *
3720  * @par Description
3721  * Checks hw->sec_hio_wait_list, if an IO is waiting for a HW IO, then try
3722  * to allocate a secondary HW io, and dispatch it.
3723  *
3724  * @n @b Note: hw->io_lock MUST be taken when called.
3725  *
3726  * @param hw pointer to HW object
3727  *
3728  * @return none
3729  */
3730 static void
ocs_hw_check_sec_hio_list(ocs_hw_t * hw)3731 ocs_hw_check_sec_hio_list(ocs_hw_t *hw)
3732 {
3733 	ocs_hw_io_t *io;
3734 	ocs_hw_io_t *sec_io;
3735 	int rc = 0;
3736 
3737 	while (!ocs_list_empty(&hw->sec_hio_wait_list)) {
3738 		uint16_t flags;
3739 
3740 		sec_io = _ocs_hw_io_alloc(hw);
3741 		if (sec_io == NULL) {
3742 			break;
3743 		}
3744 
3745 		io = ocs_list_remove_head(&hw->sec_hio_wait_list);
3746 		ocs_list_add_tail(&hw->io_inuse, io);
3747 		io->state = OCS_HW_IO_STATE_INUSE;
3748 		io->sec_hio = sec_io;
3749 
3750 		/* mark secondary XRI for second and subsequent data phase as quarantine */
3751 		if (io->xbusy) {
3752 			sec_io->quarantine = TRUE;
3753 		}
3754 
3755 		flags = io->sec_iparam.fcp_tgt.flags;
3756 		if (io->xbusy) {
3757 			flags |= SLI4_IO_CONTINUATION;
3758 		} else {
3759 			flags &= ~SLI4_IO_CONTINUATION;
3760 		}
3761 
3762 		io->tgt_wqe_timeout = io->sec_iparam.fcp_tgt.timeout;
3763 
3764 		/* Complete (continue) TRECV IO */
3765 		if (io->xbusy) {
3766 			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3767 				io->first_data_sge,
3768 				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator, io->sec_hio->indicator,
3769 				io->reqtag, SLI4_CQ_DEFAULT,
3770 				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3771 				flags,
3772 				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)) {
3773 					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3774 					break;
3775 			}
3776 		} else {
3777 			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3778 				io->first_data_sge,
3779 				io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator,
3780 				io->reqtag, SLI4_CQ_DEFAULT,
3781 				io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3782 				flags,
3783 				io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size,
3784 				io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3785 					ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3786 					break;
3787 			}
3788 		}
3789 
3790 		if (io->wq == NULL) {
3791 			io->wq = ocs_hw_queue_next_wq(hw, io);
3792 			ocs_hw_assert(io->wq != NULL);
3793 		}
3794 		io->xbusy = TRUE;
3795 
3796 		/*
3797 		 * Add IO to active io wqe list before submitting, in case the
3798 		 * wcqe processing preempts this thread.
3799 		 */
3800 		ocs_hw_add_io_timed_wqe(hw, io);
3801 		rc = hw_wq_write(io->wq, &io->wqe);
3802 		if (rc >= 0) {
3803 			/* non-negative return is success */
3804 			rc = 0;
3805 		} else {
3806 			/* failed to write wqe, remove from active wqe list */
3807 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
3808 			io->xbusy = FALSE;
3809 			ocs_hw_remove_io_timed_wqe(hw, io);
3810 		}
3811 	}
3812 }
3813 
3814 /**
3815  * @ingroup io
3816  * @brief Send a Single Request/Response Sequence (SRRS).
3817  *
3818  * @par Description
3819  * This routine supports communication sequences consisting of a single
3820  * request and single response between two endpoints. Examples include:
3821  *  - Sending an ELS request.
3822  *  - Sending an ELS response - To send an ELS reponse, the caller must provide
3823  * the OX_ID from the received request.
3824  *  - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
3825  * the caller must provide the R_CTL, TYPE, and DF_CTL
3826  * values to place in the FC frame header.
3827  *  .
3828  * @n @b Note: The caller is expected to provide both send and receive
3829  * buffers for requests. In the case of sending a response, no receive buffer
3830  * is necessary and the caller may pass in a NULL pointer.
3831  *
3832  * @param hw Hardware context.
3833  * @param type Type of sequence (ELS request/response, FC-CT).
3834  * @param io Previously-allocated HW IO object.
3835  * @param send DMA memory holding data to send (for example, ELS request, BLS response).
3836  * @param len Length, in bytes, of data to send.
3837  * @param receive Optional DMA memory to hold a response.
3838  * @param rnode Destination of data (that is, a remote node).
3839  * @param iparam IO parameters (ELS response and FC-CT).
3840  * @param cb Function call upon completion of sending the data (may be NULL).
3841  * @param arg Argument to pass to IO completion function.
3842  *
3843  * @return Returns 0 on success, or a non-zero on failure.
3844  */
3845 ocs_hw_rtn_e
ocs_hw_srrs_send(ocs_hw_t * hw,ocs_hw_io_type_e type,ocs_hw_io_t * io,ocs_dma_t * send,uint32_t len,ocs_dma_t * receive,ocs_remote_node_t * rnode,ocs_hw_io_param_t * iparam,ocs_hw_srrs_cb_t cb,void * arg)3846 ocs_hw_srrs_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
3847 		  ocs_dma_t *send, uint32_t len, ocs_dma_t *receive,
3848 		  ocs_remote_node_t *rnode, ocs_hw_io_param_t *iparam,
3849 		  ocs_hw_srrs_cb_t cb, void *arg)
3850 {
3851 	sli4_sge_t	*sge = NULL;
3852 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
3853 	uint16_t	local_flags = 0;
3854 
3855 	if (!hw || !io || !rnode || !iparam) {
3856 		ocs_log_err(NULL, "bad parm hw=%p io=%p send=%p receive=%p rnode=%p iparam=%p\n",
3857 			    hw, io, send, receive, rnode, iparam);
3858 		return OCS_HW_RTN_ERROR;
3859 	}
3860 
3861 	if (hw->state != OCS_HW_STATE_ACTIVE) {
3862 		ocs_log_test(hw->os, "cannot send SRRS, HW state=%d\n", hw->state);
3863 		return OCS_HW_RTN_ERROR;
3864 	}
3865 
3866 	if (ocs_hw_is_xri_port_owned(hw, io->indicator)) {
3867 		/* We must set the XC bit for port owned XRIs */
3868 		local_flags |= SLI4_IO_CONTINUATION;
3869 	}
3870 	io->rnode = rnode;
3871 	io->type  = type;
3872 	io->done = cb;
3873 	io->arg  = arg;
3874 
3875 	sge = io->sgl->virt;
3876 
3877 	/* clear both SGE */
3878 	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
3879 
3880 	if (send) {
3881 		sge[0].buffer_address_high = ocs_addr32_hi(send->phys);
3882 		sge[0].buffer_address_low  = ocs_addr32_lo(send->phys);
3883 		sge[0].sge_type = SLI4_SGE_TYPE_DATA;
3884 		sge[0].buffer_length = len;
3885 	}
3886 
3887 	if ((OCS_HW_ELS_REQ == type) || (OCS_HW_FC_CT == type)) {
3888 		sge[1].buffer_address_high = ocs_addr32_hi(receive->phys);
3889 		sge[1].buffer_address_low  = ocs_addr32_lo(receive->phys);
3890 		sge[1].sge_type = SLI4_SGE_TYPE_DATA;
3891 		sge[1].buffer_length = receive->size;
3892 		sge[1].last = TRUE;
3893 	} else {
3894 		sge[0].last = TRUE;
3895 	}
3896 
3897 	switch (type) {
3898 	case OCS_HW_ELS_REQ:
3899 		if ( (!send) || sli_els_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl,
3900 							*((uint8_t *)(send->virt)), /* req_type */
3901 							len, receive->size,
3902 							iparam->els.timeout, io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rnode)) {
3903 			ocs_log_err(hw->os, "REQ WQE error\n");
3904 			rc = OCS_HW_RTN_ERROR;
3905 		}
3906 		break;
3907 	case OCS_HW_ELS_RSP:
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.ox_id,
3911 							rnode, local_flags, UINT32_MAX)) {
3912 			ocs_log_err(hw->os, "RSP WQE error\n");
3913 			rc = OCS_HW_RTN_ERROR;
3914 		}
3915 		break;
3916 	case OCS_HW_ELS_RSP_SID:
3917 		if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3918 					   io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3919 					   iparam->els_sid.ox_id,
3920 							rnode, local_flags, iparam->els_sid.s_id)) {
3921 			ocs_log_err(hw->os, "RSP (SID) WQE error\n");
3922 			rc = OCS_HW_RTN_ERROR;
3923 		}
3924 		break;
3925 	case OCS_HW_FC_CT:
3926 		if ( (!send) || sli_gen_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3927 					  receive->size, iparam->fc_ct.timeout, io->indicator,
3928 					  io->reqtag, SLI4_CQ_DEFAULT, rnode, iparam->fc_ct.r_ctl,
3929 					  iparam->fc_ct.type, iparam->fc_ct.df_ctl)) {
3930 			ocs_log_err(hw->os, "GEN WQE error\n");
3931 			rc = OCS_HW_RTN_ERROR;
3932 		}
3933 		break;
3934 	case OCS_HW_FC_CT_RSP:
3935 		if ( (!send) || sli_xmit_sequence64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3936 					  iparam->fc_ct_rsp.timeout, iparam->fc_ct_rsp.ox_id, io->indicator,
3937 					  io->reqtag, rnode, iparam->fc_ct_rsp.r_ctl,
3938 					  iparam->fc_ct_rsp.type, iparam->fc_ct_rsp.df_ctl)) {
3939 			ocs_log_err(hw->os, "XMIT SEQ WQE error\n");
3940 			rc = OCS_HW_RTN_ERROR;
3941 		}
3942 		break;
3943 	case OCS_HW_BLS_ACC:
3944 	case OCS_HW_BLS_RJT:
3945 	{
3946 		sli_bls_payload_t	bls;
3947 
3948 		if (OCS_HW_BLS_ACC == type) {
3949 			bls.type = SLI_BLS_ACC;
3950 			ocs_memcpy(&bls.u.acc, iparam->bls.payload, sizeof(bls.u.acc));
3951 		} else {
3952 			bls.type = SLI_BLS_RJT;
3953 			ocs_memcpy(&bls.u.rjt, iparam->bls.payload, sizeof(bls.u.rjt));
3954 		}
3955 
3956 		bls.ox_id = iparam->bls.ox_id;
3957 		bls.rx_id = iparam->bls.rx_id;
3958 
3959 		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3960 					   io->indicator, io->reqtag,
3961 					   SLI4_CQ_DEFAULT,
3962 					   rnode, UINT32_MAX)) {
3963 			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
3964 			rc = OCS_HW_RTN_ERROR;
3965 		}
3966 		break;
3967 	}
3968 	case OCS_HW_BLS_ACC_SID:
3969 	{
3970 		sli_bls_payload_t	bls;
3971 
3972 		bls.type = SLI_BLS_ACC;
3973 		ocs_memcpy(&bls.u.acc, iparam->bls_sid.payload, sizeof(bls.u.acc));
3974 
3975 		bls.ox_id = iparam->bls_sid.ox_id;
3976 		bls.rx_id = iparam->bls_sid.rx_id;
3977 
3978 		if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3979 					   io->indicator, io->reqtag,
3980 					   SLI4_CQ_DEFAULT,
3981 					   rnode, iparam->bls_sid.s_id)) {
3982 			ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE SID error\n");
3983 			rc = OCS_HW_RTN_ERROR;
3984 		}
3985 		break;
3986 	}
3987 	case OCS_HW_BCAST:
3988 		if ( (!send) || sli_xmit_bcast64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3989 					iparam->bcast.timeout, io->indicator, io->reqtag,
3990 					SLI4_CQ_DEFAULT, rnode,
3991 					iparam->bcast.r_ctl, iparam->bcast.type, iparam->bcast.df_ctl)) {
3992 			ocs_log_err(hw->os, "XMIT_BCAST64 WQE error\n");
3993 			rc = OCS_HW_RTN_ERROR;
3994 		}
3995 		break;
3996 	default:
3997 		ocs_log_err(hw->os, "bad SRRS type %#x\n", type);
3998 		rc = OCS_HW_RTN_ERROR;
3999 	}
4000 
4001 	if (OCS_HW_RTN_SUCCESS == rc) {
4002 		if (io->wq == NULL) {
4003 			io->wq = ocs_hw_queue_next_wq(hw, io);
4004 			ocs_hw_assert(io->wq != NULL);
4005 		}
4006 		io->xbusy = TRUE;
4007 
4008 		/*
4009 		 * Add IO to active io wqe list before submitting, in case the
4010 		 * wcqe processing preempts this thread.
4011 		 */
4012 		OCS_STAT(io->wq->use_count++);
4013 		ocs_hw_add_io_timed_wqe(hw, io);
4014 		rc = hw_wq_write(io->wq, &io->wqe);
4015 		if (rc >= 0) {
4016 			/* non-negative return is success */
4017 			rc = 0;
4018 		} else {
4019 			/* failed to write wqe, remove from active wqe list */
4020 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4021 			io->xbusy = FALSE;
4022 			ocs_hw_remove_io_timed_wqe(hw, io);
4023 		}
4024 	}
4025 
4026 	return rc;
4027 }
4028 
4029 /**
4030  * @ingroup io
4031  * @brief Send a read, write, or response IO.
4032  *
4033  * @par Description
4034  * This routine supports sending a higher-level IO (for example, FCP) between two endpoints
4035  * as a target or initiator. Examples include:
4036  *  - Sending read data and good response (target).
4037  *  - Sending a response (target with no data or after receiving write data).
4038  *  .
4039  * This routine assumes all IOs use the SGL associated with the HW IO. Prior to
4040  * calling this routine, the data should be loaded using ocs_hw_io_add_sge().
4041  *
4042  * @param hw Hardware context.
4043  * @param type Type of IO (target read, target response, and so on).
4044  * @param io Previously-allocated HW IO object.
4045  * @param len Length, in bytes, of data to send.
4046  * @param iparam IO parameters.
4047  * @param rnode Destination of data (that is, a remote node).
4048  * @param cb Function call upon completion of sending data (may be NULL).
4049  * @param arg Argument to pass to IO completion function.
4050  *
4051  * @return Returns 0 on success, or a non-zero value on failure.
4052  *
4053  * @todo
4054  *  - Support specifiying relative offset.
4055  *  - Use a WQ other than 0.
4056  */
4057 ocs_hw_rtn_e
ocs_hw_io_send(ocs_hw_t * hw,ocs_hw_io_type_e type,ocs_hw_io_t * io,uint32_t len,ocs_hw_io_param_t * iparam,ocs_remote_node_t * rnode,void * cb,void * arg)4058 ocs_hw_io_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
4059 		uint32_t len, ocs_hw_io_param_t *iparam, ocs_remote_node_t *rnode,
4060 		void *cb, void *arg)
4061 {
4062 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4063 	uint32_t	rpi;
4064 	uint8_t		send_wqe = TRUE;
4065 
4066 	CPUTRACE("");
4067 
4068 	if (!hw || !io || !rnode || !iparam) {
4069 		ocs_log_err(NULL, "bad parm hw=%p io=%p iparam=%p rnode=%p\n",
4070 			    hw, io, iparam, rnode);
4071 		return OCS_HW_RTN_ERROR;
4072 	}
4073 
4074 	if (hw->state != OCS_HW_STATE_ACTIVE) {
4075 		ocs_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
4076 		return OCS_HW_RTN_ERROR;
4077 	}
4078 
4079 	rpi = rnode->indicator;
4080 
4081 	if (hw->workaround.use_unregistered_rpi && (rpi == UINT32_MAX)) {
4082 		rpi = hw->workaround.unregistered_rid;
4083 		ocs_log_test(hw->os, "using unregistered RPI: %d\n", rpi);
4084 	}
4085 
4086 	/*
4087 	 * Save state needed during later stages
4088 	 */
4089 	io->rnode = rnode;
4090 	io->type  = type;
4091 	io->done  = cb;
4092 	io->arg   = arg;
4093 
4094 	/*
4095 	 * Format the work queue entry used to send the IO
4096 	 */
4097 	switch (type) {
4098 	case OCS_HW_IO_INITIATOR_READ:
4099 		/*
4100 		 * If use_dif_quarantine workaround is in effect, and dif_separates then mark the
4101 		 * initiator read IO for quarantine
4102 		 */
4103 		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4104 		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4105 			io->quarantine = TRUE;
4106 		}
4107 
4108 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4109 				iparam->fcp_ini.rsp);
4110 
4111 		if (sli_fcp_iread64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge, len,
4112 					io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rpi, rnode,
4113 					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4114 					iparam->fcp_ini.timeout)) {
4115 			ocs_log_err(hw->os, "IREAD WQE error\n");
4116 			rc = OCS_HW_RTN_ERROR;
4117 		}
4118 		break;
4119 	case OCS_HW_IO_INITIATOR_WRITE:
4120 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4121 				iparam->fcp_ini.rsp);
4122 
4123 		if (sli_fcp_iwrite64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4124 					 len, iparam->fcp_ini.first_burst,
4125 					 io->indicator, io->reqtag,
4126 					SLI4_CQ_DEFAULT, rpi, rnode,
4127 					iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4128 					iparam->fcp_ini.timeout)) {
4129 			ocs_log_err(hw->os, "IWRITE WQE error\n");
4130 			rc = OCS_HW_RTN_ERROR;
4131 		}
4132 		break;
4133 	case OCS_HW_IO_INITIATOR_NODATA:
4134 		ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4135 				iparam->fcp_ini.rsp);
4136 
4137 		if (sli_fcp_icmnd64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
4138 					io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
4139 					rpi, rnode, iparam->fcp_ini.timeout)) {
4140 			ocs_log_err(hw->os, "ICMND WQE error\n");
4141 			rc = OCS_HW_RTN_ERROR;
4142 		}
4143 		break;
4144 	case OCS_HW_IO_TARGET_WRITE: {
4145 		uint16_t flags = iparam->fcp_tgt.flags;
4146 		fcp_xfer_rdy_iu_t *xfer = io->xfer_rdy.virt;
4147 
4148 		/*
4149 		 * Fill in the XFER_RDY for IF_TYPE 0 devices
4150 		 */
4151 		*((uint32_t *)xfer->fcp_data_ro) = ocs_htobe32(iparam->fcp_tgt.offset);
4152 		*((uint32_t *)xfer->fcp_burst_len) = ocs_htobe32(len);
4153 		*((uint32_t *)xfer->rsvd) = 0;
4154 
4155 		if (io->xbusy) {
4156 			flags |= SLI4_IO_CONTINUATION;
4157 		} else {
4158 			flags &= ~SLI4_IO_CONTINUATION;
4159 		}
4160 
4161 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4162 
4163 		/*
4164 		 * If use_dif_quarantine workaround is in effect, and this is a DIF enabled IO
4165 		 * then mark the target write IO for quarantine
4166 		 */
4167 		if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4168 		    (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4169 			io->quarantine = TRUE;
4170 		}
4171 
4172 		/*
4173 		 * BZ 161832 Workaround:
4174 		 * Check for use_dif_sec_xri workaround.  Note, even though the first dataphase
4175 		 * doesn't really need a secondary XRI, we allocate one anyway, as this avoids the
4176 		 * potential for deadlock where all XRI's are allocated as primaries to IOs that
4177 		 * are on hw->sec_hio_wait_list.   If this secondary XRI is not for the first
4178 		 * data phase, it is marked for quarantine.
4179 		 */
4180 		if (hw->workaround.use_dif_sec_xri && (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4181 
4182 			/*
4183 			 * If we have allocated a chained SGL for skyhawk, then
4184 			 * we can re-use this for the sec_hio.
4185 			 */
4186 			if (io->ovfl_io != NULL) {
4187 				io->sec_hio = io->ovfl_io;
4188 				io->sec_hio->quarantine = TRUE;
4189 			} else {
4190 				io->sec_hio = ocs_hw_io_alloc(hw);
4191 			}
4192 			if (io->sec_hio == NULL) {
4193 				/* Failed to allocate, so save full request context and put
4194 				 * this IO on the wait list
4195 				 */
4196 				io->sec_iparam = *iparam;
4197 				io->sec_len = len;
4198 				ocs_lock(&hw->io_lock);
4199 					ocs_list_remove(&hw->io_inuse,  io);
4200 					ocs_list_add_tail(&hw->sec_hio_wait_list, io);
4201 					io->state = OCS_HW_IO_STATE_WAIT_SEC_HIO;
4202 					hw->sec_hio_wait_count++;
4203 				ocs_unlock(&hw->io_lock);
4204 				send_wqe = FALSE;
4205 				/* Done */
4206 				break;
4207 			}
4208 			/* We quarantine the secondary IO if this is the second or subsequent data phase */
4209 			if (io->xbusy) {
4210 				io->sec_hio->quarantine = TRUE;
4211 			}
4212 		}
4213 
4214 		/*
4215 		 * If not the first data phase, and io->sec_hio has been allocated, then issue
4216 		 * FCP_CONT_TRECEIVE64 WQE, otherwise use the usual FCP_TRECEIVE64 WQE
4217 		 */
4218 		if (io->xbusy && (io->sec_hio != NULL)) {
4219 			if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4220 						   iparam->fcp_tgt.offset, len, io->indicator, io->sec_hio->indicator,
4221 						   io->reqtag, SLI4_CQ_DEFAULT,
4222 						   iparam->fcp_tgt.ox_id, rpi, rnode,
4223 						   flags,
4224 						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4225 						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4226 				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4227 				rc = OCS_HW_RTN_ERROR;
4228 			}
4229 		} else {
4230 			if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4231 						   iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4232 						   SLI4_CQ_DEFAULT,
4233 						   iparam->fcp_tgt.ox_id, rpi, rnode,
4234 						   flags,
4235 						   iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4236 						   iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4237 				ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4238 				rc = OCS_HW_RTN_ERROR;
4239 			}
4240 		}
4241 		break;
4242 	}
4243 	case OCS_HW_IO_TARGET_READ: {
4244 		uint16_t flags = iparam->fcp_tgt.flags;
4245 
4246 		if (io->xbusy) {
4247 			flags |= SLI4_IO_CONTINUATION;
4248 		} else {
4249 			flags &= ~SLI4_IO_CONTINUATION;
4250 		}
4251 
4252 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4253 		if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4254 					iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4255 					SLI4_CQ_DEFAULT,
4256 					iparam->fcp_tgt.ox_id, rpi, rnode,
4257 					flags,
4258 					iparam->fcp_tgt.dif_oper,
4259 					iparam->fcp_tgt.blk_size,
4260 					iparam->fcp_tgt.cs_ctl,
4261 					iparam->fcp_tgt.app_id)) {
4262 			ocs_log_err(hw->os, "TSEND WQE error\n");
4263 			rc = OCS_HW_RTN_ERROR;
4264 		} else if (hw->workaround.retain_tsend_io_length) {
4265 			io->length = len;
4266 		}
4267 		break;
4268 	}
4269 	case OCS_HW_IO_TARGET_RSP: {
4270 		uint16_t flags = iparam->fcp_tgt.flags;
4271 
4272 		if (io->xbusy) {
4273 			flags |= SLI4_IO_CONTINUATION;
4274 		} else {
4275 			flags &= ~SLI4_IO_CONTINUATION;
4276 		}
4277 
4278 		/* post a new auto xfer ready buffer */
4279 		if (hw->auto_xfer_rdy_enabled && io->is_port_owned) {
4280 			if ((io->auto_xfer_rdy_dnrx = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1))) {
4281 				flags |= SLI4_IO_DNRX;
4282 			}
4283 		}
4284 
4285 		io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4286 		if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size,
4287 					&io->def_sgl,
4288 					len,
4289 					io->indicator, io->reqtag,
4290 					SLI4_CQ_DEFAULT,
4291 					iparam->fcp_tgt.ox_id,
4292 					rpi, rnode,
4293 					flags, iparam->fcp_tgt.cs_ctl,
4294 					io->is_port_owned,
4295 					iparam->fcp_tgt.app_id)) {
4296 			ocs_log_err(hw->os, "TRSP WQE error\n");
4297 			rc = OCS_HW_RTN_ERROR;
4298 		}
4299 
4300 		break;
4301 	}
4302 	default:
4303 		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4304 		rc = OCS_HW_RTN_ERROR;
4305 	}
4306 
4307 	if (send_wqe && (OCS_HW_RTN_SUCCESS == rc)) {
4308 		if (io->wq == NULL) {
4309 			io->wq = ocs_hw_queue_next_wq(hw, io);
4310 			ocs_hw_assert(io->wq != NULL);
4311 		}
4312 
4313 		io->xbusy = TRUE;
4314 
4315 		/*
4316 		 * Add IO to active io wqe list before submitting, in case the
4317 		 * wcqe processing preempts this thread.
4318 		 */
4319 		OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
4320 		OCS_STAT(io->wq->use_count++);
4321 		ocs_hw_add_io_timed_wqe(hw, io);
4322 		rc = hw_wq_write(io->wq, &io->wqe);
4323 		if (rc >= 0) {
4324 			/* non-negative return is success */
4325 			rc = 0;
4326 		} else {
4327 			/* failed to write wqe, remove from active wqe list */
4328 			ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4329 			io->xbusy = FALSE;
4330 			ocs_hw_remove_io_timed_wqe(hw, io);
4331 		}
4332 	}
4333 
4334 	return rc;
4335 }
4336 
4337 /**
4338  * @brief Send a raw frame
4339  *
4340  * @par Description
4341  * Using the SEND_FRAME_WQE, a frame consisting of header and payload is sent.
4342  *
4343  * @param hw Pointer to HW object.
4344  * @param hdr Pointer to a little endian formatted FC header.
4345  * @param sof Value to use as the frame SOF.
4346  * @param eof Value to use as the frame EOF.
4347  * @param payload Pointer to payload DMA buffer.
4348  * @param ctx Pointer to caller provided send frame context.
4349  * @param callback Callback function.
4350  * @param arg Callback function argument.
4351  *
4352  * @return Returns 0 on success, or a negative error code value on failure.
4353  */
4354 ocs_hw_rtn_e
ocs_hw_send_frame(ocs_hw_t * hw,fc_header_le_t * hdr,uint8_t sof,uint8_t eof,ocs_dma_t * payload,ocs_hw_send_frame_context_t * ctx,void (* callback)(void * arg,uint8_t * cqe,int32_t status),void * arg)4355 ocs_hw_send_frame(ocs_hw_t *hw, fc_header_le_t *hdr, uint8_t sof, uint8_t eof, ocs_dma_t *payload,
4356 		   ocs_hw_send_frame_context_t *ctx, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
4357 {
4358 	int32_t rc;
4359 	ocs_hw_wqe_t *wqe;
4360 	uint32_t xri;
4361 	hw_wq_t *wq;
4362 
4363 	wqe = &ctx->wqe;
4364 
4365 	/* populate the callback object */
4366 	ctx->hw = hw;
4367 
4368 	/* Fetch and populate request tag */
4369 	ctx->wqcb = ocs_hw_reqtag_alloc(hw, callback, arg);
4370 	if (ctx->wqcb == NULL) {
4371 		ocs_log_err(hw->os, "can't allocate request tag\n");
4372 		return OCS_HW_RTN_NO_RESOURCES;
4373 	}
4374 
4375 	/* Choose a work queue, first look for a class[1] wq, otherwise just use wq[0] */
4376 	wq = ocs_varray_iter_next(hw->wq_class_array[1]);
4377 	if (wq == NULL) {
4378 		wq = hw->hw_wq[0];
4379 	}
4380 
4381 	/* Set XRI and RX_ID in the header based on which WQ, and which send_frame_io we are using */
4382 	xri = wq->send_frame_io->indicator;
4383 
4384 	/* Build the send frame WQE */
4385 	rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, hw->sli.config.wqe_size, sof, eof, (uint32_t*) hdr, payload,
4386 				payload->len, OCS_HW_SEND_FRAME_TIMEOUT, xri, ctx->wqcb->instance_index);
4387 	if (rc) {
4388 		ocs_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
4389 		return OCS_HW_RTN_ERROR;
4390 	}
4391 
4392 	/* Write to WQ */
4393 	rc = hw_wq_write(wq, wqe);
4394 	if (rc) {
4395 		ocs_log_err(hw->os, "hw_wq_write failed: %d\n", rc);
4396 		return OCS_HW_RTN_ERROR;
4397 	}
4398 
4399 	OCS_STAT(wq->use_count++);
4400 
4401 	return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
4402 }
4403 
4404 ocs_hw_rtn_e
ocs_hw_io_register_sgl(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_dma_t * sgl,uint32_t sgl_count)4405 ocs_hw_io_register_sgl(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *sgl, uint32_t sgl_count)
4406 {
4407 	if (sli_get_sgl_preregister(&hw->sli)) {
4408 		ocs_log_err(hw->os, "can't use temporary SGL with pre-registered SGLs\n");
4409 		return OCS_HW_RTN_ERROR;
4410 	}
4411 	io->ovfl_sgl = sgl;
4412 	io->ovfl_sgl_count = sgl_count;
4413 	io->ovfl_io = NULL;
4414 
4415 	return OCS_HW_RTN_SUCCESS;
4416 }
4417 
4418 static void
ocs_hw_io_restore_sgl(ocs_hw_t * hw,ocs_hw_io_t * io)4419 ocs_hw_io_restore_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4420 {
4421 	/* Restore the default */
4422 	io->sgl = &io->def_sgl;
4423 	io->sgl_count = io->def_sgl_count;
4424 
4425 	/*
4426 	 * For skyhawk, we need to free the IO allocated for the chained
4427 	 * SGL. For all devices, clear the overflow fields on the IO.
4428 	 *
4429 	 * Note: For DIF IOs, we may be using the same XRI for the sec_hio and
4430 	 *       the chained SGLs. If so, then we clear the ovfl_io field
4431 	 *       when the sec_hio is freed.
4432 	 */
4433 	if (io->ovfl_io != NULL) {
4434 		ocs_hw_io_free(hw, io->ovfl_io);
4435 		io->ovfl_io = NULL;
4436 	}
4437 
4438 	/* Clear the overflow SGL */
4439 	io->ovfl_sgl = NULL;
4440 	io->ovfl_sgl_count = 0;
4441 	io->ovfl_lsp = NULL;
4442 }
4443 
4444 /**
4445  * @ingroup io
4446  * @brief Initialize the scatter gather list entries of an IO.
4447  *
4448  * @param hw Hardware context.
4449  * @param io Previously-allocated HW IO object.
4450  * @param type Type of IO (target read, target response, and so on).
4451  *
4452  * @return Returns 0 on success, or a non-zero value on failure.
4453  */
4454 ocs_hw_rtn_e
ocs_hw_io_init_sges(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_hw_io_type_e type)4455 ocs_hw_io_init_sges(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_io_type_e type)
4456 {
4457 	sli4_sge_t	*data = NULL;
4458 	uint32_t	i = 0;
4459 	uint32_t	skips = 0;
4460 
4461 	if (!hw || !io) {
4462 		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p\n",
4463 			    hw, io);
4464 		return OCS_HW_RTN_ERROR;
4465 	}
4466 
4467 	/* Clear / reset the scatter-gather list */
4468 	io->sgl = &io->def_sgl;
4469 	io->sgl_count = io->def_sgl_count;
4470 	io->first_data_sge = 0;
4471 
4472 	ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
4473 	io->n_sge = 0;
4474 	io->sge_offset = 0;
4475 
4476 	io->type = type;
4477 
4478 	data = io->sgl->virt;
4479 
4480 	/*
4481 	 * Some IO types have underlying hardware requirements on the order
4482 	 * of SGEs. Process all special entries here.
4483 	 */
4484 	switch (type) {
4485 	case OCS_HW_IO_INITIATOR_READ:
4486 	case OCS_HW_IO_INITIATOR_WRITE:
4487 	case OCS_HW_IO_INITIATOR_NODATA:
4488 		/*
4489 		 * No skips, 2 special for initiator I/Os
4490 		 * The addresses and length are written later
4491 		 */
4492 		/* setup command pointer */
4493 		data->sge_type = SLI4_SGE_TYPE_DATA;
4494 		data++;
4495 
4496 		/* setup response pointer */
4497 		data->sge_type = SLI4_SGE_TYPE_DATA;
4498 
4499 		if (OCS_HW_IO_INITIATOR_NODATA == type) {
4500 			data->last = TRUE;
4501 		}
4502 		data++;
4503 
4504 		io->n_sge = 2;
4505 		break;
4506 	case OCS_HW_IO_TARGET_WRITE:
4507 #define OCS_TARGET_WRITE_SKIPS	2
4508 		skips = OCS_TARGET_WRITE_SKIPS;
4509 
4510 		/* populate host resident XFER_RDY buffer */
4511 		data->sge_type = SLI4_SGE_TYPE_DATA;
4512 		data->buffer_address_high = ocs_addr32_hi(io->xfer_rdy.phys);
4513 		data->buffer_address_low  = ocs_addr32_lo(io->xfer_rdy.phys);
4514 		data->buffer_length = io->xfer_rdy.size;
4515 		data++;
4516 
4517 		skips--;
4518 
4519 		io->n_sge = 1;
4520 		break;
4521 	case OCS_HW_IO_TARGET_READ:
4522 		/*
4523 		 * For FCP_TSEND64, the first 2 entries are SKIP SGE's
4524 		 */
4525 #define OCS_TARGET_READ_SKIPS	2
4526 		skips = OCS_TARGET_READ_SKIPS;
4527 		break;
4528 	case OCS_HW_IO_TARGET_RSP:
4529 		/*
4530 		 * No skips, etc. for FCP_TRSP64
4531 		 */
4532 		break;
4533 	default:
4534 		ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4535 		return OCS_HW_RTN_ERROR;
4536 	}
4537 
4538 	/*
4539 	 * Write skip entries
4540 	 */
4541 	for (i = 0; i < skips; i++) {
4542 		data->sge_type = SLI4_SGE_TYPE_SKIP;
4543 		data++;
4544 	}
4545 
4546 	io->n_sge += skips;
4547 
4548 	/*
4549 	 * Set last
4550 	 */
4551 	data->last = TRUE;
4552 
4553 	return OCS_HW_RTN_SUCCESS;
4554 }
4555 
4556 /**
4557  * @ingroup io
4558  * @brief Add a T10 PI seed scatter gather list entry.
4559  *
4560  * @param hw Hardware context.
4561  * @param io Previously-allocated HW IO object.
4562  * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
4563  *
4564  * @return Returns 0 on success, or a non-zero value on failure.
4565  */
4566 ocs_hw_rtn_e
ocs_hw_io_add_seed_sge(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_hw_dif_info_t * dif_info)4567 ocs_hw_io_add_seed_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_dif_info_t *dif_info)
4568 {
4569 	sli4_sge_t	*data = NULL;
4570 	sli4_diseed_sge_t *dif_seed;
4571 
4572 	/* If no dif_info, or dif_oper is disabled, then just return success */
4573 	if ((dif_info == NULL) || (dif_info->dif_oper == OCS_HW_DIF_OPER_DISABLED)) {
4574 		return OCS_HW_RTN_SUCCESS;
4575 	}
4576 
4577 	if (!hw || !io) {
4578 		ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p dif_info=%p\n",
4579 			    hw, io, dif_info);
4580 		return OCS_HW_RTN_ERROR;
4581 	}
4582 
4583 	data = io->sgl->virt;
4584 	data += io->n_sge;
4585 
4586 	/* If we are doing T10 DIF add the DIF Seed SGE */
4587 	ocs_memset(data, 0, sizeof(sli4_diseed_sge_t));
4588 	dif_seed = (sli4_diseed_sge_t *)data;
4589 	dif_seed->ref_tag_cmp = dif_info->ref_tag_cmp;
4590 	dif_seed->ref_tag_repl = dif_info->ref_tag_repl;
4591 	dif_seed->app_tag_repl = dif_info->app_tag_repl;
4592 	dif_seed->repl_app_tag = dif_info->repl_app_tag;
4593 	if (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) {
4594 		dif_seed->atrt = dif_info->disable_app_ref_ffff;
4595 		dif_seed->at = dif_info->disable_app_ffff;
4596 	}
4597 	dif_seed->sge_type = SLI4_SGE_TYPE_DISEED;
4598 	/* Workaround for SKH (BZ157233) */
4599 	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4600 		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) && dif_info->dif_separate) {
4601 		dif_seed->sge_type = SLI4_SGE_TYPE_SKIP;
4602 	}
4603 
4604 	dif_seed->app_tag_cmp = dif_info->app_tag_cmp;
4605 	dif_seed->dif_blk_size = dif_info->blk_size;
4606 	dif_seed->auto_incr_ref_tag = dif_info->auto_incr_ref_tag;
4607 	dif_seed->check_app_tag = dif_info->check_app_tag;
4608 	dif_seed->check_ref_tag = dif_info->check_ref_tag;
4609 	dif_seed->check_crc = dif_info->check_guard;
4610 	dif_seed->new_ref_tag = dif_info->repl_ref_tag;
4611 
4612 	switch(dif_info->dif_oper) {
4613 	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
4614 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4615 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4616 		break;
4617 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
4618 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4619 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4620 		break;
4621 	case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM:
4622 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4623 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4624 		break;
4625 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF:
4626 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4627 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4628 		break;
4629 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
4630 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4631 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4632 		break;
4633 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM:
4634 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4635 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4636 		break;
4637 	case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
4638 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4639 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4640 		break;
4641 	case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
4642 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4643 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4644 		break;
4645 	case OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW:
4646 		dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4647 		dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4648 		break;
4649 	default:
4650 		ocs_log_err(hw->os, "unsupported DIF operation %#x\n",
4651 			    dif_info->dif_oper);
4652 		return OCS_HW_RTN_ERROR;
4653 	}
4654 
4655 	/*
4656 	 * Set last, clear previous last
4657 	 */
4658 	data->last = TRUE;
4659 	if (io->n_sge) {
4660 		data[-1].last = FALSE;
4661 	}
4662 
4663 	io->n_sge++;
4664 
4665 	return OCS_HW_RTN_SUCCESS;
4666 }
4667 
4668 static ocs_hw_rtn_e
ocs_hw_io_overflow_sgl(ocs_hw_t * hw,ocs_hw_io_t * io)4669 ocs_hw_io_overflow_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4670 {
4671 	sli4_lsp_sge_t *lsp;
4672 
4673 	/* fail if we're already pointing to the overflow SGL */
4674 	if (io->sgl == io->ovfl_sgl) {
4675 		return OCS_HW_RTN_ERROR;
4676 	}
4677 
4678 	/*
4679 	 * For skyhawk, we can use another SGL to extend the SGL list. The
4680 	 * Chained entry must not be in the first 4 entries.
4681 	 *
4682 	 * Note: For DIF enabled IOs, we will use the ovfl_io for the sec_hio.
4683 	 */
4684 	if (sli_get_sgl_preregister(&hw->sli) &&
4685 	    io->def_sgl_count > 4 &&
4686 	    io->ovfl_io == NULL &&
4687 	    ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4688 		(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
4689 		io->ovfl_io = ocs_hw_io_alloc(hw);
4690 		if (io->ovfl_io != NULL) {
4691 			/*
4692 			 * Note: We can't call ocs_hw_io_register_sgl() here
4693 			 * because it checks that SGLs are not pre-registered
4694 			 * and for shyhawk, preregistered SGLs are required.
4695 			 */
4696 			io->ovfl_sgl = &io->ovfl_io->def_sgl;
4697 			io->ovfl_sgl_count = io->ovfl_io->def_sgl_count;
4698 		}
4699 	}
4700 
4701 	/* fail if we don't have an overflow SGL registered */
4702 	if (io->ovfl_sgl == NULL) {
4703 		return OCS_HW_RTN_ERROR;
4704 	}
4705 
4706 	/*
4707 	 * Overflow, we need to put a link SGE in the last location of the current SGL, after
4708 	 * copying the the last SGE to the overflow SGL
4709 	 */
4710 
4711 	((sli4_sge_t*)io->ovfl_sgl->virt)[0] = ((sli4_sge_t*)io->sgl->virt)[io->n_sge - 1];
4712 
4713 	lsp = &((sli4_lsp_sge_t*)io->sgl->virt)[io->n_sge - 1];
4714 	ocs_memset(lsp, 0, sizeof(*lsp));
4715 
4716 	if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4717 	    (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
4718 		sli_skh_chain_sge_build(&hw->sli,
4719 					(sli4_sge_t*)lsp,
4720 					io->ovfl_io->indicator,
4721 					0, /* frag_num */
4722 					0); /* offset */
4723 	} else {
4724 		lsp->buffer_address_high = ocs_addr32_hi(io->ovfl_sgl->phys);
4725 		lsp->buffer_address_low  = ocs_addr32_lo(io->ovfl_sgl->phys);
4726 		lsp->sge_type = SLI4_SGE_TYPE_LSP;
4727 		lsp->last = 0;
4728 		io->ovfl_lsp = lsp;
4729 		io->ovfl_lsp->segment_length = sizeof(sli4_sge_t);
4730 	}
4731 
4732 	/* Update the current SGL pointer, and n_sgl */
4733 	io->sgl = io->ovfl_sgl;
4734 	io->sgl_count = io->ovfl_sgl_count;
4735 	io->n_sge = 1;
4736 
4737 	return OCS_HW_RTN_SUCCESS;
4738 }
4739 
4740 /**
4741  * @ingroup io
4742  * @brief Add a scatter gather list entry to an IO.
4743  *
4744  * @param hw Hardware context.
4745  * @param io Previously-allocated HW IO object.
4746  * @param addr Physical address.
4747  * @param length Length of memory pointed to by @c addr.
4748  *
4749  * @return Returns 0 on success, or a non-zero value on failure.
4750  */
4751 ocs_hw_rtn_e
ocs_hw_io_add_sge(ocs_hw_t * hw,ocs_hw_io_t * io,uintptr_t addr,uint32_t length)4752 ocs_hw_io_add_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr, uint32_t length)
4753 {
4754 	sli4_sge_t	*data = NULL;
4755 
4756 	if (!hw || !io || !addr || !length) {
4757 		ocs_log_err(hw ? hw->os : NULL,
4758 			    "bad parameter hw=%p io=%p addr=%lx length=%u\n",
4759 			    hw, io, addr, length);
4760 		return OCS_HW_RTN_ERROR;
4761 	}
4762 
4763 	if ((length != 0) && (io->n_sge + 1) > io->sgl_count) {
4764 		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_SUCCESS) {
4765 			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4766 			return OCS_HW_RTN_ERROR;
4767 		}
4768 	}
4769 
4770 	if (length > sli_get_max_sge(&hw->sli)) {
4771 		ocs_log_err(hw->os, "length of SGE %d bigger than allowed %d\n",
4772 			    length, sli_get_max_sge(&hw->sli));
4773 		return OCS_HW_RTN_ERROR;
4774 	}
4775 
4776 	data = io->sgl->virt;
4777 	data += io->n_sge;
4778 
4779 	data->sge_type = SLI4_SGE_TYPE_DATA;
4780 	data->buffer_address_high = ocs_addr32_hi(addr);
4781 	data->buffer_address_low  = ocs_addr32_lo(addr);
4782 	data->buffer_length = length;
4783 	data->data_offset = io->sge_offset;
4784 	/*
4785 	 * Always assume this is the last entry and mark as such.
4786 	 * If this is not the first entry unset the "last SGE"
4787 	 * indication for the previous entry
4788 	 */
4789 	data->last = TRUE;
4790 	if (io->n_sge) {
4791 		data[-1].last = FALSE;
4792 	}
4793 
4794 	/* Set first_data_bde if not previously set */
4795 	if (io->first_data_sge == 0) {
4796 		io->first_data_sge = io->n_sge;
4797 	}
4798 
4799 	io->sge_offset += length;
4800 	io->n_sge++;
4801 
4802 	/* Update the linked segment length (only executed after overflow has begun) */
4803 	if (io->ovfl_lsp != NULL) {
4804 		io->ovfl_lsp->segment_length = io->n_sge * sizeof(sli4_sge_t);
4805 	}
4806 
4807 	return OCS_HW_RTN_SUCCESS;
4808 }
4809 
4810 /**
4811  * @ingroup io
4812  * @brief Add a T10 DIF scatter gather list entry to an IO.
4813  *
4814  * @param hw Hardware context.
4815  * @param io Previously-allocated HW IO object.
4816  * @param addr DIF physical address.
4817  *
4818  * @return Returns 0 on success, or a non-zero value on failure.
4819  */
4820 ocs_hw_rtn_e
ocs_hw_io_add_dif_sge(ocs_hw_t * hw,ocs_hw_io_t * io,uintptr_t addr)4821 ocs_hw_io_add_dif_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr)
4822 {
4823 	sli4_dif_sge_t	*data = NULL;
4824 
4825 	if (!hw || !io || !addr) {
4826 		ocs_log_err(hw ? hw->os : NULL,
4827 			    "bad parameter hw=%p io=%p addr=%lx\n",
4828 			    hw, io, addr);
4829 		return OCS_HW_RTN_ERROR;
4830 	}
4831 
4832 	if ((io->n_sge + 1) > hw->config.n_sgl) {
4833 		if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_ERROR) {
4834 			ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4835 			return OCS_HW_RTN_ERROR;
4836 		}
4837 	}
4838 
4839 	data = io->sgl->virt;
4840 	data += io->n_sge;
4841 
4842 	data->sge_type = SLI4_SGE_TYPE_DIF;
4843 	/* Workaround for SKH (BZ157233) */
4844 	if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4845 		(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type)) {
4846 		data->sge_type = SLI4_SGE_TYPE_SKIP;
4847 	}
4848 
4849 	data->buffer_address_high = ocs_addr32_hi(addr);
4850 	data->buffer_address_low  = ocs_addr32_lo(addr);
4851 
4852 	/*
4853 	 * Always assume this is the last entry and mark as such.
4854 	 * If this is not the first entry unset the "last SGE"
4855 	 * indication for the previous entry
4856 	 */
4857 	data->last = TRUE;
4858 	if (io->n_sge) {
4859 		data[-1].last = FALSE;
4860 	}
4861 
4862 	io->n_sge++;
4863 
4864 	return OCS_HW_RTN_SUCCESS;
4865 }
4866 
4867 /**
4868  * @ingroup io
4869  * @brief Abort a previously-started IO.
4870  *
4871  * @param hw Hardware context.
4872  * @param io_to_abort The IO to abort.
4873  * @param send_abts Boolean to have the hardware automatically
4874  * generate an ABTS.
4875  * @param cb Function call upon completion of the abort (may be NULL).
4876  * @param arg Argument to pass to abort completion function.
4877  *
4878  * @return Returns 0 on success, or a non-zero value on failure.
4879  */
4880 ocs_hw_rtn_e
ocs_hw_io_abort(ocs_hw_t * hw,ocs_hw_io_t * io_to_abort,uint32_t send_abts,void * cb,void * arg)4881 ocs_hw_io_abort(ocs_hw_t *hw, ocs_hw_io_t *io_to_abort, uint32_t send_abts, void *cb, void *arg)
4882 {
4883 	sli4_abort_type_e atype = SLI_ABORT_MAX;
4884 	uint32_t	id = 0, mask = 0;
4885 	ocs_hw_rtn_e	rc = OCS_HW_RTN_SUCCESS;
4886 	hw_wq_callback_t *wqcb;
4887 
4888 	if (!hw || !io_to_abort) {
4889 		ocs_log_err(hw ? hw->os : NULL,
4890 			    "bad parameter hw=%p io=%p\n",
4891 			    hw, io_to_abort);
4892 		return OCS_HW_RTN_ERROR;
4893 	}
4894 
4895 	if (hw->state != OCS_HW_STATE_ACTIVE) {
4896 		ocs_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
4897 			    hw->state);
4898 		return OCS_HW_RTN_ERROR;
4899 	}
4900 
4901 	/* take a reference on IO being aborted */
4902 	if (ocs_ref_get_unless_zero(&io_to_abort->ref) == 0) {
4903 		/* command no longer active */
4904 		ocs_log_test(hw ? hw->os : NULL,
4905 				"io not active xri=0x%x tag=0x%x\n",
4906 				io_to_abort->indicator, io_to_abort->reqtag);
4907 		return OCS_HW_RTN_IO_NOT_ACTIVE;
4908 	}
4909 
4910 	/* non-port owned XRI checks */
4911 	/* Must have a valid WQ reference */
4912 	if (io_to_abort->wq == NULL) {
4913 		ocs_log_test(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
4914 				io_to_abort->indicator);
4915 		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4916 		return OCS_HW_RTN_IO_NOT_ACTIVE;
4917 	}
4918 
4919 	/* Validation checks complete; now check to see if already being aborted */
4920 	ocs_lock(&hw->io_abort_lock);
4921 		if (io_to_abort->abort_in_progress) {
4922 			ocs_unlock(&hw->io_abort_lock);
4923 			ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4924 			ocs_log_debug(hw ? hw->os : NULL,
4925 				"io already being aborted xri=0x%x tag=0x%x\n",
4926 				io_to_abort->indicator, io_to_abort->reqtag);
4927 			return OCS_HW_RTN_IO_ABORT_IN_PROGRESS;
4928 		}
4929 
4930 		/*
4931 		 * This IO is not already being aborted. Set flag so we won't try to
4932 		 * abort it again. After all, we only have one abort_done callback.
4933 		 */
4934 		io_to_abort->abort_in_progress = 1;
4935 	ocs_unlock(&hw->io_abort_lock);
4936 
4937 	/*
4938 	 * If we got here, the possibilities are:
4939 	 * - host owned xri
4940 	 *	- io_to_abort->wq_index != UINT32_MAX
4941 	 *		- submit ABORT_WQE to same WQ
4942 	 * - port owned xri:
4943 	 *	- rxri: io_to_abort->wq_index == UINT32_MAX
4944 	 *		- submit ABORT_WQE to any WQ
4945 	 *	- non-rxri
4946 	 *		- io_to_abort->index != UINT32_MAX
4947 	 *			- submit ABORT_WQE to same WQ
4948 	 *		- io_to_abort->index == UINT32_MAX
4949 	 *			- submit ABORT_WQE to any WQ
4950 	 */
4951 	io_to_abort->abort_done = cb;
4952 	io_to_abort->abort_arg  = arg;
4953 
4954 	atype = SLI_ABORT_XRI;
4955 	id = io_to_abort->indicator;
4956 
4957 	/* Allocate a request tag for the abort portion of this IO */
4958 	wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_abort, io_to_abort);
4959 	if (wqcb == NULL) {
4960 		ocs_log_err(hw->os, "can't allocate request tag\n");
4961 		return OCS_HW_RTN_NO_RESOURCES;
4962 	}
4963 	io_to_abort->abort_reqtag = wqcb->instance_index;
4964 
4965 	/*
4966 	 * If the wqe is on the pending list, then set this wqe to be
4967 	 * aborted when the IO's wqe is removed from the list.
4968 	 */
4969 	if (io_to_abort->wq != NULL) {
4970 		sli_queue_lock(io_to_abort->wq->queue);
4971 			if (ocs_list_on_list(&io_to_abort->wqe.link)) {
4972 				io_to_abort->wqe.abort_wqe_submit_needed = 1;
4973 				io_to_abort->wqe.send_abts = send_abts;
4974 				io_to_abort->wqe.id = id;
4975 				io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
4976 				sli_queue_unlock(io_to_abort->wq->queue);
4977 				return 0;
4978 		}
4979 		sli_queue_unlock(io_to_abort->wq->queue);
4980 	}
4981 
4982 	if (sli_abort_wqe(&hw->sli, io_to_abort->wqe.wqebuf, hw->sli.config.wqe_size, atype, send_abts, id, mask,
4983 			  io_to_abort->abort_reqtag, SLI4_CQ_DEFAULT)) {
4984 		ocs_log_err(hw->os, "ABORT WQE error\n");
4985 		io_to_abort->abort_reqtag = UINT32_MAX;
4986 		ocs_hw_reqtag_free(hw, wqcb);
4987 		rc = OCS_HW_RTN_ERROR;
4988 	}
4989 
4990 	if (OCS_HW_RTN_SUCCESS == rc) {
4991 		if (io_to_abort->wq == NULL) {
4992 			io_to_abort->wq = ocs_hw_queue_next_wq(hw, io_to_abort);
4993 			ocs_hw_assert(io_to_abort->wq != NULL);
4994 		}
4995 		/* ABORT_WQE does not actually utilize an XRI on the Port,
4996 		 * therefore, keep xbusy as-is to track the exchange's state,
4997 		 * not the ABORT_WQE's state
4998 		 */
4999 		rc = hw_wq_write(io_to_abort->wq, &io_to_abort->wqe);
5000 		if (rc > 0) {
5001 			/* non-negative return is success */
5002 			rc = 0;
5003 			/* can't abort an abort so skip adding to timed wqe list */
5004 		}
5005 	}
5006 
5007 	if (OCS_HW_RTN_SUCCESS != rc) {
5008 		ocs_lock(&hw->io_abort_lock);
5009 			io_to_abort->abort_in_progress = 0;
5010 		ocs_unlock(&hw->io_abort_lock);
5011 		ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
5012 	}
5013 	return rc;
5014 }
5015 
5016 /**
5017  * @ingroup io
5018  * @brief Return the OX_ID/RX_ID of the IO.
5019  *
5020  * @param hw Hardware context.
5021  * @param io HW IO object.
5022  *
5023  * @return Returns X_ID on success, or -1 on failure.
5024  */
5025 int32_t
ocs_hw_io_get_xid(ocs_hw_t * hw,ocs_hw_io_t * io)5026 ocs_hw_io_get_xid(ocs_hw_t *hw, ocs_hw_io_t *io)
5027 {
5028 	if (!hw || !io) {
5029 		ocs_log_err(hw ? hw->os : NULL,
5030 			    "bad parameter hw=%p io=%p\n", hw, io);
5031 		return -1;
5032 	}
5033 
5034 	return io->indicator;
5035 }
5036 
5037 
5038 typedef struct ocs_hw_fw_write_cb_arg {
5039 	ocs_hw_fw_cb_t cb;
5040 	void *arg;
5041 } ocs_hw_fw_write_cb_arg_t;
5042 
5043 typedef struct ocs_hw_sfp_cb_arg {
5044 	ocs_hw_sfp_cb_t cb;
5045 	void *arg;
5046 	ocs_dma_t payload;
5047 } ocs_hw_sfp_cb_arg_t;
5048 
5049 typedef struct ocs_hw_temp_cb_arg {
5050 	ocs_hw_temp_cb_t cb;
5051 	void *arg;
5052 } ocs_hw_temp_cb_arg_t;
5053 
5054 typedef struct ocs_hw_link_stat_cb_arg {
5055 	ocs_hw_link_stat_cb_t cb;
5056 	void *arg;
5057 } ocs_hw_link_stat_cb_arg_t;
5058 
5059 typedef struct ocs_hw_host_stat_cb_arg {
5060 	ocs_hw_host_stat_cb_t cb;
5061 	void *arg;
5062 } ocs_hw_host_stat_cb_arg_t;
5063 
5064 typedef struct ocs_hw_dump_get_cb_arg {
5065 	ocs_hw_dump_get_cb_t cb;
5066 	void *arg;
5067 	void *mbox_cmd;
5068 } ocs_hw_dump_get_cb_arg_t;
5069 
5070 typedef struct ocs_hw_dump_clear_cb_arg {
5071 	ocs_hw_dump_clear_cb_t cb;
5072 	void *arg;
5073 	void *mbox_cmd;
5074 } ocs_hw_dump_clear_cb_arg_t;
5075 
5076 /**
5077  * @brief Write a portion of a firmware image to the device.
5078  *
5079  * @par Description
5080  * Calls the correct firmware write function based on the device type.
5081  *
5082  * @param hw Hardware context.
5083  * @param dma DMA structure containing the firmware image chunk.
5084  * @param size Size of the firmware image chunk.
5085  * @param offset Offset, in bytes, from the beginning of the firmware image.
5086  * @param last True if this is the last chunk of the image.
5087  * Causes the image to be committed to flash.
5088  * @param cb Pointer to a callback function that is called when the command completes.
5089  * The callback function prototype is
5090  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5091  * @param arg Pointer to be passed to the callback function.
5092  *
5093  * @return Returns 0 on success, or a non-zero value on failure.
5094  */
5095 ocs_hw_rtn_e
ocs_hw_firmware_write(ocs_hw_t * hw,ocs_dma_t * dma,uint32_t size,uint32_t offset,int last,ocs_hw_fw_cb_t cb,void * arg)5096 ocs_hw_firmware_write(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
5097 {
5098 	if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
5099 		return ocs_hw_firmware_write_lancer(hw, dma, size, offset, last, cb, arg);
5100 	} else {
5101 		/* Write firmware_write for BE3/Skyhawk not supported */
5102 		return -1;
5103 	}
5104 }
5105 
5106 /**
5107  * @brief Write a portion of a firmware image to the Emulex XE201 ASIC (Lancer).
5108  *
5109  * @par Description
5110  * Creates a SLI_CONFIG mailbox command, fills it with the correct values to write a
5111  * firmware image chunk, and then sends the command with ocs_hw_command(). On completion,
5112  * the callback function ocs_hw_fw_write_cb() gets called to free the mailbox
5113  * and to signal the caller that the write has completed.
5114  *
5115  * @param hw Hardware context.
5116  * @param dma DMA structure containing the firmware image chunk.
5117  * @param size Size of the firmware image chunk.
5118  * @param offset Offset, in bytes, from the beginning of the firmware image.
5119  * @param last True if this is the last chunk of the image. Causes the image to be committed to flash.
5120  * @param cb Pointer to a callback function that is called when the command completes.
5121  * The callback function prototype is
5122  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5123  * @param arg Pointer to be passed to the callback function.
5124  *
5125  * @return Returns 0 on success, or a non-zero value on failure.
5126  */
5127 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)5128 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)
5129 {
5130 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5131 	uint8_t *mbxdata;
5132 	ocs_hw_fw_write_cb_arg_t *cb_arg;
5133 	int noc=0;	/* No Commit bit - set to 1 for testing */
5134 
5135 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
5136 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
5137 		return OCS_HW_RTN_ERROR;
5138 	}
5139 
5140 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5141 	if (mbxdata == NULL) {
5142 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5143 		return OCS_HW_RTN_NO_MEMORY;
5144 	}
5145 
5146 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_fw_write_cb_arg_t), OCS_M_NOWAIT);
5147 	if (cb_arg == NULL) {
5148 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5149 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5150 		return OCS_HW_RTN_NO_MEMORY;
5151 	}
5152 
5153 	cb_arg->cb = cb;
5154 	cb_arg->arg = arg;
5155 
5156 	if (sli_cmd_common_write_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE, noc, last,
5157 			size, offset, "/prg/", dma)) {
5158 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_fw_write, cb_arg);
5159 	}
5160 
5161 	if (rc != OCS_HW_RTN_SUCCESS) {
5162 		ocs_log_test(hw->os, "COMMON_WRITE_OBJECT failed\n");
5163 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5164 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5165 	}
5166 
5167 	return rc;
5168 
5169 }
5170 
5171 /**
5172  * @brief Called when the WRITE OBJECT command completes.
5173  *
5174  * @par Description
5175  * Get the number of bytes actually written out of the response, free the mailbox
5176  * that was malloc'd by ocs_hw_firmware_write(),
5177  * then call the callback and pass the status and bytes written.
5178  *
5179  * @param hw Hardware context.
5180  * @param status Status field from the mbox completion.
5181  * @param mqe Mailbox response structure.
5182  * @param arg Pointer to a callback function that signals the caller that the command is done.
5183  * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_written)</tt>.
5184  *
5185  * @return Returns 0.
5186  */
5187 static int32_t
ocs_hw_cb_fw_write(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5188 ocs_hw_cb_fw_write(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5189 {
5190 
5191 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
5192 	sli4_res_common_write_object_t* wr_obj_rsp = (sli4_res_common_write_object_t*) &(mbox_rsp->payload.embed);
5193 	ocs_hw_fw_write_cb_arg_t *cb_arg = arg;
5194 	uint32_t bytes_written;
5195 	uint16_t mbox_status;
5196 	uint32_t change_status;
5197 
5198 	bytes_written = wr_obj_rsp->actual_write_length;
5199 	mbox_status = mbox_rsp->hdr.status;
5200 	change_status = wr_obj_rsp->change_status;
5201 
5202 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5203 
5204 	if (cb_arg) {
5205 		if (cb_arg->cb) {
5206 			if ((status == 0) && mbox_status) {
5207 				status = mbox_status;
5208 			}
5209 			cb_arg->cb(status, bytes_written, change_status, cb_arg->arg);
5210 		}
5211 
5212 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5213 	}
5214 
5215 	return 0;
5216 
5217 }
5218 
5219 /**
5220  * @brief Called when the READ_TRANSCEIVER_DATA command completes.
5221  *
5222  * @par Description
5223  * Get the number of bytes read out of the response, free the mailbox that was malloc'd
5224  * by ocs_hw_get_sfp(), then call the callback and pass the status and bytes written.
5225  *
5226  * @param hw Hardware context.
5227  * @param status Status field from the mbox completion.
5228  * @param mqe Mailbox response structure.
5229  * @param arg Pointer to a callback function that signals the caller that the command is done.
5230  * The callback function prototype is
5231  * <tt>void cb(int32_t status, uint32_t bytes_written, uint32_t *data, void *arg)</tt>.
5232  *
5233  * @return Returns 0.
5234  */
5235 static int32_t
ocs_hw_cb_sfp(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5236 ocs_hw_cb_sfp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5237 {
5238 
5239 	ocs_hw_sfp_cb_arg_t *cb_arg = arg;
5240 	ocs_dma_t *payload = NULL;
5241 	sli4_res_common_read_transceiver_data_t* mbox_rsp = NULL;
5242 	uint32_t bytes_written;
5243 
5244 	if (cb_arg) {
5245 		payload = &(cb_arg->payload);
5246 		if (cb_arg->cb) {
5247 			mbox_rsp = (sli4_res_common_read_transceiver_data_t*) payload->virt;
5248 			bytes_written = mbox_rsp->hdr.response_length;
5249 			if ((status == 0) && mbox_rsp->hdr.status) {
5250 				status = mbox_rsp->hdr.status;
5251 			}
5252 			cb_arg->cb(hw->os, status, bytes_written, mbox_rsp->page_data, cb_arg->arg);
5253 		}
5254 
5255 		ocs_dma_free(hw->os, &cb_arg->payload);
5256 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5257 	}
5258 
5259 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5260 	return 0;
5261 }
5262 
5263 /**
5264  * @ingroup io
5265  * @brief Function to retrieve the SFP information.
5266  *
5267  * @param hw Hardware context.
5268  * @param page The page of SFP data to retrieve (0xa0 or 0xa2).
5269  * @param cb Function call upon completion of sending the data (may be NULL).
5270  * @param arg Argument to pass to IO completion function.
5271  *
5272  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5273  */
5274 ocs_hw_rtn_e
ocs_hw_get_sfp(ocs_hw_t * hw,uint16_t page,ocs_hw_sfp_cb_t cb,void * arg)5275 ocs_hw_get_sfp(ocs_hw_t *hw, uint16_t page, ocs_hw_sfp_cb_t cb, void *arg)
5276 {
5277 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5278 	ocs_hw_sfp_cb_arg_t *cb_arg;
5279 	uint8_t *mbxdata;
5280 
5281 	/* mbxdata holds the header of the command */
5282 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5283 	if (mbxdata == NULL) {
5284 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5285 		return OCS_HW_RTN_NO_MEMORY;
5286 	}
5287 
5288 	/* cb_arg holds the data that will be passed to the callback on completion */
5289 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_sfp_cb_arg_t), OCS_M_NOWAIT);
5290 	if (cb_arg == NULL) {
5291 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5292 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5293 		return OCS_HW_RTN_NO_MEMORY;
5294 	}
5295 
5296 	cb_arg->cb = cb;
5297 	cb_arg->arg = arg;
5298 
5299 	/* payload holds the non-embedded portion */
5300 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_read_transceiver_data_t),
5301 			  OCS_MIN_DMA_ALIGNMENT)) {
5302 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
5303 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5304 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5305 		return OCS_HW_RTN_NO_MEMORY;
5306 	}
5307 
5308 	/* Send the HW command */
5309 	if (sli_cmd_common_read_transceiver_data(&hw->sli, mbxdata, SLI4_BMBX_SIZE, page,
5310 	    &cb_arg->payload)) {
5311 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_sfp, cb_arg);
5312 	}
5313 
5314 	if (rc != OCS_HW_RTN_SUCCESS) {
5315 		ocs_log_test(hw->os, "READ_TRANSCEIVER_DATA failed with status %d\n",
5316 				rc);
5317 		ocs_dma_free(hw->os, &cb_arg->payload);
5318 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5319 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5320 	}
5321 
5322 	return rc;
5323 }
5324 
5325 /**
5326  * @brief Function to retrieve the temperature information.
5327  *
5328  * @param hw Hardware context.
5329  * @param cb Function call upon completion of sending the data (may be NULL).
5330  * @param arg Argument to pass to IO completion function.
5331  *
5332  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5333  */
5334 ocs_hw_rtn_e
ocs_hw_get_temperature(ocs_hw_t * hw,ocs_hw_temp_cb_t cb,void * arg)5335 ocs_hw_get_temperature(ocs_hw_t *hw, ocs_hw_temp_cb_t cb, void *arg)
5336 {
5337 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5338 	ocs_hw_temp_cb_arg_t *cb_arg;
5339 	uint8_t *mbxdata;
5340 
5341 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5342 	if (mbxdata == NULL) {
5343 		ocs_log_err(hw->os, "failed to malloc mbox");
5344 		return OCS_HW_RTN_NO_MEMORY;
5345 	}
5346 
5347 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_temp_cb_arg_t), OCS_M_NOWAIT);
5348 	if (cb_arg == NULL) {
5349 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5350 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5351 		return OCS_HW_RTN_NO_MEMORY;
5352 	}
5353 
5354 	cb_arg->cb = cb;
5355 	cb_arg->arg = arg;
5356 
5357 	if (sli_cmd_dump_type4(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5358 				SLI4_WKI_TAG_SAT_TEM)) {
5359 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_temp, cb_arg);
5360 	}
5361 
5362 	if (rc != OCS_HW_RTN_SUCCESS) {
5363 		ocs_log_test(hw->os, "DUMP_TYPE4 failed\n");
5364 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5365 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5366 	}
5367 
5368 	return rc;
5369 }
5370 
5371 /**
5372  * @brief Called when the DUMP command completes.
5373  *
5374  * @par Description
5375  * Get the temperature data out of the response, free the mailbox that was malloc'd
5376  * by ocs_hw_get_temperature(), then call the callback and pass the status and data.
5377  *
5378  * @param hw Hardware context.
5379  * @param status Status field from the mbox completion.
5380  * @param mqe Mailbox response structure.
5381  * @param arg Pointer to a callback function that signals the caller that the command is done.
5382  * The callback function prototype is defined by ocs_hw_temp_cb_t.
5383  *
5384  * @return Returns 0.
5385  */
5386 static int32_t
ocs_hw_cb_temp(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5387 ocs_hw_cb_temp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5388 {
5389 
5390 	sli4_cmd_dump4_t* mbox_rsp = (sli4_cmd_dump4_t*) mqe;
5391 	ocs_hw_temp_cb_arg_t *cb_arg = arg;
5392 	uint32_t curr_temp = mbox_rsp->resp_data[0]; /* word 5 */
5393 	uint32_t crit_temp_thrshld = mbox_rsp->resp_data[1]; /* word 6*/
5394 	uint32_t warn_temp_thrshld = mbox_rsp->resp_data[2]; /* word 7 */
5395 	uint32_t norm_temp_thrshld = mbox_rsp->resp_data[3]; /* word 8 */
5396 	uint32_t fan_off_thrshld = mbox_rsp->resp_data[4];   /* word 9 */
5397 	uint32_t fan_on_thrshld = mbox_rsp->resp_data[5];    /* word 10 */
5398 
5399 	if (cb_arg) {
5400 		if (cb_arg->cb) {
5401 			if ((status == 0) && mbox_rsp->hdr.status) {
5402 				status = mbox_rsp->hdr.status;
5403 			}
5404 			cb_arg->cb(status,
5405 				   curr_temp,
5406 				   crit_temp_thrshld,
5407 				   warn_temp_thrshld,
5408 				   norm_temp_thrshld,
5409 				   fan_off_thrshld,
5410 				   fan_on_thrshld,
5411 				   cb_arg->arg);
5412 		}
5413 
5414 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5415 	}
5416 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5417 
5418 	return 0;
5419 }
5420 
5421 /**
5422  * @brief Function to retrieve the link statistics.
5423  *
5424  * @param hw Hardware context.
5425  * @param req_ext_counters If TRUE, then the extended counters will be requested.
5426  * @param clear_overflow_flags If TRUE, then overflow flags will be cleared.
5427  * @param clear_all_counters If TRUE, the counters will be cleared.
5428  * @param cb Function call upon completion of sending the data (may be NULL).
5429  * @param arg Argument to pass to IO completion function.
5430  *
5431  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5432  */
5433 ocs_hw_rtn_e
ocs_hw_get_link_stats(ocs_hw_t * hw,uint8_t req_ext_counters,uint8_t clear_overflow_flags,uint8_t clear_all_counters,ocs_hw_link_stat_cb_t cb,void * arg)5434 ocs_hw_get_link_stats(ocs_hw_t *hw,
5435 			uint8_t req_ext_counters,
5436 			uint8_t clear_overflow_flags,
5437 			uint8_t clear_all_counters,
5438 			ocs_hw_link_stat_cb_t cb,
5439 			void *arg)
5440 {
5441 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5442 	ocs_hw_link_stat_cb_arg_t *cb_arg;
5443 	uint8_t *mbxdata;
5444 
5445 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5446 	if (mbxdata == NULL) {
5447 		ocs_log_err(hw->os, "failed to malloc mbox");
5448 		return OCS_HW_RTN_NO_MEMORY;
5449 	}
5450 
5451 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_link_stat_cb_arg_t), OCS_M_NOWAIT);
5452 	if (cb_arg == NULL) {
5453 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5454 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5455 		return OCS_HW_RTN_NO_MEMORY;
5456 	}
5457 
5458 	cb_arg->cb = cb;
5459 	cb_arg->arg = arg;
5460 
5461 	if (sli_cmd_read_link_stats(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5462 				    req_ext_counters,
5463 				    clear_overflow_flags,
5464 				    clear_all_counters)) {
5465 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_link_stat, cb_arg);
5466 	}
5467 
5468 	if (rc != OCS_HW_RTN_SUCCESS) {
5469 		ocs_log_test(hw->os, "READ_LINK_STATS failed\n");
5470 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5471 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5472 	}
5473 
5474 	return rc;
5475 }
5476 
5477 /**
5478  * @brief Called when the READ_LINK_STAT command completes.
5479  *
5480  * @par Description
5481  * Get the counters out of the response, free the mailbox that was malloc'd
5482  * by ocs_hw_get_link_stats(), then call the callback and pass the status and data.
5483  *
5484  * @param hw Hardware context.
5485  * @param status Status field from the mbox completion.
5486  * @param mqe Mailbox response structure.
5487  * @param arg Pointer to a callback function that signals the caller that the command is done.
5488  * The callback function prototype is defined by ocs_hw_link_stat_cb_t.
5489  *
5490  * @return Returns 0.
5491  */
5492 static int32_t
ocs_hw_cb_link_stat(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5493 ocs_hw_cb_link_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5494 {
5495 
5496 	sli4_cmd_read_link_stats_t* mbox_rsp = (sli4_cmd_read_link_stats_t*) mqe;
5497 	ocs_hw_link_stat_cb_arg_t *cb_arg = arg;
5498 	ocs_hw_link_stat_counts_t counts[OCS_HW_LINK_STAT_MAX];
5499 	uint32_t num_counters = (mbox_rsp->gec ? 20 : 13);
5500 
5501 	ocs_memset(counts, 0, sizeof(ocs_hw_link_stat_counts_t) *
5502 		   OCS_HW_LINK_STAT_MAX);
5503 
5504 	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].overflow = mbox_rsp->w02of;
5505 	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].overflow = mbox_rsp->w03of;
5506 	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].overflow = mbox_rsp->w04of;
5507 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].overflow = mbox_rsp->w05of;
5508 	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].overflow = mbox_rsp->w06of;
5509 	counts[OCS_HW_LINK_STAT_CRC_COUNT].overflow = mbox_rsp->w07of;
5510 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].overflow = mbox_rsp->w08of;
5511 	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].overflow = mbox_rsp->w09of;
5512 	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].overflow = mbox_rsp->w10of;
5513 	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].overflow = mbox_rsp->w11of;
5514 	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].overflow = mbox_rsp->w12of;
5515 	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].overflow = mbox_rsp->w13of;
5516 	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].overflow = mbox_rsp->w14of;
5517 	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].overflow = mbox_rsp->w15of;
5518 	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].overflow = mbox_rsp->w16of;
5519 	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].overflow = mbox_rsp->w17of;
5520 	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].overflow = mbox_rsp->w18of;
5521 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].overflow = mbox_rsp->w19of;
5522 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].overflow = mbox_rsp->w20of;
5523 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].overflow = mbox_rsp->w21of;
5524 
5525 	counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = mbox_rsp->link_failure_error_count;
5526 	counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = mbox_rsp->loss_of_sync_error_count;
5527 	counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = mbox_rsp->loss_of_signal_error_count;
5528 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = mbox_rsp->primitive_sequence_error_count;
5529 	counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = mbox_rsp->invalid_transmission_word_error_count;
5530 	counts[OCS_HW_LINK_STAT_CRC_COUNT].counter = mbox_rsp->crc_error_count;
5531 	counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = mbox_rsp->primitive_sequence_event_timeout_count;
5532 	counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = mbox_rsp->elastic_buffer_overrun_error_count;
5533 	counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = mbox_rsp->arbitration_fc_al_timout_count;
5534 	counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = mbox_rsp->advertised_receive_bufftor_to_buffer_credit;
5535 	counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = mbox_rsp->current_receive_buffer_to_buffer_credit;
5536 	counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = mbox_rsp->advertised_transmit_buffer_to_buffer_credit;
5537 	counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = mbox_rsp->current_transmit_buffer_to_buffer_credit;
5538 	counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].counter = mbox_rsp->received_eofa_count;
5539 	counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = mbox_rsp->received_eofdti_count;
5540 	counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = mbox_rsp->received_eofni_count;
5541 	counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].counter = mbox_rsp->received_soff_count;
5542 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = mbox_rsp->received_dropped_no_aer_count;
5543 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = mbox_rsp->received_dropped_no_available_rpi_resources_count;
5544 	counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = mbox_rsp->received_dropped_no_available_xri_resources_count;
5545 
5546 	if (cb_arg) {
5547 		if (cb_arg->cb) {
5548 			if ((status == 0) && mbox_rsp->hdr.status) {
5549 				status = mbox_rsp->hdr.status;
5550 			}
5551 			cb_arg->cb(status,
5552 				   num_counters,
5553 				   counts,
5554 				   cb_arg->arg);
5555 		}
5556 
5557 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5558 	}
5559 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5560 
5561 	return 0;
5562 }
5563 
5564 /**
5565  * @brief Function to retrieve the link and host statistics.
5566  *
5567  * @param hw Hardware context.
5568  * @param cc clear counters, if TRUE all counters will be cleared.
5569  * @param cb Function call upon completion of receiving the data.
5570  * @param arg Argument to pass to pointer fc hosts statistics structure.
5571  *
5572  * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5573  */
5574 ocs_hw_rtn_e
ocs_hw_get_host_stats(ocs_hw_t * hw,uint8_t cc,ocs_hw_host_stat_cb_t cb,void * arg)5575 ocs_hw_get_host_stats(ocs_hw_t *hw, uint8_t cc, ocs_hw_host_stat_cb_t cb, void *arg)
5576 {
5577 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5578 	ocs_hw_host_stat_cb_arg_t *cb_arg;
5579 	uint8_t *mbxdata;
5580 
5581 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO);
5582 	if (mbxdata == NULL) {
5583 		ocs_log_err(hw->os, "failed to malloc mbox");
5584 		return OCS_HW_RTN_NO_MEMORY;
5585 	}
5586 
5587 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_host_stat_cb_arg_t), 0);
5588 	if (cb_arg == NULL) {
5589 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5590 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5591 		return OCS_HW_RTN_NO_MEMORY;
5592 	 }
5593 
5594 	 cb_arg->cb = cb;
5595 	 cb_arg->arg = arg;
5596 
5597 	 /* Send the HW command to get the host stats */
5598 	if (sli_cmd_read_status(&hw->sli, mbxdata, SLI4_BMBX_SIZE, cc)) {
5599 		 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_host_stat, cb_arg);
5600 	}
5601 
5602 	if (rc != OCS_HW_RTN_SUCCESS) {
5603 		ocs_log_test(hw->os, "READ_HOST_STATS failed\n");
5604 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5605 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5606 	}
5607 
5608 	return rc;
5609 }
5610 
5611 
5612 /**
5613  * @brief Called when the READ_STATUS command completes.
5614  *
5615  * @par Description
5616  * Get the counters out of the response, free the mailbox that was malloc'd
5617  * by ocs_hw_get_host_stats(), then call the callback and pass
5618  * the status and data.
5619  *
5620  * @param hw Hardware context.
5621  * @param status Status field from the mbox completion.
5622  * @param mqe Mailbox response structure.
5623  * @param arg Pointer to a callback function that signals the caller that the command is done.
5624  * The callback function prototype is defined by
5625  * ocs_hw_host_stat_cb_t.
5626  *
5627  * @return Returns 0.
5628  */
5629 static int32_t
ocs_hw_cb_host_stat(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5630 ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5631 {
5632 
5633 	sli4_cmd_read_status_t* mbox_rsp = (sli4_cmd_read_status_t*) mqe;
5634 	ocs_hw_host_stat_cb_arg_t *cb_arg = arg;
5635 	ocs_hw_host_stat_counts_t counts[OCS_HW_HOST_STAT_MAX];
5636 	uint32_t num_counters = OCS_HW_HOST_STAT_MAX;
5637 
5638 	ocs_memset(counts, 0, sizeof(ocs_hw_host_stat_counts_t) *
5639 		   OCS_HW_HOST_STAT_MAX);
5640 
5641 	counts[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter = mbox_rsp->transmit_kbyte_count;
5642 	counts[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter = mbox_rsp->receive_kbyte_count;
5643 	counts[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter = mbox_rsp->transmit_frame_count;
5644 	counts[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter = mbox_rsp->receive_frame_count;
5645 	counts[OCS_HW_HOST_STAT_TX_SEQ_COUNT].counter = mbox_rsp->transmit_sequence_count;
5646 	counts[OCS_HW_HOST_STAT_RX_SEQ_COUNT].counter = mbox_rsp->receive_sequence_count;
5647 	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = mbox_rsp->total_exchanges_originator;
5648 	counts[OCS_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = mbox_rsp->total_exchanges_responder;
5649 	counts[OCS_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = mbox_rsp->receive_p_bsy_count;
5650 	counts[OCS_HW_HOST_STAT_RX_F_BSY_COUNT].counter = mbox_rsp->receive_f_bsy_count;
5651 	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_rq_buffer_count;
5652 	counts[OCS_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = mbox_rsp->empty_rq_timeout_count;
5653 	counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_xri_count;
5654 	counts[OCS_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = mbox_rsp->empty_xri_pool_count;
5655 
5656 
5657 	if (cb_arg) {
5658 		if (cb_arg->cb) {
5659 			if ((status == 0) && mbox_rsp->hdr.status) {
5660 				status = mbox_rsp->hdr.status;
5661 			}
5662 			cb_arg->cb(status,
5663 				   num_counters,
5664 				   counts,
5665 				   cb_arg->arg);
5666 		}
5667 
5668 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5669 	}
5670 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5671 
5672 	return 0;
5673 }
5674 
5675 /**
5676  * @brief HW link configuration enum to the CLP string value mapping.
5677  *
5678  * This structure provides a mapping from the ocs_hw_linkcfg_e
5679  * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5680  * control) to the CLP string that is used
5681  * in the DMTF_CLP_CMD mailbox command.
5682  */
5683 typedef struct ocs_hw_linkcfg_map_s {
5684 	ocs_hw_linkcfg_e linkcfg;
5685 	const char *clp_str;
5686 } ocs_hw_linkcfg_map_t;
5687 
5688 /**
5689  * @brief Mapping from the HW linkcfg enum to the CLP command value
5690  * string.
5691  */
5692 static ocs_hw_linkcfg_map_t linkcfg_map[] = {
5693 	{OCS_HW_LINKCFG_4X10G, "ELX_4x10G"},
5694 	{OCS_HW_LINKCFG_1X40G, "ELX_1x40G"},
5695 	{OCS_HW_LINKCFG_2X16G, "ELX_2x16G"},
5696 	{OCS_HW_LINKCFG_4X8G, "ELX_4x8G"},
5697 	{OCS_HW_LINKCFG_4X1G, "ELX_4x1G"},
5698 	{OCS_HW_LINKCFG_2X10G, "ELX_2x10G"},
5699 	{OCS_HW_LINKCFG_2X10G_2X8G, "ELX_2x10G_2x8G"}};
5700 
5701 /**
5702  * @brief HW link configuration enum to Skyhawk link config ID mapping.
5703  *
5704  * This structure provides a mapping from the ocs_hw_linkcfg_e
5705  * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5706  * control) to the link config ID numbers used by Skyhawk
5707  */
5708 typedef struct ocs_hw_skyhawk_linkcfg_map_s {
5709 	ocs_hw_linkcfg_e linkcfg;
5710 	uint32_t	config_id;
5711 } ocs_hw_skyhawk_linkcfg_map_t;
5712 
5713 /**
5714  * @brief Mapping from the HW linkcfg enum to the Skyhawk link config IDs
5715  */
5716 static ocs_hw_skyhawk_linkcfg_map_t skyhawk_linkcfg_map[] = {
5717 	{OCS_HW_LINKCFG_4X10G, 0x0a},
5718 	{OCS_HW_LINKCFG_1X40G, 0x09},
5719 };
5720 
5721 /**
5722  * @brief Helper function for getting the HW linkcfg enum from the CLP
5723  * string value
5724  *
5725  * @param clp_str CLP string value from OEMELX_LinkConfig.
5726  *
5727  * @return Returns the HW linkcfg enum corresponding to clp_str.
5728  */
5729 static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_clp(const char * clp_str)5730 ocs_hw_linkcfg_from_clp(const char *clp_str)
5731 {
5732 	uint32_t i;
5733 	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5734 		if (ocs_strncmp(linkcfg_map[i].clp_str, clp_str, ocs_strlen(clp_str)) == 0) {
5735 			return linkcfg_map[i].linkcfg;
5736 		}
5737 	}
5738 	return OCS_HW_LINKCFG_NA;
5739 }
5740 
5741 /**
5742  * @brief Helper function for getting the CLP string value from the HW
5743  * linkcfg enum.
5744  *
5745  * @param linkcfg HW linkcfg enum.
5746  *
5747  * @return Returns the OEMELX_LinkConfig CLP string value corresponding to
5748  * given linkcfg.
5749  */
5750 static const char *
ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)5751 ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5752 {
5753 	uint32_t i;
5754 	for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5755 		if (linkcfg_map[i].linkcfg == linkcfg) {
5756 			return linkcfg_map[i].clp_str;
5757 		}
5758 	}
5759 	return NULL;
5760 }
5761 
5762 /**
5763  * @brief Helper function for getting a Skyhawk link config ID from the HW
5764  * linkcfg enum.
5765  *
5766  * @param linkcfg HW linkcfg enum.
5767  *
5768  * @return Returns the Skyhawk link config ID corresponding to
5769  * given linkcfg.
5770  */
5771 static uint32_t
ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)5772 ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5773 {
5774 	uint32_t i;
5775 	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5776 		if (skyhawk_linkcfg_map[i].linkcfg == linkcfg) {
5777 			return skyhawk_linkcfg_map[i].config_id;
5778 		}
5779 	}
5780 	return 0;
5781 }
5782 
5783 /**
5784  * @brief Helper function for getting the HW linkcfg enum from a
5785  * Skyhawk config ID.
5786  *
5787  * @param config_id Skyhawk link config ID.
5788  *
5789  * @return Returns the HW linkcfg enum corresponding to config_id.
5790  */
5791 static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_config_id(const uint32_t config_id)5792 ocs_hw_linkcfg_from_config_id(const uint32_t config_id)
5793 {
5794 	uint32_t i;
5795 	for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5796 		if (skyhawk_linkcfg_map[i].config_id == config_id) {
5797 			return skyhawk_linkcfg_map[i].linkcfg;
5798 		}
5799 	}
5800 	return OCS_HW_LINKCFG_NA;
5801 }
5802 
5803 /**
5804  * @brief Link configuration callback argument.
5805  */
5806 typedef struct ocs_hw_linkcfg_cb_arg_s {
5807 	ocs_hw_port_control_cb_t cb;
5808 	void *arg;
5809 	uint32_t opts;
5810 	int32_t status;
5811 	ocs_dma_t dma_cmd;
5812 	ocs_dma_t dma_resp;
5813 	uint32_t result_len;
5814 } ocs_hw_linkcfg_cb_arg_t;
5815 
5816 /**
5817  * @brief Set link configuration.
5818  *
5819  * @param hw Hardware context.
5820  * @param value Link configuration enum to which the link configuration is
5821  * set.
5822  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5823  * @param cb Callback function to invoke following mbx command.
5824  * @param arg Callback argument.
5825  *
5826  * @return Returns OCS_HW_RTN_SUCCESS on success.
5827  */
5828 static ocs_hw_rtn_e
ocs_hw_set_linkcfg(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5829 ocs_hw_set_linkcfg(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5830 {
5831 	if (!sli_link_is_configurable(&hw->sli)) {
5832 		ocs_log_debug(hw->os, "Function not supported\n");
5833 		return OCS_HW_RTN_ERROR;
5834 	}
5835 
5836 	if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
5837 		return ocs_hw_set_linkcfg_lancer(hw, value, opts, cb, arg);
5838 	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
5839 		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
5840 		return ocs_hw_set_linkcfg_skyhawk(hw, value, opts, cb, arg);
5841 	} else {
5842 		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
5843 		return OCS_HW_RTN_ERROR;
5844 	}
5845 }
5846 
5847 /**
5848  * @brief Set link configuration for Lancer
5849  *
5850  * @param hw Hardware context.
5851  * @param value Link configuration enum to which the link configuration is
5852  * set.
5853  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5854  * @param cb Callback function to invoke following mbx command.
5855  * @param arg Callback argument.
5856  *
5857  * @return Returns OCS_HW_RTN_SUCCESS on success.
5858  */
5859 static ocs_hw_rtn_e
ocs_hw_set_linkcfg_lancer(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5860 ocs_hw_set_linkcfg_lancer(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5861 {
5862 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
5863 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5864 	const char *value_str = NULL;
5865 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5866 
5867 	/* translate ocs_hw_linkcfg_e to CLP string */
5868 	value_str = ocs_hw_clp_from_linkcfg(value);
5869 
5870 	/* allocate memory for callback argument */
5871 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
5872 	if (cb_arg == NULL) {
5873 		ocs_log_err(hw->os, "failed to malloc cb_arg");
5874 		return OCS_HW_RTN_NO_MEMORY;
5875 	}
5876 
5877 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_LinkConfig=%s", value_str);
5878 	/* allocate DMA for command  */
5879 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
5880 		ocs_log_err(hw->os, "malloc failed\n");
5881 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5882 		return OCS_HW_RTN_NO_MEMORY;
5883 	}
5884 	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
5885 	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
5886 
5887 	/* allocate DMA for response */
5888 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
5889 		ocs_log_err(hw->os, "malloc failed\n");
5890 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5891 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5892 		return OCS_HW_RTN_NO_MEMORY;
5893 	}
5894 	cb_arg->cb = cb;
5895 	cb_arg->arg = arg;
5896 	cb_arg->opts = opts;
5897 
5898 	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
5899 					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
5900 
5901 	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
5902 		/* if failed, or polling, free memory here; if success and not
5903 		 * polling, will free in callback function
5904 		 */
5905 		if (rc) {
5906 			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
5907 					(char *)cb_arg->dma_cmd.virt);
5908 		}
5909 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5910 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
5911 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5912 	}
5913 	return rc;
5914 }
5915 
5916 /**
5917  * @brief Callback for ocs_hw_set_linkcfg_skyhawk
5918  *
5919  * @param hw Hardware context.
5920  * @param status Status from the RECONFIG_GET_LINK_INFO command.
5921  * @param mqe Mailbox response structure.
5922  * @param arg Pointer to a callback argument.
5923  *
5924  * @return none
5925  */
5926 static void
ocs_hw_set_active_link_config_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5927 ocs_hw_set_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
5928 {
5929 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
5930 
5931 	if (status) {
5932 		ocs_log_test(hw->os, "SET_RECONFIG_LINK_ID failed, status=%d\n", status);
5933 	}
5934 
5935 	/* invoke callback */
5936 	if (cb_arg->cb) {
5937 		cb_arg->cb(status, 0, cb_arg->arg);
5938 	}
5939 
5940 	/* if polling, will free memory in calling function */
5941 	if (cb_arg->opts != OCS_CMD_POLL) {
5942 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5943 	}
5944 }
5945 
5946 /**
5947  * @brief Set link configuration for a Skyhawk
5948  *
5949  * @param hw Hardware context.
5950  * @param value Link configuration enum to which the link configuration is
5951  * set.
5952  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5953  * @param cb Callback function to invoke following mbx command.
5954  * @param arg Callback argument.
5955  *
5956  * @return Returns OCS_HW_RTN_SUCCESS on success.
5957  */
5958 static ocs_hw_rtn_e
ocs_hw_set_linkcfg_skyhawk(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5959 ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5960 {
5961 	uint8_t *mbxdata;
5962 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
5963 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5964 	uint32_t config_id;
5965 
5966 	config_id = ocs_hw_config_id_from_linkcfg(value);
5967 
5968 	if (config_id == 0) {
5969 		ocs_log_test(hw->os, "Link config %d not supported by Skyhawk\n", value);
5970 		return OCS_HW_RTN_ERROR;
5971 	}
5972 
5973 	/* mbxdata holds the header of the command */
5974 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5975 	if (mbxdata == NULL) {
5976 		ocs_log_err(hw->os, "failed to malloc mbox\n");
5977 		return OCS_HW_RTN_NO_MEMORY;
5978 	}
5979 
5980 	/* cb_arg holds the data that will be passed to the callback on completion */
5981 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
5982 	if (cb_arg == NULL) {
5983 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5984 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5985 		return OCS_HW_RTN_NO_MEMORY;
5986 	}
5987 
5988 	cb_arg->cb = cb;
5989 	cb_arg->arg = arg;
5990 
5991 	if (sli_cmd_common_set_reconfig_link_id(&hw->sli, mbxdata, SLI4_BMBX_SIZE, NULL, 0, config_id)) {
5992 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_set_active_link_config_cb, cb_arg);
5993 	}
5994 
5995 	if (rc != OCS_HW_RTN_SUCCESS) {
5996 		ocs_log_err(hw->os, "SET_RECONFIG_LINK_ID failed\n");
5997 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5998 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
5999 	} else if (opts == OCS_CMD_POLL) {
6000 		/* if we're polling we have to call the callback here. */
6001 		ocs_hw_set_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6002 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6003 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6004 	} else {
6005 		/* We weren't poling, so the callback got called */
6006 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6007 	}
6008 
6009 	return rc;
6010 }
6011 
6012 /**
6013  * @brief Get link configuration.
6014  *
6015  * @param hw Hardware context.
6016  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6017  * @param cb Callback function to invoke following mbx command.
6018  * @param arg Callback argument.
6019  *
6020  * @return Returns OCS_HW_RTN_SUCCESS on success.
6021  */
6022 static ocs_hw_rtn_e
ocs_hw_get_linkcfg(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6023 ocs_hw_get_linkcfg(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6024 {
6025 	if (!sli_link_is_configurable(&hw->sli)) {
6026 		ocs_log_debug(hw->os, "Function not supported\n");
6027 		return OCS_HW_RTN_ERROR;
6028 	}
6029 
6030 	if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
6031 	    (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))){
6032 		return ocs_hw_get_linkcfg_lancer(hw, opts, cb, arg);
6033 	} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
6034 		   (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
6035 		return ocs_hw_get_linkcfg_skyhawk(hw, opts, cb, arg);
6036 	} else {
6037 		ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
6038 		return OCS_HW_RTN_ERROR;
6039 	}
6040 }
6041 
6042 /**
6043  * @brief Get link configuration for a Lancer
6044  *
6045  * @param hw Hardware context.
6046  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6047  * @param cb Callback function to invoke following mbx command.
6048  * @param arg Callback argument.
6049  *
6050  * @return Returns OCS_HW_RTN_SUCCESS on success.
6051  */
6052 static ocs_hw_rtn_e
ocs_hw_get_linkcfg_lancer(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6053 ocs_hw_get_linkcfg_lancer(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6054 {
6055 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6056 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6057 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6058 
6059 	/* allocate memory for callback argument */
6060 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6061 	if (cb_arg == NULL) {
6062 		ocs_log_err(hw->os, "failed to malloc cb_arg");
6063 		return OCS_HW_RTN_NO_MEMORY;
6064 	}
6065 
6066 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "show / OEMELX_LinkConfig");
6067 
6068 	/* allocate DMA for command  */
6069 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6070 		ocs_log_err(hw->os, "malloc failed\n");
6071 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6072 		return OCS_HW_RTN_NO_MEMORY;
6073 	}
6074 
6075 	/* copy CLP command to DMA command */
6076 	ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6077 	ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
6078 
6079 	/* allocate DMA for response */
6080 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6081 		ocs_log_err(hw->os, "malloc failed\n");
6082 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6083 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6084 		return OCS_HW_RTN_NO_MEMORY;
6085 	}
6086 	cb_arg->cb = cb;
6087 	cb_arg->arg = arg;
6088 	cb_arg->opts = opts;
6089 
6090 	rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
6091 					opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
6092 
6093 	if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6094 		/* if failed or polling, free memory here; if not polling and success,
6095 		 * will free in callback function
6096 		 */
6097 		if (rc) {
6098 			ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
6099 					(char *)cb_arg->dma_cmd.virt);
6100 		}
6101 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6102 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6103 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6104 	}
6105 	return rc;
6106 }
6107 
6108 
6109 /**
6110  * @brief Get the link configuration callback.
6111  *
6112  * @param hw Hardware context.
6113  * @param status Status from the RECONFIG_GET_LINK_INFO command.
6114  * @param mqe Mailbox response structure.
6115  * @param arg Pointer to a callback argument.
6116  *
6117  * @return none
6118  */
6119 static void
ocs_hw_get_active_link_config_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6120 ocs_hw_get_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6121 {
6122 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6123 	sli4_res_common_get_reconfig_link_info_t *rsp = cb_arg->dma_cmd.virt;
6124 	ocs_hw_linkcfg_e value = OCS_HW_LINKCFG_NA;
6125 
6126 	if (status) {
6127 		ocs_log_test(hw->os, "GET_RECONFIG_LINK_INFO failed, status=%d\n", status);
6128 	} else {
6129 		/* Call was successful */
6130 		value = ocs_hw_linkcfg_from_config_id(rsp->active_link_config_id);
6131 	}
6132 
6133 	/* invoke callback */
6134 	if (cb_arg->cb) {
6135 		cb_arg->cb(status, value, cb_arg->arg);
6136 	}
6137 
6138 	/* if polling, will free memory in calling function */
6139 	if (cb_arg->opts != OCS_CMD_POLL) {
6140 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6141 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6142 	}
6143 }
6144 
6145 /**
6146  * @brief Get link configuration for a Skyhawk.
6147  *
6148  * @param hw Hardware context.
6149  * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6150  * @param cb Callback function to invoke following mbx command.
6151  * @param arg Callback argument.
6152  *
6153  * @return Returns OCS_HW_RTN_SUCCESS on success.
6154  */
6155 static ocs_hw_rtn_e
ocs_hw_get_linkcfg_skyhawk(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6156 ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6157 {
6158 	uint8_t *mbxdata;
6159 	ocs_hw_linkcfg_cb_arg_t *cb_arg;
6160 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6161 
6162 	/* mbxdata holds the header of the command */
6163 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6164 	if (mbxdata == NULL) {
6165 		ocs_log_err(hw->os, "failed to malloc mbox\n");
6166 		return OCS_HW_RTN_NO_MEMORY;
6167 	}
6168 
6169 	/* cb_arg holds the data that will be passed to the callback on completion */
6170 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
6171 	if (cb_arg == NULL) {
6172 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
6173 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6174 		return OCS_HW_RTN_NO_MEMORY;
6175 	}
6176 
6177 	cb_arg->cb = cb;
6178 	cb_arg->arg = arg;
6179 	cb_arg->opts = opts;
6180 
6181 	/* dma_mem holds the non-embedded portion */
6182 	if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, sizeof(sli4_res_common_get_reconfig_link_info_t), 4)) {
6183 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
6184 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6185 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6186 		return OCS_HW_RTN_NO_MEMORY;
6187 	}
6188 
6189 	if (sli_cmd_common_get_reconfig_link_info(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->dma_cmd)) {
6190 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_get_active_link_config_cb, cb_arg);
6191 	}
6192 
6193 	if (rc != OCS_HW_RTN_SUCCESS) {
6194 		ocs_log_err(hw->os, "GET_RECONFIG_LINK_INFO failed\n");
6195 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6196 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6197 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6198 	} else if (opts == OCS_CMD_POLL) {
6199 		/* if we're polling we have to call the callback here. */
6200 		ocs_hw_get_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6201 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6202 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6203 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6204 	} else {
6205 		/* We weren't poling, so the callback got called */
6206 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6207 	}
6208 
6209 	return rc;
6210 }
6211 
6212 /**
6213  * @brief Sets the DIF seed value.
6214  *
6215  * @param hw Hardware context.
6216  *
6217  * @return Returns OCS_HW_RTN_SUCCESS on success.
6218  */
6219 static ocs_hw_rtn_e
ocs_hw_set_dif_seed(ocs_hw_t * hw)6220 ocs_hw_set_dif_seed(ocs_hw_t *hw)
6221 {
6222 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6223 	uint8_t buf[SLI4_BMBX_SIZE];
6224 	sli4_req_common_set_features_dif_seed_t seed_param;
6225 
6226 	ocs_memset(&seed_param, 0, sizeof(seed_param));
6227 	seed_param.seed = hw->config.dif_seed;
6228 
6229 	/* send set_features command */
6230 	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6231 					SLI4_SET_FEATURES_DIF_SEED,
6232 					4,
6233 					(uint32_t*)&seed_param)) {
6234 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6235 		if (rc) {
6236 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6237 		} else {
6238 			ocs_log_debug(hw->os, "DIF seed set to 0x%x\n",
6239 					hw->config.dif_seed);
6240 		}
6241 	} else {
6242 		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6243 		rc = OCS_HW_RTN_ERROR;
6244 	}
6245 	return rc;
6246 }
6247 
6248 
6249 /**
6250  * @brief Sets the DIF mode value.
6251  *
6252  * @param hw Hardware context.
6253  *
6254  * @return Returns OCS_HW_RTN_SUCCESS on success.
6255  */
6256 static ocs_hw_rtn_e
ocs_hw_set_dif_mode(ocs_hw_t * hw)6257 ocs_hw_set_dif_mode(ocs_hw_t *hw)
6258 {
6259 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6260 	uint8_t buf[SLI4_BMBX_SIZE];
6261 	sli4_req_common_set_features_t10_pi_mem_model_t mode_param;
6262 
6263 	ocs_memset(&mode_param, 0, sizeof(mode_param));
6264 	mode_param.tmm = (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? 0 : 1);
6265 
6266 	/* send set_features command */
6267 	if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6268 					SLI4_SET_FEATURES_DIF_MEMORY_MODE,
6269 					sizeof(mode_param),
6270 					(uint32_t*)&mode_param)) {
6271 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6272 		if (rc) {
6273 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6274 		} else {
6275 			ocs_log_test(hw->os, "DIF mode set to %s\n",
6276 				(hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? "inline" : "separate"));
6277 		}
6278 	} else {
6279 		ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6280 		rc = OCS_HW_RTN_ERROR;
6281 	}
6282 	return rc;
6283 }
6284 
6285 static void
ocs_hw_watchdog_timer_cb(void * arg)6286 ocs_hw_watchdog_timer_cb(void *arg)
6287 {
6288 	ocs_hw_t *hw = (ocs_hw_t *)arg;
6289 
6290 	ocs_hw_config_watchdog_timer(hw);
6291 	return;
6292 }
6293 
6294 static void
ocs_hw_cb_cfg_watchdog(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6295 ocs_hw_cb_cfg_watchdog(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6296 {
6297 	uint16_t timeout = hw->watchdog_timeout;
6298 
6299 	if (status != 0) {
6300 		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", status);
6301 	} else {
6302 		if(timeout != 0) {
6303 			/* keeping callback 500ms before timeout to keep heartbeat alive */
6304 			ocs_setup_timer(hw->os, &hw->watchdog_timer, ocs_hw_watchdog_timer_cb, hw, (timeout*1000 - 500) );
6305 		}else {
6306 			ocs_del_timer(&hw->watchdog_timer);
6307 		}
6308 	}
6309 
6310 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6311 	return;
6312 }
6313 
6314 /**
6315  * @brief Set configuration parameters for watchdog timer feature.
6316  *
6317  * @param hw Hardware context.
6318  * @param timeout Timeout for watchdog timer in seconds
6319  *
6320  * @return Returns OCS_HW_RTN_SUCCESS on success.
6321  */
6322 static ocs_hw_rtn_e
ocs_hw_config_watchdog_timer(ocs_hw_t * hw)6323 ocs_hw_config_watchdog_timer(ocs_hw_t *hw)
6324 {
6325 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6326 	uint8_t *buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
6327 
6328 	sli4_cmd_lowlevel_set_watchdog(&hw->sli, buf, SLI4_BMBX_SIZE, hw->watchdog_timeout);
6329 	rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_cfg_watchdog, NULL);
6330 	if (rc) {
6331 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
6332 		ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", rc);
6333 	}
6334 	return rc;
6335 }
6336 
6337 /**
6338  * @brief Set configuration parameters for auto-generate xfer_rdy T10 PI feature.
6339  *
6340  * @param hw Hardware context.
6341  * @param buf Pointer to a mailbox buffer area.
6342  *
6343  * @return Returns OCS_HW_RTN_SUCCESS on success.
6344  */
6345 static ocs_hw_rtn_e
ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t * hw,uint8_t * buf)6346 ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf)
6347 {
6348 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6349 	sli4_req_common_set_features_xfer_rdy_t10pi_t param;
6350 
6351 	ocs_memset(&param, 0, sizeof(param));
6352 	param.rtc = (hw->config.auto_xfer_rdy_ref_tag_is_lba ? 0 : 1);
6353 	param.atv = (hw->config.auto_xfer_rdy_app_tag_valid ? 1 : 0);
6354 	param.tmm = ((hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE) ? 0 : 1);
6355 	param.app_tag = hw->config.auto_xfer_rdy_app_tag_value;
6356 	param.blk_size = hw->config.auto_xfer_rdy_blk_size_chip;
6357 
6358 	switch (hw->config.auto_xfer_rdy_p_type) {
6359 	case 1:
6360 		param.p_type = 0;
6361 		break;
6362 	case 3:
6363 		param.p_type = 2;
6364 		break;
6365 	default:
6366 		ocs_log_err(hw->os, "unsupported p_type %d\n",
6367 			hw->config.auto_xfer_rdy_p_type);
6368 		return OCS_HW_RTN_ERROR;
6369 	}
6370 
6371 	/* build the set_features command */
6372 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6373 				    SLI4_SET_FEATURES_SET_CONFIG_AUTO_XFER_RDY_T10PI,
6374 				    sizeof(param),
6375 				    &param);
6376 
6377 
6378 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6379 	if (rc) {
6380 		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6381 	} else {
6382 		ocs_log_test(hw->os, "Auto XFER RDY T10 PI configured rtc:%d atv:%d p_type:%d app_tag:%x blk_size:%d\n",
6383 				param.rtc, param.atv, param.p_type,
6384 				param.app_tag, param.blk_size);
6385 	}
6386 
6387 	return rc;
6388 }
6389 
6390 
6391 /**
6392  * @brief enable sli port health check
6393  *
6394  * @param hw Hardware context.
6395  * @param buf Pointer to a mailbox buffer area.
6396  * @param query current status of the health check feature enabled/disabled
6397  * @param enable if 1: enable 0: disable
6398  * @param buf Pointer to a mailbox buffer area.
6399  *
6400  * @return Returns OCS_HW_RTN_SUCCESS on success.
6401  */
6402 static ocs_hw_rtn_e
ocs_hw_config_sli_port_health_check(ocs_hw_t * hw,uint8_t query,uint8_t enable)6403 ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable)
6404 {
6405 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6406 	uint8_t buf[SLI4_BMBX_SIZE];
6407 	sli4_req_common_set_features_health_check_t param;
6408 
6409 	ocs_memset(&param, 0, sizeof(param));
6410 	param.hck = enable;
6411 	param.qry = query;
6412 
6413 	/* build the set_features command */
6414 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6415 				    SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK,
6416 				    sizeof(param),
6417 				    &param);
6418 
6419 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6420 	if (rc) {
6421 		ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6422 	} else {
6423 		ocs_log_test(hw->os, "SLI Port Health Check is enabled \n");
6424 	}
6425 
6426 	return rc;
6427 }
6428 
6429 /**
6430  * @brief Set FTD transfer hint feature
6431  *
6432  * @param hw Hardware context.
6433  * @param fdt_xfer_hint size in bytes where read requests are segmented.
6434  *
6435  * @return Returns OCS_HW_RTN_SUCCESS on success.
6436  */
6437 static ocs_hw_rtn_e
ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t * hw,uint32_t fdt_xfer_hint)6438 ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint)
6439 {
6440 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6441 	uint8_t buf[SLI4_BMBX_SIZE];
6442 	sli4_req_common_set_features_set_fdt_xfer_hint_t param;
6443 
6444 	ocs_memset(&param, 0, sizeof(param));
6445 	param.fdt_xfer_hint = fdt_xfer_hint;
6446 	/* build the set_features command */
6447 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6448 				    SLI4_SET_FEATURES_SET_FTD_XFER_HINT,
6449 				    sizeof(param),
6450 				    &param);
6451 
6452 
6453 	rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6454 	if (rc) {
6455 		ocs_log_warn(hw->os, "set FDT hint %d failed: %d\n", fdt_xfer_hint, rc);
6456 	} else {
6457 		ocs_log_debug(hw->os, "Set FTD transfer hint to %d\n", param.fdt_xfer_hint);
6458 	}
6459 
6460 	return rc;
6461 }
6462 
6463 /**
6464  * @brief Get the link configuration callback.
6465  *
6466  * @param hw Hardware context.
6467  * @param status Status from the DMTF CLP command.
6468  * @param result_len Length, in bytes, of the DMTF CLP result.
6469  * @param arg Pointer to a callback argument.
6470  *
6471  * @return Returns OCS_HW_RTN_SUCCESS on success.
6472  */
6473 static void
ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t * hw,int32_t status,uint32_t result_len,void * arg)6474 ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg)
6475 {
6476 	int32_t rval;
6477 	char retdata_str[64];
6478 	ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6479 	ocs_hw_linkcfg_e linkcfg = OCS_HW_LINKCFG_NA;
6480 
6481 	if (status) {
6482 		ocs_log_test(hw->os, "CLP cmd failed, status=%d\n", status);
6483 	} else {
6484 		/* parse CLP response to get return data */
6485 		rval = ocs_hw_clp_resp_get_value(hw, "retdata", retdata_str,
6486 						  sizeof(retdata_str),
6487 						  cb_arg->dma_resp.virt,
6488 						  result_len);
6489 
6490 		if (rval <= 0) {
6491 			ocs_log_err(hw->os, "failed to get retdata %d\n", result_len);
6492 		} else {
6493 			/* translate string into hw enum */
6494 			linkcfg = ocs_hw_linkcfg_from_clp(retdata_str);
6495 		}
6496 	}
6497 
6498 	/* invoke callback */
6499 	if (cb_arg->cb) {
6500 		cb_arg->cb(status, linkcfg, cb_arg->arg);
6501 	}
6502 
6503 	/* if polling, will free memory in calling function */
6504 	if (cb_arg->opts != OCS_CMD_POLL) {
6505 		ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6506 		ocs_dma_free(hw->os, &cb_arg->dma_resp);
6507 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6508 	}
6509 }
6510 
6511 /**
6512  * @brief Set the Lancer dump location
6513  * @par Description
6514  * This function tells a Lancer chip to use a specific DMA
6515  * buffer as a dump location rather than the internal flash.
6516  *
6517  * @param hw Hardware context.
6518  * @param num_buffers The number of DMA buffers to hold the dump (1..n).
6519  * @param dump_buffers DMA buffers to hold the dump.
6520  *
6521  * @return Returns OCS_HW_RTN_SUCCESS on success.
6522  */
6523 ocs_hw_rtn_e
ocs_hw_set_dump_location(ocs_hw_t * hw,uint32_t num_buffers,ocs_dma_t * dump_buffers,uint8_t fdb)6524 ocs_hw_set_dump_location(ocs_hw_t *hw, uint32_t num_buffers, ocs_dma_t *dump_buffers, uint8_t fdb)
6525 {
6526 	uint8_t bus, dev, func;
6527 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6528 	uint8_t	buf[SLI4_BMBX_SIZE];
6529 
6530 	/*
6531 	 * Make sure the FW is new enough to support this command. If the FW
6532 	 * is too old, the FW will UE.
6533 	 */
6534 	if (hw->workaround.disable_dump_loc) {
6535 		ocs_log_test(hw->os, "FW version is too old for this feature\n");
6536 		return OCS_HW_RTN_ERROR;
6537 	}
6538 
6539 	/* This command is only valid for physical port 0 */
6540 	ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
6541 	if (fdb == 0 && func != 0) {
6542 		ocs_log_test(hw->os, "function only valid for pci function 0, %d passed\n",
6543 			     func);
6544 		return OCS_HW_RTN_ERROR;
6545 	}
6546 
6547 	/*
6548 	 * If a single buffer is used, then it may be passed as is to the chip. For multiple buffers,
6549 	 * We must allocate a SGL list and then pass the address of the list to the chip.
6550 	 */
6551 	if (num_buffers > 1) {
6552 		uint32_t sge_size = num_buffers * sizeof(sli4_sge_t);
6553 		sli4_sge_t *sge;
6554 		uint32_t i;
6555 
6556 		if (hw->dump_sges.size < sge_size) {
6557 			ocs_dma_free(hw->os, &hw->dump_sges);
6558 			if (ocs_dma_alloc(hw->os, &hw->dump_sges, sge_size, OCS_MIN_DMA_ALIGNMENT)) {
6559 				ocs_log_err(hw->os, "SGE DMA allocation failed\n");
6560 				return OCS_HW_RTN_NO_MEMORY;
6561 			}
6562 		}
6563 		/* build the SGE list */
6564 		ocs_memset(hw->dump_sges.virt, 0, hw->dump_sges.size);
6565 		hw->dump_sges.len = sge_size;
6566 		sge = hw->dump_sges.virt;
6567 		for (i = 0; i < num_buffers; i++) {
6568 			sge[i].buffer_address_high = ocs_addr32_hi(dump_buffers[i].phys);
6569 			sge[i].buffer_address_low = ocs_addr32_lo(dump_buffers[i].phys);
6570 			sge[i].last = (i == num_buffers - 1 ? 1 : 0);
6571 			sge[i].buffer_length = dump_buffers[i].size;
6572 		}
6573 		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6574 						      SLI4_BMBX_SIZE, FALSE, TRUE,
6575 						      &hw->dump_sges, fdb);
6576 	} else {
6577 		dump_buffers->len = dump_buffers->size;
6578 		rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6579 						      SLI4_BMBX_SIZE, FALSE, FALSE,
6580 						      dump_buffers, fdb);
6581 	}
6582 
6583 	if (rc) {
6584 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL,
6585 				     NULL, NULL);
6586 		if (rc) {
6587 			ocs_log_err(hw->os, "ocs_hw_command returns %d\n",
6588 				rc);
6589 		}
6590 	} else {
6591 		ocs_log_err(hw->os,
6592 			"sli_cmd_common_set_dump_location failed\n");
6593 		rc = OCS_HW_RTN_ERROR;
6594 	}
6595 
6596 	return rc;
6597 }
6598 
6599 
6600 /**
6601  * @brief Set the Ethernet license.
6602  *
6603  * @par Description
6604  * This function sends the appropriate mailbox command (DMTF
6605  * CLP) to set the Ethernet license to the given license value.
6606  * Since it is used during the time of ocs_hw_init(), the mailbox
6607  * command is sent via polling (the BMBX route).
6608  *
6609  * @param hw Hardware context.
6610  * @param license 32-bit license value.
6611  *
6612  * @return Returns OCS_HW_RTN_SUCCESS on success.
6613  */
6614 static ocs_hw_rtn_e
ocs_hw_set_eth_license(ocs_hw_t * hw,uint32_t license)6615 ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license)
6616 {
6617 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6618 	char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6619 	ocs_dma_t dma_cmd;
6620 	ocs_dma_t dma_resp;
6621 
6622 	/* only for lancer right now */
6623 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6624 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6625 		return OCS_HW_RTN_ERROR;
6626 	}
6627 
6628 	ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_Ethernet_License=%X", license);
6629 	/* allocate DMA for command  */
6630 	if (ocs_dma_alloc(hw->os, &dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6631 		ocs_log_err(hw->os, "malloc failed\n");
6632 		return OCS_HW_RTN_NO_MEMORY;
6633 	}
6634 	ocs_memset(dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6635 	ocs_memcpy(dma_cmd.virt, cmd, ocs_strlen(cmd));
6636 
6637 	/* allocate DMA for response */
6638 	if (ocs_dma_alloc(hw->os, &dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6639 		ocs_log_err(hw->os, "malloc failed\n");
6640 		ocs_dma_free(hw->os, &dma_cmd);
6641 		return OCS_HW_RTN_NO_MEMORY;
6642 	}
6643 
6644 	/* send DMTF CLP command mbx and poll */
6645 	if (ocs_hw_exec_dmtf_clp_cmd(hw, &dma_cmd, &dma_resp, OCS_CMD_POLL, NULL, NULL)) {
6646 		ocs_log_err(hw->os, "CLP cmd=\"%s\" failed\n", (char *)dma_cmd.virt);
6647 		rc = OCS_HW_RTN_ERROR;
6648 	}
6649 
6650 	ocs_dma_free(hw->os, &dma_cmd);
6651 	ocs_dma_free(hw->os, &dma_resp);
6652 	return rc;
6653 }
6654 
6655 /**
6656  * @brief Callback argument structure for the DMTF CLP commands.
6657  */
6658 typedef struct ocs_hw_clp_cb_arg_s {
6659 	ocs_hw_dmtf_clp_cb_t cb;
6660 	ocs_dma_t *dma_resp;
6661 	int32_t status;
6662 	uint32_t opts;
6663 	void *arg;
6664 } ocs_hw_clp_cb_arg_t;
6665 
6666 /**
6667  * @brief Execute the DMTF CLP command.
6668  *
6669  * @param hw Hardware context.
6670  * @param dma_cmd DMA buffer containing the CLP command.
6671  * @param dma_resp DMA buffer that will contain the response (if successful).
6672  * @param opts Mailbox command options (such as OCS_CMD_NOWAIT and POLL).
6673  * @param cb Callback function.
6674  * @param arg Callback argument.
6675  *
6676  * @return Returns the number of bytes written to the response
6677  * buffer on success, or a negative value if failed.
6678  */
6679 static ocs_hw_rtn_e
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)6680 ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg)
6681 {
6682 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6683 	ocs_hw_clp_cb_arg_t *cb_arg;
6684 	uint8_t *mbxdata;
6685 
6686 	/* allocate DMA for mailbox */
6687 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6688 	if (mbxdata == NULL) {
6689 		ocs_log_err(hw->os, "failed to malloc mbox\n");
6690 		return OCS_HW_RTN_NO_MEMORY;
6691 	}
6692 
6693 	/* allocate memory for callback argument */
6694 	cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6695 	if (cb_arg == NULL) {
6696 		ocs_log_err(hw->os, "failed to malloc cb_arg");
6697 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6698 		return OCS_HW_RTN_NO_MEMORY;
6699 	}
6700 
6701 	cb_arg->cb = cb;
6702 	cb_arg->arg = arg;
6703 	cb_arg->dma_resp = dma_resp;
6704 	cb_arg->opts = opts;
6705 
6706 	/* Send the HW command */
6707 	if (sli_cmd_dmtf_exec_clp_cmd(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
6708 				      dma_cmd, dma_resp)) {
6709 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_dmtf_clp_cb, cb_arg);
6710 
6711 		if (opts == OCS_CMD_POLL && rc == OCS_HW_RTN_SUCCESS) {
6712 			/* if we're polling, copy response and invoke callback to
6713 			 * parse result */
6714 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
6715 			ocs_hw_dmtf_clp_cb(hw, 0, mbxdata, cb_arg);
6716 
6717 			/* set rc to resulting or "parsed" status */
6718 			rc = cb_arg->status;
6719 		}
6720 
6721 		/* if failed, or polling, free memory here */
6722 		if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6723 			if (rc != OCS_HW_RTN_SUCCESS) {
6724 				ocs_log_test(hw->os, "ocs_hw_command failed\n");
6725 			}
6726 			ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6727 			ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6728 		}
6729 	} else {
6730 		ocs_log_test(hw->os, "sli_cmd_dmtf_exec_clp_cmd failed\n");
6731 		rc = OCS_HW_RTN_ERROR;
6732 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6733 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6734 	}
6735 
6736 	return rc;
6737 }
6738 
6739 
6740 /**
6741  * @brief Called when the DMTF CLP command completes.
6742  *
6743  * @param hw Hardware context.
6744  * @param status Status field from the mbox completion.
6745  * @param mqe Mailbox response structure.
6746  * @param arg Pointer to a callback argument.
6747  *
6748  * @return None.
6749  *
6750  */
6751 static void
ocs_hw_dmtf_clp_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6752 ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6753 {
6754 	int32_t cb_status = 0;
6755 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6756 	sli4_res_dmtf_exec_clp_cmd_t *clp_rsp = (sli4_res_dmtf_exec_clp_cmd_t *) mbox_rsp->payload.embed;
6757 	ocs_hw_clp_cb_arg_t *cb_arg = arg;
6758 	uint32_t result_len = 0;
6759 	int32_t stat_len;
6760 	char stat_str[8];
6761 
6762 	/* there are several status codes here, check them all and condense
6763 	 * into a single callback status
6764 	 */
6765 	if (status || mbox_rsp->hdr.status || clp_rsp->clp_status) {
6766 		ocs_log_debug(hw->os, "status=x%x/x%x/x%x  addl=x%x clp=x%x detail=x%x\n",
6767 			status,
6768 			mbox_rsp->hdr.status,
6769 			clp_rsp->hdr.status,
6770 			clp_rsp->hdr.additional_status,
6771 			clp_rsp->clp_status,
6772 			clp_rsp->clp_detailed_status);
6773 		if (status) {
6774 			cb_status = status;
6775 		} else if (mbox_rsp->hdr.status) {
6776 			cb_status = mbox_rsp->hdr.status;
6777 		} else {
6778 			cb_status = clp_rsp->clp_status;
6779 		}
6780 	} else {
6781 		result_len = clp_rsp->resp_length;
6782 	}
6783 
6784 	if (cb_status) {
6785 		goto ocs_hw_cb_dmtf_clp_done;
6786 	}
6787 
6788 	if ((result_len == 0) || (cb_arg->dma_resp->size < result_len)) {
6789 		ocs_log_test(hw->os, "Invalid response length: resp_len=%zu result len=%d\n",
6790 			     cb_arg->dma_resp->size, result_len);
6791 		cb_status = -1;
6792 		goto ocs_hw_cb_dmtf_clp_done;
6793 	}
6794 
6795 	/* parse CLP response to get status */
6796 	stat_len = ocs_hw_clp_resp_get_value(hw, "status", stat_str,
6797 					      sizeof(stat_str),
6798 					      cb_arg->dma_resp->virt,
6799 					      result_len);
6800 
6801 	if (stat_len <= 0) {
6802 		ocs_log_test(hw->os, "failed to get status %d\n", stat_len);
6803 		cb_status = -1;
6804 		goto ocs_hw_cb_dmtf_clp_done;
6805 	}
6806 
6807 	if (ocs_strcmp(stat_str, "0") != 0) {
6808 		ocs_log_test(hw->os, "CLP status indicates failure=%s\n", stat_str);
6809 		cb_status = -1;
6810 		goto ocs_hw_cb_dmtf_clp_done;
6811 	}
6812 
6813 ocs_hw_cb_dmtf_clp_done:
6814 
6815 	/* save status in cb_arg for callers with NULL cb's + polling */
6816 	cb_arg->status = cb_status;
6817 	if (cb_arg->cb) {
6818 		cb_arg->cb(hw, cb_status, result_len, cb_arg->arg);
6819 	}
6820 	/* if polling, caller will free memory */
6821 	if (cb_arg->opts != OCS_CMD_POLL) {
6822 		ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6823 		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6824 	}
6825 }
6826 
6827 /**
6828  * @brief Parse the CLP result and get the value corresponding to the given
6829  * keyword.
6830  *
6831  * @param hw Hardware context.
6832  * @param keyword CLP keyword for which the value is returned.
6833  * @param value Location to which the resulting value is copied.
6834  * @param value_len Length of the value parameter.
6835  * @param resp Pointer to the response buffer that is searched
6836  * for the keyword and value.
6837  * @param resp_len Length of response buffer passed in.
6838  *
6839  * @return Returns the number of bytes written to the value
6840  * buffer on success, or a negative vaue on failure.
6841  */
6842 static int32_t
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)6843 ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len)
6844 {
6845 	char *start = NULL;
6846 	char *end = NULL;
6847 
6848 	/* look for specified keyword in string */
6849 	start = ocs_strstr(resp, keyword);
6850 	if (start == NULL) {
6851 		ocs_log_test(hw->os, "could not find keyword=%s in CLP response\n",
6852 			     keyword);
6853 		return -1;
6854 	}
6855 
6856 	/* now look for '=' and go one past */
6857 	start = ocs_strchr(start, '=');
6858 	if (start == NULL) {
6859 		ocs_log_test(hw->os, "could not find \'=\' in CLP response for keyword=%s\n",
6860 			     keyword);
6861 		return -1;
6862 	}
6863 	start++;
6864 
6865 	/* \r\n terminates value */
6866 	end = ocs_strstr(start, "\r\n");
6867 	if (end == NULL) {
6868 		ocs_log_test(hw->os, "could not find \\r\\n for keyword=%s in CLP response\n",
6869 			     keyword);
6870 		return -1;
6871 	}
6872 
6873 	/* make sure given result array is big enough */
6874 	if ((end - start + 1) > value_len) {
6875 		ocs_log_test(hw->os, "value len=%d not large enough for actual=%ld\n",
6876 			     value_len, (end-start));
6877 		return -1;
6878 	}
6879 
6880 	ocs_strncpy(value, start, (end - start));
6881 	value[end-start] = '\0';
6882 	return (end-start+1);
6883 }
6884 
6885 /**
6886  * @brief Cause chip to enter an unrecoverable error state.
6887  *
6888  * @par Description
6889  * Cause chip to enter an unrecoverable error state. This is
6890  * used when detecting unexpected FW behavior so that the FW can be
6891  * hwted from the driver as soon as the error is detected.
6892  *
6893  * @param hw Hardware context.
6894  * @param dump Generate dump as part of reset.
6895  *
6896  * @return Returns 0 on success, or a non-zero value on failure.
6897  *
6898  */
6899 ocs_hw_rtn_e
ocs_hw_raise_ue(ocs_hw_t * hw,uint8_t dump)6900 ocs_hw_raise_ue(ocs_hw_t *hw, uint8_t dump)
6901 {
6902 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6903 
6904 	if (sli_raise_ue(&hw->sli, dump) != 0) {
6905 		rc = OCS_HW_RTN_ERROR;
6906 	} else {
6907 		if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
6908 			hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
6909 		}
6910 	}
6911 
6912 	return rc;
6913 }
6914 
6915 /**
6916  * @brief Called when the OBJECT_GET command completes.
6917  *
6918  * @par Description
6919  * Get the number of bytes actually written out of the response, free the mailbox
6920  * that was malloc'd by ocs_hw_dump_get(), then call the callback
6921  * and pass the status and bytes read.
6922  *
6923  * @param hw Hardware context.
6924  * @param status Status field from the mbox completion.
6925  * @param mqe Mailbox response structure.
6926  * @param arg Pointer to a callback function that signals the caller that the command is done.
6927  * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_read)</tt>.
6928  *
6929  * @return Returns 0.
6930  */
6931 static int32_t
ocs_hw_cb_dump_get(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6932 ocs_hw_cb_dump_get(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
6933 {
6934 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6935 	sli4_res_common_read_object_t* rd_obj_rsp = (sli4_res_common_read_object_t*) mbox_rsp->payload.embed;
6936 	ocs_hw_dump_get_cb_arg_t *cb_arg = arg;
6937 	uint32_t bytes_read;
6938 	uint8_t eof;
6939 
6940 	bytes_read = rd_obj_rsp->actual_read_length;
6941 	eof = rd_obj_rsp->eof;
6942 
6943 	if (cb_arg) {
6944 		if (cb_arg->cb) {
6945 			if ((status == 0) && mbox_rsp->hdr.status) {
6946 				status = mbox_rsp->hdr.status;
6947 			}
6948 			cb_arg->cb(status, bytes_read, eof, cb_arg->arg);
6949 		}
6950 
6951 		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
6952 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
6953 	}
6954 
6955 	return 0;
6956 }
6957 
6958 
6959 /**
6960  * @brief Read a dump image to the host.
6961  *
6962  * @par Description
6963  * Creates a SLI_CONFIG mailbox command, fills in the correct values to read a
6964  * dump image chunk, then sends the command with the ocs_hw_command(). On completion,
6965  * the callback function ocs_hw_cb_dump_get() gets called to free the mailbox
6966  * and signal the caller that the read has completed.
6967  *
6968  * @param hw Hardware context.
6969  * @param dma DMA structure to transfer the dump chunk into.
6970  * @param size Size of the dump chunk.
6971  * @param offset Offset, in bytes, from the beginning of the dump.
6972  * @param cb Pointer to a callback function that is called when the command completes.
6973  * The callback function prototype is
6974  * <tt>void cb(int32_t status, uint32_t bytes_read, uint8_t eof, void *arg)</tt>.
6975  * @param arg Pointer to be passed to the callback function.
6976  *
6977  * @return Returns 0 on success, or a non-zero value on failure.
6978  */
6979 ocs_hw_rtn_e
ocs_hw_dump_get(ocs_hw_t * hw,ocs_dma_t * dma,uint32_t size,uint32_t offset,ocs_hw_dump_get_cb_t cb,void * arg)6980 ocs_hw_dump_get(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, ocs_hw_dump_get_cb_t cb, void *arg)
6981 {
6982 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6983 	uint8_t *mbxdata;
6984 	ocs_hw_dump_get_cb_arg_t *cb_arg;
6985 	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
6986 
6987 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6988 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6989 		return OCS_HW_RTN_ERROR;
6990 	}
6991 
6992 	if (1 != sli_dump_is_present(&hw->sli)) {
6993 		ocs_log_test(hw->os, "No dump is present\n");
6994 		return OCS_HW_RTN_ERROR;
6995 	}
6996 
6997 	if (1 == sli_reset_required(&hw->sli)) {
6998 		ocs_log_test(hw->os, "device reset required\n");
6999 		return OCS_HW_RTN_ERROR;
7000 	}
7001 
7002 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7003 	if (mbxdata == NULL) {
7004 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7005 		return OCS_HW_RTN_NO_MEMORY;
7006 	}
7007 
7008 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_get_cb_arg_t), OCS_M_NOWAIT);
7009 	if (cb_arg == NULL) {
7010 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7011 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7012 		return OCS_HW_RTN_NO_MEMORY;
7013 	}
7014 
7015 	cb_arg->cb = cb;
7016 	cb_arg->arg = arg;
7017 	cb_arg->mbox_cmd = mbxdata;
7018 
7019 	if (sli_cmd_common_read_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7020 			size, offset, "/dbg/dump.bin", dma)) {
7021 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_get, cb_arg);
7022 		if (rc == 0 && opts == OCS_CMD_POLL) {
7023 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7024 			rc = ocs_hw_cb_dump_get(hw, 0, mbxdata, cb_arg);
7025 		}
7026 	}
7027 
7028 	if (rc != OCS_HW_RTN_SUCCESS) {
7029 		ocs_log_test(hw->os, "COMMON_READ_OBJECT failed\n");
7030 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7031 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
7032 	}
7033 
7034 	return rc;
7035 }
7036 
7037 /**
7038  * @brief Called when the OBJECT_DELETE command completes.
7039  *
7040  * @par Description
7041  * Free the mailbox that was malloc'd
7042  * by ocs_hw_dump_clear(), then call the callback and pass the status.
7043  *
7044  * @param hw Hardware context.
7045  * @param status Status field from the mbox completion.
7046  * @param mqe Mailbox response structure.
7047  * @param arg Pointer to a callback function that signals the caller that the command is done.
7048  * The callback function prototype is <tt>void cb(int32_t status, void *arg)</tt>.
7049  *
7050  * @return Returns 0.
7051  */
7052 static int32_t
ocs_hw_cb_dump_clear(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7053 ocs_hw_cb_dump_clear(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
7054 {
7055 	ocs_hw_dump_clear_cb_arg_t *cb_arg = arg;
7056 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7057 
7058 	if (cb_arg) {
7059 		if (cb_arg->cb) {
7060 			if ((status == 0) && mbox_rsp->hdr.status) {
7061 				status = mbox_rsp->hdr.status;
7062 			}
7063 			cb_arg->cb(status, cb_arg->arg);
7064 		}
7065 
7066 		ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
7067 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7068 	}
7069 
7070 	return 0;
7071 }
7072 
7073 /**
7074  * @brief Clear a dump image from the device.
7075  *
7076  * @par Description
7077  * Creates a SLI_CONFIG mailbox command, fills it with the correct values to clear
7078  * the dump, then sends the command with ocs_hw_command(). On completion,
7079  * the callback function ocs_hw_cb_dump_clear() gets called to free the mailbox
7080  * and to signal the caller that the write has completed.
7081  *
7082  * @param hw Hardware context.
7083  * @param cb Pointer to a callback function that is called when the command completes.
7084  * The callback function prototype is
7085  * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
7086  * @param arg Pointer to be passed to the callback function.
7087  *
7088  * @return Returns 0 on success, or a non-zero value on failure.
7089  */
7090 ocs_hw_rtn_e
ocs_hw_dump_clear(ocs_hw_t * hw,ocs_hw_dump_clear_cb_t cb,void * arg)7091 ocs_hw_dump_clear(ocs_hw_t *hw, ocs_hw_dump_clear_cb_t cb, void *arg)
7092 {
7093 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7094 	uint8_t *mbxdata;
7095 	ocs_hw_dump_clear_cb_arg_t *cb_arg;
7096 	uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
7097 
7098 	if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
7099 		ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
7100 		return OCS_HW_RTN_ERROR;
7101 	}
7102 
7103 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7104 	if (mbxdata == NULL) {
7105 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7106 		return OCS_HW_RTN_NO_MEMORY;
7107 	}
7108 
7109 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_clear_cb_arg_t), OCS_M_NOWAIT);
7110 	if (cb_arg == NULL) {
7111 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7112 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7113 		return OCS_HW_RTN_NO_MEMORY;
7114 	}
7115 
7116 	cb_arg->cb = cb;
7117 	cb_arg->arg = arg;
7118 	cb_arg->mbox_cmd = mbxdata;
7119 
7120 	if (sli_cmd_common_delete_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7121 			"/dbg/dump.bin")) {
7122 		rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_clear, cb_arg);
7123 		if (rc == 0 && opts == OCS_CMD_POLL) {
7124 			ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7125 			rc = ocs_hw_cb_dump_clear(hw, 0, mbxdata, cb_arg);
7126 		}
7127 	}
7128 
7129 	if (rc != OCS_HW_RTN_SUCCESS) {
7130 		ocs_log_test(hw->os, "COMMON_DELETE_OBJECT failed\n");
7131 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7132 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7133 	}
7134 
7135 	return rc;
7136 }
7137 
7138 typedef struct ocs_hw_get_port_protocol_cb_arg_s {
7139 	ocs_get_port_protocol_cb_t cb;
7140 	void *arg;
7141 	uint32_t pci_func;
7142 	ocs_dma_t payload;
7143 } ocs_hw_get_port_protocol_cb_arg_t;
7144 
7145 /**
7146  * @brief Called for the completion of get_port_profile for a
7147  *        user request.
7148  *
7149  * @param hw Hardware context.
7150  * @param status The status from the MQE.
7151  * @param mqe Pointer to mailbox command buffer.
7152  * @param arg Pointer to a callback argument.
7153  *
7154  * @return Returns 0 on success, or a non-zero value on failure.
7155  */
7156 static int32_t
ocs_hw_get_port_protocol_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7157 ocs_hw_get_port_protocol_cb(ocs_hw_t *hw, int32_t status,
7158 			    uint8_t *mqe, void *arg)
7159 {
7160 	ocs_hw_get_port_protocol_cb_arg_t *cb_arg = arg;
7161 	ocs_dma_t *payload = &(cb_arg->payload);
7162 	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7163 	ocs_hw_port_protocol_e port_protocol;
7164 	int num_descriptors;
7165 	sli4_resource_descriptor_v1_t *desc_p;
7166 	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7167 	int i;
7168 
7169 	port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7170 
7171 	num_descriptors = response->desc_count;
7172 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7173 	for (i=0; i<num_descriptors; i++) {
7174 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7175 			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7176 			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7177 				switch(pcie_desc_p->pf_type) {
7178 				case 0x02:
7179 					port_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
7180 					break;
7181 				case 0x04:
7182 					port_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
7183 					break;
7184 				case 0x10:
7185 					port_protocol = OCS_HW_PORT_PROTOCOL_FC;
7186 					break;
7187 				default:
7188 					port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7189 					break;
7190 				}
7191 			}
7192 		}
7193 
7194 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7195 	}
7196 
7197 	if (cb_arg->cb) {
7198 		cb_arg->cb(status, port_protocol, cb_arg->arg);
7199 
7200 	}
7201 
7202 	ocs_dma_free(hw->os, &cb_arg->payload);
7203 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7204 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7205 
7206 	return 0;
7207 }
7208 
7209 /**
7210  * @ingroup io
7211  * @brief  Get the current port protocol.
7212  * @par Description
7213  * Issues a SLI4 COMMON_GET_PROFILE_CONFIG mailbox.  When the
7214  * command completes the provided mgmt callback function is
7215  * called.
7216  *
7217  * @param hw Hardware context.
7218  * @param pci_func PCI function to query for current protocol.
7219  * @param cb Callback function to be called when the command completes.
7220  * @param ul_arg An argument that is passed to the callback function.
7221  *
7222  * @return
7223  * - OCS_HW_RTN_SUCCESS on success.
7224  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7225  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7226  *   context.
7227  * - OCS_HW_RTN_ERROR on any other error.
7228  */
7229 ocs_hw_rtn_e
ocs_hw_get_port_protocol(ocs_hw_t * hw,uint32_t pci_func,ocs_get_port_protocol_cb_t cb,void * ul_arg)7230 ocs_hw_get_port_protocol(ocs_hw_t *hw, uint32_t pci_func,
7231 	ocs_get_port_protocol_cb_t cb, void* ul_arg)
7232 {
7233 	uint8_t *mbxdata;
7234 	ocs_hw_get_port_protocol_cb_arg_t *cb_arg;
7235 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7236 
7237 	/* Only supported on Skyhawk */
7238 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7239 		return OCS_HW_RTN_ERROR;
7240 	}
7241 
7242 	/* mbxdata holds the header of the command */
7243 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7244 	if (mbxdata == NULL) {
7245 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7246 		return OCS_HW_RTN_NO_MEMORY;
7247 	}
7248 
7249 
7250 	/* cb_arg holds the data that will be passed to the callback on completion */
7251 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7252 	if (cb_arg == NULL) {
7253 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7254 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7255 		return OCS_HW_RTN_NO_MEMORY;
7256 	}
7257 
7258 	cb_arg->cb = cb;
7259 	cb_arg->arg = ul_arg;
7260 	cb_arg->pci_func = pci_func;
7261 
7262 	/* dma_mem holds the non-embedded portion */
7263 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7264 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7265 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7266 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7267 		return OCS_HW_RTN_NO_MEMORY;
7268 	}
7269 
7270 	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7271 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_port_protocol_cb, cb_arg);
7272 	}
7273 
7274 	if (rc != OCS_HW_RTN_SUCCESS) {
7275 		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7276 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7277 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7278 		ocs_dma_free(hw->os, &cb_arg->payload);
7279 	}
7280 
7281 	return rc;
7282 
7283 }
7284 
7285 typedef struct ocs_hw_set_port_protocol_cb_arg_s {
7286 	ocs_set_port_protocol_cb_t cb;
7287 	void *arg;
7288 	ocs_dma_t payload;
7289 	uint32_t new_protocol;
7290 	uint32_t pci_func;
7291 } ocs_hw_set_port_protocol_cb_arg_t;
7292 
7293 /**
7294  * @brief Called for the completion of set_port_profile for a
7295  *        user request.
7296  *
7297  * @par Description
7298  * This is the second of two callbacks for the set_port_protocol
7299  * function. The set operation is a read-modify-write. This
7300  * callback is called when the write (SET_PROFILE_CONFIG)
7301  * completes.
7302  *
7303  * @param hw Hardware context.
7304  * @param status The status from the MQE.
7305  * @param mqe Pointer to mailbox command buffer.
7306  * @param arg Pointer to a callback argument.
7307  *
7308  * @return 0 on success, non-zero otherwise
7309  */
7310 static int32_t
ocs_hw_set_port_protocol_cb2(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7311 ocs_hw_set_port_protocol_cb2(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7312 {
7313 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7314 
7315 	if (cb_arg->cb) {
7316 		cb_arg->cb( status, cb_arg->arg);
7317 	}
7318 
7319 	ocs_dma_free(hw->os, &(cb_arg->payload));
7320 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7321 	ocs_free(hw->os, arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7322 
7323 	return 0;
7324 }
7325 
7326 /**
7327  * @brief Called for the completion of set_port_profile for a
7328  *        user request.
7329  *
7330  * @par Description
7331  * This is the first of two callbacks for the set_port_protocol
7332  * function.  The set operation is a read-modify-write.  This
7333  * callback is called when the read completes
7334  * (GET_PROFILE_CONFG).  It will updated the resource
7335  * descriptors, then queue the write (SET_PROFILE_CONFIG).
7336  *
7337  * On entry there are three memory areas that were allocated by
7338  * ocs_hw_set_port_protocol.  If a failure is detected in this
7339  * function those need to be freed.  If this function succeeds
7340  * it allocates three more areas.
7341  *
7342  * @param hw Hardware context.
7343  * @param status The status from the MQE
7344  * @param mqe Pointer to mailbox command buffer.
7345  * @param arg Pointer to a callback argument.
7346  *
7347  * @return Returns 0 on success, or a non-zero value otherwise.
7348  */
7349 static int32_t
ocs_hw_set_port_protocol_cb1(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7350 ocs_hw_set_port_protocol_cb1(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7351 {
7352 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7353 	ocs_dma_t *payload = &(cb_arg->payload);
7354 	sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7355 	int num_descriptors;
7356 	sli4_resource_descriptor_v1_t *desc_p;
7357 	sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7358 	int i;
7359 	ocs_hw_set_port_protocol_cb_arg_t *new_cb_arg;
7360 	ocs_hw_port_protocol_e new_protocol;
7361 	uint8_t *dst;
7362 	sli4_isap_resouce_descriptor_v1_t *isap_desc_p;
7363 	uint8_t *mbxdata;
7364 	int pci_descriptor_count;
7365 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7366 	int num_fcoe_ports = 0;
7367 	int num_iscsi_ports = 0;
7368 
7369 	new_protocol = (ocs_hw_port_protocol_e)cb_arg->new_protocol;
7370 
7371 	num_descriptors = response->desc_count;
7372 
7373 	/* Count PCI descriptors */
7374 	pci_descriptor_count = 0;
7375 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7376 	for (i=0; i<num_descriptors; i++) {
7377 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7378 			++pci_descriptor_count;
7379 		}
7380 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7381 	}
7382 
7383 	/* mbxdata holds the header of the command */
7384 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7385 	if (mbxdata == NULL) {
7386 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7387 		return OCS_HW_RTN_NO_MEMORY;
7388 	}
7389 
7390 
7391 	/* cb_arg holds the data that will be passed to the callback on completion */
7392 	new_cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7393 	if (new_cb_arg == NULL) {
7394 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7395 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7396 		return OCS_HW_RTN_NO_MEMORY;
7397 	}
7398 
7399 	new_cb_arg->cb = cb_arg->cb;
7400 	new_cb_arg->arg = cb_arg->arg;
7401 
7402 	/* Allocate memory for the descriptors we're going to send.  This is
7403 	 * one for each PCI descriptor plus one ISAP descriptor. */
7404 	if (ocs_dma_alloc(hw->os, &new_cb_arg->payload, sizeof(sli4_req_common_set_profile_config_t) +
7405 			  (pci_descriptor_count * sizeof(sli4_pcie_resource_descriptor_v1_t)) +
7406 			  sizeof(sli4_isap_resouce_descriptor_v1_t), 4)) {
7407 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7408 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7409 		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7410 		return OCS_HW_RTN_NO_MEMORY;
7411 	}
7412 
7413 	sli_cmd_common_set_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7414 						   &new_cb_arg->payload,
7415 						   0, pci_descriptor_count+1, 1);
7416 
7417 	/* Point dst to the first descriptor entry in the SET_PROFILE_CONFIG command */
7418 	dst = (uint8_t *)&(((sli4_req_common_set_profile_config_t *) new_cb_arg->payload.virt)->desc);
7419 
7420 	/* Loop over all descriptors.  If the descriptor is a PCIe descriptor, copy it
7421 	 * to the SET_PROFILE_CONFIG command to be written back.  If it's the descriptor
7422 	 * that we're trying to change also set its pf_type.
7423 	 */
7424 	desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7425 	for (i=0; i<num_descriptors; i++) {
7426 		if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7427 			pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7428 			if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7429 				/* This is the PCIe descriptor for this OCS instance.
7430 				 * Update it with the new pf_type */
7431 				switch(new_protocol) {
7432 				case OCS_HW_PORT_PROTOCOL_FC:
7433 					pcie_desc_p->pf_type = SLI4_PROTOCOL_FC;
7434 					break;
7435 				case OCS_HW_PORT_PROTOCOL_FCOE:
7436 					pcie_desc_p->pf_type = SLI4_PROTOCOL_FCOE;
7437 					break;
7438 				case OCS_HW_PORT_PROTOCOL_ISCSI:
7439 					pcie_desc_p->pf_type = SLI4_PROTOCOL_ISCSI;
7440 					break;
7441 				default:
7442 					pcie_desc_p->pf_type = SLI4_PROTOCOL_DEFAULT;
7443 					break;
7444 				}
7445 
7446 			}
7447 
7448 			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_FCOE) {
7449 				++num_fcoe_ports;
7450 			}
7451 			if (pcie_desc_p->pf_type == SLI4_PROTOCOL_ISCSI) {
7452 				++num_iscsi_ports;
7453 			}
7454 			ocs_memcpy(dst, pcie_desc_p, sizeof(sli4_pcie_resource_descriptor_v1_t));
7455 			dst += sizeof(sli4_pcie_resource_descriptor_v1_t);
7456 		}
7457 
7458 		desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7459 	}
7460 
7461 	/* Create an ISAP resource descriptor */
7462 	isap_desc_p = (sli4_isap_resouce_descriptor_v1_t*)dst;
7463 	isap_desc_p->descriptor_type = SLI4_RESOURCE_DESCRIPTOR_TYPE_ISAP;
7464 	isap_desc_p->descriptor_length = sizeof(sli4_isap_resouce_descriptor_v1_t);
7465 	if (num_iscsi_ports > 0) {
7466 		isap_desc_p->iscsi_tgt = 1;
7467 		isap_desc_p->iscsi_ini = 1;
7468 		isap_desc_p->iscsi_dif = 1;
7469 	}
7470 	if (num_fcoe_ports > 0) {
7471 		isap_desc_p->fcoe_tgt = 1;
7472 		isap_desc_p->fcoe_ini = 1;
7473 		isap_desc_p->fcoe_dif = 1;
7474 	}
7475 
7476 	/* At this point we're done with the memory allocated by ocs_port_set_protocol */
7477 	ocs_dma_free(hw->os, &cb_arg->payload);
7478 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7479 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7480 
7481 
7482 	/* Send a SET_PROFILE_CONFIG mailbox command with the new descriptors */
7483 	rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb2, new_cb_arg);
7484 	if (rc) {
7485 		ocs_log_err(hw->os, "Error posting COMMON_SET_PROFILE_CONFIG\n");
7486 		/* Call the upper level callback to report a failure */
7487 		if (new_cb_arg->cb) {
7488 			new_cb_arg->cb( rc, new_cb_arg->arg);
7489 		}
7490 
7491 		/* Free the memory allocated by this function */
7492 		ocs_dma_free(hw->os, &new_cb_arg->payload);
7493 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7494 		ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7495 	}
7496 
7497 
7498 	return rc;
7499 }
7500 
7501 /**
7502  * @ingroup io
7503  * @brief  Set the port protocol.
7504  * @par Description
7505  * Setting the port protocol is a read-modify-write operation.
7506  * This function submits a GET_PROFILE_CONFIG command to read
7507  * the current settings.  The callback function will modify the
7508  * settings and issue the write.
7509  *
7510  * On successful completion this function will have allocated
7511  * two regular memory areas and one dma area which will need to
7512  * get freed later in the callbacks.
7513  *
7514  * @param hw Hardware context.
7515  * @param new_protocol New protocol to use.
7516  * @param pci_func PCI function to configure.
7517  * @param cb Callback function to be called when the command completes.
7518  * @param ul_arg An argument that is passed to the callback function.
7519  *
7520  * @return
7521  * - OCS_HW_RTN_SUCCESS on success.
7522  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7523  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7524  *   context.
7525  * - OCS_HW_RTN_ERROR on any other error.
7526  */
7527 ocs_hw_rtn_e
ocs_hw_set_port_protocol(ocs_hw_t * hw,ocs_hw_port_protocol_e new_protocol,uint32_t pci_func,ocs_set_port_protocol_cb_t cb,void * ul_arg)7528 ocs_hw_set_port_protocol(ocs_hw_t *hw, ocs_hw_port_protocol_e new_protocol,
7529 		uint32_t pci_func, ocs_set_port_protocol_cb_t cb, void *ul_arg)
7530 {
7531 	uint8_t *mbxdata;
7532 	ocs_hw_set_port_protocol_cb_arg_t *cb_arg;
7533 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7534 
7535 	/* Only supported on Skyhawk */
7536 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7537 		return OCS_HW_RTN_ERROR;
7538 	}
7539 
7540 	/* mbxdata holds the header of the command */
7541 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7542 	if (mbxdata == NULL) {
7543 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7544 		return OCS_HW_RTN_NO_MEMORY;
7545 	}
7546 
7547 
7548 	/* cb_arg holds the data that will be passed to the callback on completion */
7549 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7550 	if (cb_arg == NULL) {
7551 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7552 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7553 		return OCS_HW_RTN_NO_MEMORY;
7554 	}
7555 
7556 	cb_arg->cb = cb;
7557 	cb_arg->arg = ul_arg;
7558 	cb_arg->new_protocol = new_protocol;
7559 	cb_arg->pci_func = pci_func;
7560 
7561 	/* dma_mem holds the non-embedded portion */
7562 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7563 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7564 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7565 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7566 		return OCS_HW_RTN_NO_MEMORY;
7567 	}
7568 
7569 	if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7570 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb1, cb_arg);
7571 	}
7572 
7573 	if (rc != OCS_HW_RTN_SUCCESS) {
7574 		ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7575 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7576 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7577 		ocs_dma_free(hw->os, &cb_arg->payload);
7578 	}
7579 
7580 	return rc;
7581 }
7582 
7583 typedef struct ocs_hw_get_profile_list_cb_arg_s {
7584 	ocs_get_profile_list_cb_t cb;
7585 	void *arg;
7586 	ocs_dma_t payload;
7587 } ocs_hw_get_profile_list_cb_arg_t;
7588 
7589 /**
7590  * @brief Called for the completion of get_profile_list for a
7591  *        user request.
7592  * @par Description
7593  * This function is called when the COMMMON_GET_PROFILE_LIST
7594  * mailbox completes.  The response will be in
7595  * ctx->non_embedded_mem.virt.  This function parses the
7596  * response and creates a ocs_hw_profile_list, then calls the
7597  * mgmt_cb callback function and passes that list to it.
7598  *
7599  * @param hw Hardware context.
7600  * @param status The status from the MQE
7601  * @param mqe Pointer to mailbox command buffer.
7602  * @param arg Pointer to a callback argument.
7603  *
7604  * @return Returns 0 on success, or a non-zero value on failure.
7605  */
7606 static int32_t
ocs_hw_get_profile_list_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7607 ocs_hw_get_profile_list_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7608 {
7609 	ocs_hw_profile_list_t *list;
7610 	ocs_hw_get_profile_list_cb_arg_t *cb_arg = arg;
7611 	ocs_dma_t *payload = &(cb_arg->payload);
7612 	sli4_res_common_get_profile_list_t *response = (sli4_res_common_get_profile_list_t *)payload->virt;
7613 	int i;
7614 	int num_descriptors;
7615 
7616 	list = ocs_malloc(hw->os, sizeof(ocs_hw_profile_list_t), OCS_M_ZERO);
7617 	if (list == NULL) {
7618 		ocs_log_err(hw->os, "failed to malloc list\n");
7619 		return OCS_HW_RTN_NO_MEMORY;
7620 	}
7621 
7622 	list->num_descriptors = response->profile_descriptor_count;
7623 
7624 	num_descriptors = list->num_descriptors;
7625 	if (num_descriptors > OCS_HW_MAX_PROFILES) {
7626 		num_descriptors = OCS_HW_MAX_PROFILES;
7627 	}
7628 
7629 	for (i=0; i<num_descriptors; i++) {
7630 		list->descriptors[i].profile_id = response->profile_descriptor[i].profile_id;
7631 		list->descriptors[i].profile_index = response->profile_descriptor[i].profile_index;
7632 		ocs_strcpy(list->descriptors[i].profile_description, (char *)response->profile_descriptor[i].profile_description);
7633 	}
7634 
7635 	if (cb_arg->cb) {
7636 		cb_arg->cb(status, list, cb_arg->arg);
7637 	} else {
7638 		ocs_free(hw->os, list, sizeof(*list));
7639 	}
7640 
7641 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7642 	ocs_dma_free(hw->os, &cb_arg->payload);
7643 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7644 
7645 	return 0;
7646 }
7647 
7648 /**
7649  * @ingroup io
7650  * @brief  Get a list of available profiles.
7651  * @par Description
7652  * Issues a SLI-4 COMMON_GET_PROFILE_LIST mailbox.  When the
7653  * command completes the provided mgmt callback function is
7654  * called.
7655  *
7656  * @param hw Hardware context.
7657  * @param cb Callback function to be called when the
7658  *      	  command completes.
7659  * @param ul_arg An argument that is passed to the callback
7660  *      	 function.
7661  *
7662  * @return
7663  * - OCS_HW_RTN_SUCCESS on success.
7664  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7665  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7666  *   context.
7667  * - OCS_HW_RTN_ERROR on any other error.
7668  */
7669 ocs_hw_rtn_e
ocs_hw_get_profile_list(ocs_hw_t * hw,ocs_get_profile_list_cb_t cb,void * ul_arg)7670 ocs_hw_get_profile_list(ocs_hw_t *hw, ocs_get_profile_list_cb_t cb, void* ul_arg)
7671 {
7672 	uint8_t *mbxdata;
7673 	ocs_hw_get_profile_list_cb_arg_t *cb_arg;
7674 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7675 
7676 	/* Only supported on Skyhawk */
7677 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7678 		return OCS_HW_RTN_ERROR;
7679 	}
7680 
7681 	/* mbxdata holds the header of the command */
7682 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7683 	if (mbxdata == NULL) {
7684 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7685 		return OCS_HW_RTN_NO_MEMORY;
7686 	}
7687 
7688 
7689 	/* cb_arg holds the data that will be passed to the callback on completion */
7690 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_profile_list_cb_arg_t), OCS_M_NOWAIT);
7691 	if (cb_arg == NULL) {
7692 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7693 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7694 		return OCS_HW_RTN_NO_MEMORY;
7695 	}
7696 
7697 	cb_arg->cb = cb;
7698 	cb_arg->arg = ul_arg;
7699 
7700 	/* dma_mem holds the non-embedded portion */
7701 	if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_get_profile_list_t), 4)) {
7702 		ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7703 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7704 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7705 		return OCS_HW_RTN_NO_MEMORY;
7706 	}
7707 
7708 	if (sli_cmd_common_get_profile_list(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, &cb_arg->payload)) {
7709 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_profile_list_cb, cb_arg);
7710 	}
7711 
7712 	if (rc != OCS_HW_RTN_SUCCESS) {
7713 		ocs_log_test(hw->os, "GET_PROFILE_LIST failed\n");
7714 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7715 		ocs_dma_free(hw->os, &cb_arg->payload);
7716 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7717 	}
7718 
7719 	return rc;
7720 }
7721 
7722 typedef struct ocs_hw_get_active_profile_cb_arg_s {
7723 	ocs_get_active_profile_cb_t cb;
7724 	void *arg;
7725 } ocs_hw_get_active_profile_cb_arg_t;
7726 
7727 /**
7728  * @brief Called for the completion of get_active_profile for a
7729  *        user request.
7730  *
7731  * @param hw Hardware context.
7732  * @param status The status from the MQE
7733  * @param mqe Pointer to mailbox command buffer.
7734  * @param arg Pointer to a callback argument.
7735  *
7736  * @return Returns 0 on success, or a non-zero value on failure.
7737  */
7738 static int32_t
ocs_hw_get_active_profile_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7739 ocs_hw_get_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7740 {
7741 	ocs_hw_get_active_profile_cb_arg_t *cb_arg = arg;
7742 	sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7743 	sli4_res_common_get_active_profile_t* response = (sli4_res_common_get_active_profile_t*) mbox_rsp->payload.embed;
7744 	uint32_t active_profile;
7745 
7746 	active_profile = response->active_profile_id;
7747 
7748 	if (cb_arg->cb) {
7749 		cb_arg->cb(status, active_profile, cb_arg->arg);
7750 	}
7751 
7752 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7753 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7754 
7755 	return 0;
7756 }
7757 
7758 /**
7759  * @ingroup io
7760  * @brief  Get the currently active profile.
7761  * @par Description
7762  * Issues a SLI-4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
7763  * command completes the provided mgmt callback function is
7764  * called.
7765  *
7766  * @param hw Hardware context.
7767  * @param cb Callback function to be called when the
7768  *	     command completes.
7769  * @param ul_arg An argument that is passed to the callback
7770  *      	 function.
7771  *
7772  * @return
7773  * - OCS_HW_RTN_SUCCESS on success.
7774  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7775  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7776  *   context.
7777  * - OCS_HW_RTN_ERROR on any other error.
7778  */
7779 int32_t
ocs_hw_get_active_profile(ocs_hw_t * hw,ocs_get_active_profile_cb_t cb,void * ul_arg)7780 ocs_hw_get_active_profile(ocs_hw_t *hw, ocs_get_active_profile_cb_t cb, void* ul_arg)
7781 {
7782 	uint8_t *mbxdata;
7783 	ocs_hw_get_active_profile_cb_arg_t *cb_arg;
7784 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7785 
7786 	/* Only supported on Skyhawk */
7787 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7788 		return OCS_HW_RTN_ERROR;
7789 	}
7790 
7791 	/* mbxdata holds the header of the command */
7792 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7793 	if (mbxdata == NULL) {
7794 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7795 		return OCS_HW_RTN_NO_MEMORY;
7796 	}
7797 
7798 	/* cb_arg holds the data that will be passed to the callback on completion */
7799 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_active_profile_cb_arg_t), OCS_M_NOWAIT);
7800 	if (cb_arg == NULL) {
7801 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7802 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7803 		return OCS_HW_RTN_NO_MEMORY;
7804 	}
7805 
7806 	cb_arg->cb = cb;
7807 	cb_arg->arg = ul_arg;
7808 
7809 	if (sli_cmd_common_get_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7810 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_active_profile_cb, cb_arg);
7811 	}
7812 
7813 	if (rc != OCS_HW_RTN_SUCCESS) {
7814 		ocs_log_test(hw->os, "GET_ACTIVE_PROFILE failed\n");
7815 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7816 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7817 	}
7818 
7819 	return rc;
7820 }
7821 
7822 typedef struct ocs_hw_get_nvparms_cb_arg_s {
7823 	ocs_get_nvparms_cb_t cb;
7824 	void *arg;
7825 } ocs_hw_get_nvparms_cb_arg_t;
7826 
7827 /**
7828  * @brief Called for the completion of get_nvparms for a
7829  *        user request.
7830  *
7831  * @param hw Hardware context.
7832  * @param status The status from the MQE.
7833  * @param mqe Pointer to mailbox command buffer.
7834  * @param arg Pointer to a callback argument.
7835  *
7836  * @return 0 on success, non-zero otherwise
7837  */
7838 static int32_t
ocs_hw_get_nvparms_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7839 ocs_hw_get_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7840 {
7841 	ocs_hw_get_nvparms_cb_arg_t *cb_arg = arg;
7842 	sli4_cmd_read_nvparms_t* mbox_rsp = (sli4_cmd_read_nvparms_t*) mqe;
7843 
7844 	if (cb_arg->cb) {
7845 		cb_arg->cb(status, mbox_rsp->wwpn, mbox_rsp->wwnn, mbox_rsp->hard_alpa,
7846 				mbox_rsp->preferred_d_id, cb_arg->arg);
7847 	}
7848 
7849 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7850 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7851 
7852 	return 0;
7853 }
7854 
7855 /**
7856  * @ingroup io
7857  * @brief  Read non-volatile parms.
7858  * @par Description
7859  * Issues a SLI-4 READ_NVPARMS mailbox. When the
7860  * command completes the provided mgmt callback function is
7861  * called.
7862  *
7863  * @param hw Hardware context.
7864  * @param cb Callback function to be called when the
7865  *	  command completes.
7866  * @param ul_arg An argument that is passed to the callback
7867  *	  function.
7868  *
7869  * @return
7870  * - OCS_HW_RTN_SUCCESS on success.
7871  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7872  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7873  *   context.
7874  * - OCS_HW_RTN_ERROR on any other error.
7875  */
7876 int32_t
ocs_hw_get_nvparms(ocs_hw_t * hw,ocs_get_nvparms_cb_t cb,void * ul_arg)7877 ocs_hw_get_nvparms(ocs_hw_t *hw, ocs_get_nvparms_cb_t cb, void* ul_arg)
7878 {
7879 	uint8_t *mbxdata;
7880 	ocs_hw_get_nvparms_cb_arg_t *cb_arg;
7881 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7882 
7883 	/* mbxdata holds the header of the command */
7884 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7885 	if (mbxdata == NULL) {
7886 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7887 		return OCS_HW_RTN_NO_MEMORY;
7888 	}
7889 
7890 	/* cb_arg holds the data that will be passed to the callback on completion */
7891 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_nvparms_cb_arg_t), OCS_M_NOWAIT);
7892 	if (cb_arg == NULL) {
7893 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7894 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7895 		return OCS_HW_RTN_NO_MEMORY;
7896 	}
7897 
7898 	cb_arg->cb = cb;
7899 	cb_arg->arg = ul_arg;
7900 
7901 	if (sli_cmd_read_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7902 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_nvparms_cb, cb_arg);
7903 	}
7904 
7905 	if (rc != OCS_HW_RTN_SUCCESS) {
7906 		ocs_log_test(hw->os, "READ_NVPARMS failed\n");
7907 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7908 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7909 	}
7910 
7911 	return rc;
7912 }
7913 
7914 typedef struct ocs_hw_set_nvparms_cb_arg_s {
7915 	ocs_set_nvparms_cb_t cb;
7916 	void *arg;
7917 } ocs_hw_set_nvparms_cb_arg_t;
7918 
7919 /**
7920  * @brief Called for the completion of set_nvparms for a
7921  *        user request.
7922  *
7923  * @param hw Hardware context.
7924  * @param status The status from the MQE.
7925  * @param mqe Pointer to mailbox command buffer.
7926  * @param arg Pointer to a callback argument.
7927  *
7928  * @return Returns 0 on success, or a non-zero value on failure.
7929  */
7930 static int32_t
ocs_hw_set_nvparms_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7931 ocs_hw_set_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7932 {
7933 	ocs_hw_set_nvparms_cb_arg_t *cb_arg = arg;
7934 
7935 	if (cb_arg->cb) {
7936 		cb_arg->cb(status, cb_arg->arg);
7937 	}
7938 
7939 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7940 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7941 
7942 	return 0;
7943 }
7944 
7945 /**
7946  * @ingroup io
7947  * @brief  Write non-volatile parms.
7948  * @par Description
7949  * Issues a SLI-4 WRITE_NVPARMS mailbox. When the
7950  * command completes the provided mgmt callback function is
7951  * called.
7952  *
7953  * @param hw Hardware context.
7954  * @param cb Callback function to be called when the
7955  *	  command completes.
7956  * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
7957  * @param wwnn Port's WWNN in big-endian order, or NULL to use default.
7958  * @param hard_alpa A hard AL_PA address setting used during loop
7959  * initialization. If no hard AL_PA is required, set to 0.
7960  * @param preferred_d_id A preferred D_ID address setting
7961  * that may be overridden with the CONFIG_LINK mailbox command.
7962  * If there is no preference, set to 0.
7963  * @param ul_arg An argument that is passed to the callback
7964  *	  function.
7965  *
7966  * @return
7967  * - OCS_HW_RTN_SUCCESS on success.
7968  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7969  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7970  *   context.
7971  * - OCS_HW_RTN_ERROR on any other error.
7972  */
7973 int32_t
ocs_hw_set_nvparms(ocs_hw_t * hw,ocs_set_nvparms_cb_t cb,uint8_t * wwpn,uint8_t * wwnn,uint8_t hard_alpa,uint32_t preferred_d_id,void * ul_arg)7974 ocs_hw_set_nvparms(ocs_hw_t *hw, ocs_set_nvparms_cb_t cb, uint8_t *wwpn,
7975 		uint8_t *wwnn, uint8_t hard_alpa, uint32_t preferred_d_id, void* ul_arg)
7976 {
7977 	uint8_t *mbxdata;
7978 	ocs_hw_set_nvparms_cb_arg_t *cb_arg;
7979 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7980 
7981 	/* mbxdata holds the header of the command */
7982 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7983 	if (mbxdata == NULL) {
7984 		ocs_log_err(hw->os, "failed to malloc mbox\n");
7985 		return OCS_HW_RTN_NO_MEMORY;
7986 	}
7987 
7988 	/* cb_arg holds the data that will be passed to the callback on completion */
7989 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_nvparms_cb_arg_t), OCS_M_NOWAIT);
7990 	if (cb_arg == NULL) {
7991 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7992 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7993 		return OCS_HW_RTN_NO_MEMORY;
7994 	}
7995 
7996 	cb_arg->cb = cb;
7997 	cb_arg->arg = ul_arg;
7998 
7999 	if (sli_cmd_write_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE, wwpn, wwnn, hard_alpa, preferred_d_id)) {
8000 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_nvparms_cb, cb_arg);
8001 	}
8002 
8003 	if (rc != OCS_HW_RTN_SUCCESS) {
8004 		ocs_log_test(hw->os, "SET_NVPARMS failed\n");
8005 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8006 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
8007 	}
8008 
8009 	return rc;
8010 }
8011 
8012 
8013 
8014 /**
8015  * @brief Called to obtain the count for the specified type.
8016  *
8017  * @param hw Hardware context.
8018  * @param io_count_type IO count type (inuse, free, wait_free).
8019  *
8020  * @return Returns the number of IOs on the specified list type.
8021  */
8022 uint32_t
ocs_hw_io_get_count(ocs_hw_t * hw,ocs_hw_io_count_type_e io_count_type)8023 ocs_hw_io_get_count(ocs_hw_t *hw, ocs_hw_io_count_type_e io_count_type)
8024 {
8025 	ocs_hw_io_t *io = NULL;
8026 	uint32_t count = 0;
8027 
8028 	ocs_lock(&hw->io_lock);
8029 
8030 	switch (io_count_type) {
8031 	case OCS_HW_IO_INUSE_COUNT :
8032 		ocs_list_foreach(&hw->io_inuse, io) {
8033 			count++;
8034 		}
8035 		break;
8036 	case OCS_HW_IO_FREE_COUNT :
8037 		 ocs_list_foreach(&hw->io_free, io) {
8038 			 count++;
8039 		 }
8040 		 break;
8041 	case OCS_HW_IO_WAIT_FREE_COUNT :
8042 		 ocs_list_foreach(&hw->io_wait_free, io) {
8043 			 count++;
8044 		 }
8045 		 break;
8046 	case OCS_HW_IO_PORT_OWNED_COUNT:
8047 		 ocs_list_foreach(&hw->io_port_owned, io) {
8048 			 count++;
8049 		 }
8050 		 break;
8051 	case OCS_HW_IO_N_TOTAL_IO_COUNT :
8052 		count = hw->config.n_io;
8053 		break;
8054 	}
8055 
8056 	ocs_unlock(&hw->io_lock);
8057 
8058 	return count;
8059 }
8060 
8061 /**
8062  * @brief Called to obtain the count of produced RQs.
8063  *
8064  * @param hw Hardware context.
8065  *
8066  * @return Returns the number of RQs produced.
8067  */
8068 uint32_t
ocs_hw_get_rqes_produced_count(ocs_hw_t * hw)8069 ocs_hw_get_rqes_produced_count(ocs_hw_t *hw)
8070 {
8071 	uint32_t count = 0;
8072 	uint32_t i;
8073 	uint32_t j;
8074 
8075 	for (i = 0; i < hw->hw_rq_count; i++) {
8076 		hw_rq_t *rq = hw->hw_rq[i];
8077 		if (rq->rq_tracker != NULL) {
8078 			for (j = 0; j < rq->entry_count; j++) {
8079 				if (rq->rq_tracker[j] != NULL) {
8080 					count++;
8081 				}
8082 			}
8083 		}
8084 	}
8085 
8086 	return count;
8087 }
8088 
8089 typedef struct ocs_hw_set_active_profile_cb_arg_s {
8090 	ocs_set_active_profile_cb_t cb;
8091 	void *arg;
8092 } ocs_hw_set_active_profile_cb_arg_t;
8093 
8094 /**
8095  * @brief Called for the completion of set_active_profile for a
8096  *        user request.
8097  *
8098  * @param hw Hardware context.
8099  * @param status The status from the MQE
8100  * @param mqe Pointer to mailbox command buffer.
8101  * @param arg Pointer to a callback argument.
8102  *
8103  * @return Returns 0 on success, or a non-zero value on failure.
8104  */
8105 static int32_t
ocs_hw_set_active_profile_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)8106 ocs_hw_set_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
8107 {
8108 	ocs_hw_set_active_profile_cb_arg_t *cb_arg = arg;
8109 
8110 	if (cb_arg->cb) {
8111 		cb_arg->cb(status, cb_arg->arg);
8112 	}
8113 
8114 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
8115 	ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
8116 
8117 	return 0;
8118 }
8119 
8120 /**
8121  * @ingroup io
8122  * @brief  Set the currently active profile.
8123  * @par Description
8124  * Issues a SLI4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
8125  * command completes the provided mgmt callback function is
8126  * called.
8127  *
8128  * @param hw Hardware context.
8129  * @param profile_id Profile ID to activate.
8130  * @param cb Callback function to be called when the command completes.
8131  * @param ul_arg An argument that is passed to the callback function.
8132  *
8133  * @return
8134  * - OCS_HW_RTN_SUCCESS on success.
8135  * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
8136  * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
8137  *   context.
8138  * - OCS_HW_RTN_ERROR on any other error.
8139  */
8140 int32_t
ocs_hw_set_active_profile(ocs_hw_t * hw,ocs_set_active_profile_cb_t cb,uint32_t profile_id,void * ul_arg)8141 ocs_hw_set_active_profile(ocs_hw_t *hw, ocs_set_active_profile_cb_t cb, uint32_t profile_id, void* ul_arg)
8142 {
8143 	uint8_t *mbxdata;
8144 	ocs_hw_set_active_profile_cb_arg_t *cb_arg;
8145 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
8146 
8147 	/* Only supported on Skyhawk */
8148 	if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
8149 		return OCS_HW_RTN_ERROR;
8150 	}
8151 
8152 	/* mbxdata holds the header of the command */
8153 	mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
8154 	if (mbxdata == NULL) {
8155 		ocs_log_err(hw->os, "failed to malloc mbox\n");
8156 		return OCS_HW_RTN_NO_MEMORY;
8157 	}
8158 
8159 
8160 	/* cb_arg holds the data that will be passed to the callback on completion */
8161 	cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_active_profile_cb_arg_t), OCS_M_NOWAIT);
8162 	if (cb_arg == NULL) {
8163 		ocs_log_err(hw->os, "failed to malloc cb_arg\n");
8164 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8165 		return OCS_HW_RTN_NO_MEMORY;
8166 	}
8167 
8168 	cb_arg->cb = cb;
8169 	cb_arg->arg = ul_arg;
8170 
8171 	if (sli_cmd_common_set_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, profile_id)) {
8172 		rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_active_profile_cb, cb_arg);
8173 	}
8174 
8175 	if (rc != OCS_HW_RTN_SUCCESS) {
8176 		ocs_log_test(hw->os, "SET_ACTIVE_PROFILE failed\n");
8177 		ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8178 		ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_active_profile_cb_arg_t));
8179 	}
8180 
8181 	return rc;
8182 }
8183 
8184 
8185 
8186 /*
8187  * Private functions
8188  */
8189 
8190 /**
8191  * @brief Update the queue hash with the ID and index.
8192  *
8193  * @param hash Pointer to hash table.
8194  * @param id ID that was created.
8195  * @param index The index into the hash object.
8196  */
8197 static void
ocs_hw_queue_hash_add(ocs_queue_hash_t * hash,uint16_t id,uint16_t index)8198 ocs_hw_queue_hash_add(ocs_queue_hash_t *hash, uint16_t id, uint16_t index)
8199 {
8200 	uint32_t	hash_index = id & (OCS_HW_Q_HASH_SIZE - 1);
8201 
8202 	/*
8203 	 * Since the hash is always bigger than the number of queues, then we
8204 	 * never have to worry about an infinite loop.
8205 	 */
8206 	while(hash[hash_index].in_use) {
8207 		hash_index = (hash_index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8208 	}
8209 
8210 	/* not used, claim the entry */
8211 	hash[hash_index].id = id;
8212 	hash[hash_index].in_use = 1;
8213 	hash[hash_index].index = index;
8214 }
8215 
8216 /**
8217  * @brief Find index given queue ID.
8218  *
8219  * @param hash Pointer to hash table.
8220  * @param id ID to find.
8221  *
8222  * @return Returns the index into the HW cq array or -1 if not found.
8223  */
8224 int32_t
ocs_hw_queue_hash_find(ocs_queue_hash_t * hash,uint16_t id)8225 ocs_hw_queue_hash_find(ocs_queue_hash_t *hash, uint16_t id)
8226 {
8227 	int32_t	rc = -1;
8228 	int32_t	index = id & (OCS_HW_Q_HASH_SIZE - 1);
8229 
8230 	/*
8231 	 * Since the hash is always bigger than the maximum number of Qs, then we
8232 	 * never have to worry about an infinite loop. We will always find an
8233 	 * unused entry.
8234 	 */
8235 	do {
8236 		if (hash[index].in_use &&
8237 		    hash[index].id == id) {
8238 			rc = hash[index].index;
8239 		} else {
8240 			index = (index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8241 		}
8242 	} while(rc == -1 && hash[index].in_use);
8243 
8244 	return rc;
8245 }
8246 
8247 static int32_t
ocs_hw_domain_add(ocs_hw_t * hw,ocs_domain_t * domain)8248 ocs_hw_domain_add(ocs_hw_t *hw, ocs_domain_t *domain)
8249 {
8250 	int32_t		rc = OCS_HW_RTN_ERROR;
8251 	uint16_t	fcfi = UINT16_MAX;
8252 
8253 	if ((hw == NULL) || (domain == NULL)) {
8254 		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8255 				hw, domain);
8256 		return OCS_HW_RTN_ERROR;
8257 	}
8258 
8259 	fcfi = domain->fcf_indicator;
8260 
8261 	if (fcfi < SLI4_MAX_FCFI) {
8262 		uint16_t	fcf_index = UINT16_MAX;
8263 
8264 		ocs_log_debug(hw->os, "adding domain %p @ %#x\n",
8265 				domain, fcfi);
8266 		hw->domains[fcfi] = domain;
8267 
8268 		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8269 		if (hw->workaround.override_fcfi) {
8270 			if (hw->first_domain_idx < 0) {
8271 				hw->first_domain_idx = fcfi;
8272 			}
8273 		}
8274 
8275 		fcf_index = domain->fcf;
8276 
8277 		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8278 			ocs_log_debug(hw->os, "adding map of FCF index %d to FCFI %d\n",
8279 				      fcf_index, fcfi);
8280 			hw->fcf_index_fcfi[fcf_index] = fcfi;
8281 			rc = OCS_HW_RTN_SUCCESS;
8282 		} else {
8283 			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8284 				     fcf_index, SLI4_MAX_FCF_INDEX);
8285 			hw->domains[fcfi] = NULL;
8286 		}
8287 	} else {
8288 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8289 				fcfi, SLI4_MAX_FCFI);
8290 	}
8291 
8292 	return rc;
8293 }
8294 
8295 static int32_t
ocs_hw_domain_del(ocs_hw_t * hw,ocs_domain_t * domain)8296 ocs_hw_domain_del(ocs_hw_t *hw, ocs_domain_t *domain)
8297 {
8298 	int32_t		rc = OCS_HW_RTN_ERROR;
8299 	uint16_t	fcfi = UINT16_MAX;
8300 
8301 	if ((hw == NULL) || (domain == NULL)) {
8302 		ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8303 				hw, domain);
8304 		return OCS_HW_RTN_ERROR;
8305 	}
8306 
8307 	fcfi = domain->fcf_indicator;
8308 
8309 	if (fcfi < SLI4_MAX_FCFI) {
8310 		uint16_t	fcf_index = UINT16_MAX;
8311 
8312 		ocs_log_debug(hw->os, "deleting domain %p @ %#x\n",
8313 				domain, fcfi);
8314 
8315 		if (domain != hw->domains[fcfi]) {
8316 			ocs_log_test(hw->os, "provided domain %p does not match stored domain %p\n",
8317 				     domain, hw->domains[fcfi]);
8318 			return OCS_HW_RTN_ERROR;
8319 		}
8320 
8321 		hw->domains[fcfi] = NULL;
8322 
8323 		/* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8324 		if (hw->workaround.override_fcfi) {
8325 			if (hw->first_domain_idx == fcfi) {
8326 				hw->first_domain_idx = -1;
8327 			}
8328 		}
8329 
8330 		fcf_index = domain->fcf;
8331 
8332 		if (fcf_index < SLI4_MAX_FCF_INDEX) {
8333 			if (hw->fcf_index_fcfi[fcf_index] == fcfi) {
8334 				hw->fcf_index_fcfi[fcf_index] = 0;
8335 				rc = OCS_HW_RTN_SUCCESS;
8336 			} else {
8337 				ocs_log_test(hw->os, "indexed FCFI %#x doesn't match provided %#x @ %d\n",
8338 					     hw->fcf_index_fcfi[fcf_index], fcfi, fcf_index);
8339 			}
8340 		} else {
8341 			ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8342 				     fcf_index, SLI4_MAX_FCF_INDEX);
8343 		}
8344 	} else {
8345 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8346 				fcfi, SLI4_MAX_FCFI);
8347 	}
8348 
8349 	return rc;
8350 }
8351 
8352 ocs_domain_t *
ocs_hw_domain_get(ocs_hw_t * hw,uint16_t fcfi)8353 ocs_hw_domain_get(ocs_hw_t *hw, uint16_t fcfi)
8354 {
8355 
8356 	if (hw == NULL) {
8357 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8358 		return NULL;
8359 	}
8360 
8361 	if (fcfi < SLI4_MAX_FCFI) {
8362 		return hw->domains[fcfi];
8363 	} else {
8364 		ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8365 				fcfi, SLI4_MAX_FCFI);
8366 		return NULL;
8367 	}
8368 }
8369 
8370 static ocs_domain_t *
ocs_hw_domain_get_indexed(ocs_hw_t * hw,uint16_t fcf_index)8371 ocs_hw_domain_get_indexed(ocs_hw_t *hw, uint16_t fcf_index)
8372 {
8373 
8374 	if (hw == NULL) {
8375 		ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8376 		return NULL;
8377 	}
8378 
8379 	if (fcf_index < SLI4_MAX_FCF_INDEX) {
8380 		return ocs_hw_domain_get(hw, hw->fcf_index_fcfi[fcf_index]);
8381 	} else {
8382 		ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8383 			     fcf_index, SLI4_MAX_FCF_INDEX);
8384 		return NULL;
8385 	}
8386 }
8387 
8388 /**
8389  * @brief Quaratine an IO by taking a reference count and adding it to the
8390  *        quarantine list. When the IO is popped from the list then the
8391  *        count is released and the IO MAY be freed depending on whether
8392  *        it is still referenced by the IO.
8393  *
8394  *        @n @b Note: BZ 160124 - If this is a target write or an initiator read using
8395  *        DIF, then we must add the XRI to a quarantine list until we receive
8396  *        4 more completions of this same type.
8397  *
8398  * @param hw Hardware context.
8399  * @param wq Pointer to the WQ associated with the IO object to quarantine.
8400  * @param io Pointer to the io object to quarantine.
8401  */
8402 static void
ocs_hw_io_quarantine(ocs_hw_t * hw,hw_wq_t * wq,ocs_hw_io_t * io)8403 ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io)
8404 {
8405 	ocs_quarantine_info_t *q_info = &wq->quarantine_info;
8406 	uint32_t	index;
8407 	ocs_hw_io_t	*free_io = NULL;
8408 
8409 	/* return if the QX bit was clear */
8410 	if (!io->quarantine) {
8411 		return;
8412 	}
8413 
8414 	/* increment the IO refcount to prevent it from being freed before the quarantine is over */
8415 	if (ocs_ref_get_unless_zero(&io->ref) == 0) {
8416 		/* command no longer active */
8417 		ocs_log_debug(hw ? hw->os : NULL,
8418 			      "io not active xri=0x%x tag=0x%x\n",
8419 			      io->indicator, io->reqtag);
8420 		return;
8421 	}
8422 
8423 	sli_queue_lock(wq->queue);
8424 		index = q_info->quarantine_index;
8425 		free_io = q_info->quarantine_ios[index];
8426 		q_info->quarantine_ios[index] = io;
8427 		q_info->quarantine_index = (index + 1) % OCS_HW_QUARANTINE_QUEUE_DEPTH;
8428 	sli_queue_unlock(wq->queue);
8429 
8430 	if (free_io != NULL) {
8431 		ocs_ref_put(&free_io->ref); /* ocs_ref_get(): same function */
8432 	}
8433 }
8434 
8435 /**
8436  * @brief Process entries on the given completion queue.
8437  *
8438  * @param hw Hardware context.
8439  * @param cq Pointer to the HW completion queue object.
8440  *
8441  * @return None.
8442  */
8443 void
ocs_hw_cq_process(ocs_hw_t * hw,hw_cq_t * cq)8444 ocs_hw_cq_process(ocs_hw_t *hw, hw_cq_t *cq)
8445 {
8446 	uint8_t		cqe[sizeof(sli4_mcqe_t)];
8447 	uint16_t	rid = UINT16_MAX;
8448 	sli4_qentry_e	ctype;		/* completion type */
8449 	int32_t		status;
8450 	uint32_t	n_processed = 0;
8451 	time_t		tstart;
8452 	time_t		telapsed;
8453 
8454 	tstart = ocs_msectime();
8455 
8456 	while (!sli_queue_read(&hw->sli, cq->queue, cqe)) {
8457 		status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
8458 		/*
8459 		 * The sign of status is significant. If status is:
8460 		 * == 0 : call completed correctly and the CQE indicated success
8461 		 *  > 0 : call completed correctly and the CQE indicated an error
8462 		 *  < 0 : call failed and no information is available about the CQE
8463 		 */
8464 		if (status < 0) {
8465 			if (status == -2) {
8466 				/* Notification that an entry was consumed, but not completed */
8467 				continue;
8468 			}
8469 
8470 			break;
8471 		}
8472 
8473 		switch (ctype) {
8474 		case SLI_QENTRY_ASYNC:
8475 			CPUTRACE("async");
8476 			sli_cqe_async(&hw->sli, cqe);
8477 			break;
8478 		case SLI_QENTRY_MQ:
8479 			/*
8480 			 * Process MQ entry. Note there is no way to determine
8481 			 * the MQ_ID from the completion entry.
8482 			 */
8483 			CPUTRACE("mq");
8484 			ocs_hw_mq_process(hw, status, hw->mq);
8485 			break;
8486 		case SLI_QENTRY_OPT_WRITE_CMD:
8487 			ocs_hw_rqpair_process_auto_xfr_rdy_cmd(hw, cq, cqe);
8488 			break;
8489 		case SLI_QENTRY_OPT_WRITE_DATA:
8490 			ocs_hw_rqpair_process_auto_xfr_rdy_data(hw, cq, cqe);
8491 			break;
8492 		case SLI_QENTRY_WQ:
8493 			CPUTRACE("wq");
8494 			ocs_hw_wq_process(hw, cq, cqe, status, rid);
8495 			break;
8496 		case SLI_QENTRY_WQ_RELEASE: {
8497 			uint32_t wq_id = rid;
8498 			uint32_t index = ocs_hw_queue_hash_find(hw->wq_hash, wq_id);
8499 			hw_wq_t *wq = hw->hw_wq[index];
8500 
8501 			/* Submit any HW IOs that are on the WQ pending list */
8502 			hw_wq_submit_pending(wq, wq->wqec_set_count);
8503 
8504 			break;
8505 		}
8506 
8507 		case SLI_QENTRY_RQ:
8508 			CPUTRACE("rq");
8509 			ocs_hw_rqpair_process_rq(hw, cq, cqe);
8510 			break;
8511 		case SLI_QENTRY_XABT: {
8512 			CPUTRACE("xabt");
8513 			ocs_hw_xabt_process(hw, cq, cqe, rid);
8514 			break;
8515 
8516 		}
8517 		default:
8518 			ocs_log_test(hw->os, "unhandled ctype=%#x rid=%#x\n", ctype, rid);
8519 			break;
8520 		}
8521 
8522 		n_processed++;
8523 		if (n_processed == cq->queue->proc_limit) {
8524 			break;
8525 		}
8526 
8527 		if (cq->queue->n_posted >= (cq->queue->posted_limit)) {
8528 			sli_queue_arm(&hw->sli, cq->queue, FALSE);
8529 		}
8530 	}
8531 
8532 	sli_queue_arm(&hw->sli, cq->queue, TRUE);
8533 
8534 	if (n_processed > cq->queue->max_num_processed) {
8535 		cq->queue->max_num_processed = n_processed;
8536 	}
8537 	telapsed = ocs_msectime() - tstart;
8538 	if (telapsed > cq->queue->max_process_time) {
8539 		cq->queue->max_process_time = telapsed;
8540 	}
8541 }
8542 
8543 /**
8544  * @brief Process WQ completion queue entries.
8545  *
8546  * @param hw Hardware context.
8547  * @param cq Pointer to the HW completion queue object.
8548  * @param cqe Pointer to WQ completion queue.
8549  * @param status Completion status.
8550  * @param rid Resource ID (IO tag).
8551  *
8552  * @return none
8553  */
8554 void
ocs_hw_wq_process(ocs_hw_t * hw,hw_cq_t * cq,uint8_t * cqe,int32_t status,uint16_t rid)8555 ocs_hw_wq_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, int32_t status, uint16_t rid)
8556 {
8557 	hw_wq_callback_t *wqcb;
8558 
8559 	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_WQ, (void *)cqe, ((sli4_fc_wcqe_t *)cqe)->status, cq->queue->id,
8560 			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8561 
8562 	if(rid == OCS_HW_REQUE_XRI_REGTAG) {
8563 		if(status) {
8564 			ocs_log_err(hw->os, "reque xri failed, status = %d \n", status);
8565 		}
8566 		return;
8567 	}
8568 
8569 	wqcb = ocs_hw_reqtag_get_instance(hw, rid);
8570 	if (wqcb == NULL) {
8571 		ocs_log_err(hw->os, "invalid request tag: x%x\n", rid);
8572 		return;
8573 	}
8574 
8575 	if (wqcb->callback == NULL) {
8576 		ocs_log_err(hw->os, "wqcb callback is NULL\n");
8577 		return;
8578 	}
8579 
8580 	(*wqcb->callback)(wqcb->arg, cqe, status);
8581 }
8582 
8583 /**
8584  * @brief Process WQ completions for IO requests
8585  *
8586  * @param arg Generic callback argument
8587  * @param cqe Pointer to completion queue entry
8588  * @param status Completion status
8589  *
8590  * @par Description
8591  * @n @b Note:  Regarding io->reqtag, the reqtag is assigned once when HW IOs are initialized
8592  * in ocs_hw_setup_io(), and don't need to be returned to the hw->wq_reqtag_pool.
8593  *
8594  * @return None.
8595  */
8596 static void
ocs_hw_wq_process_io(void * arg,uint8_t * cqe,int32_t status)8597 ocs_hw_wq_process_io(void *arg, uint8_t *cqe, int32_t status)
8598 {
8599 	ocs_hw_io_t *io = arg;
8600 	ocs_hw_t *hw = io->hw;
8601 	sli4_fc_wcqe_t *wcqe = (void *)cqe;
8602 	uint32_t	len = 0;
8603 	uint32_t ext = 0;
8604 	uint8_t out_of_order_axr_cmd = 0;
8605 	uint8_t out_of_order_axr_data = 0;
8606 	uint8_t lock_taken = 0;
8607 #if defined(OCS_DISC_SPIN_DELAY)
8608 	uint32_t delay = 0;
8609 	char prop_buf[32];
8610 #endif
8611 
8612 	/*
8613 	 * For the primary IO, this will also be used for the
8614 	 * response. So it is important to only set/clear this
8615 	 * flag on the first data phase of the IO because
8616 	 * subsequent phases will be done on the secondary XRI.
8617 	 */
8618 	if (io->quarantine && io->quarantine_first_phase) {
8619 		io->quarantine = (wcqe->qx == 1);
8620 		ocs_hw_io_quarantine(hw, io->wq, io);
8621 	}
8622 	io->quarantine_first_phase = FALSE;
8623 
8624 	/* BZ 161832 - free secondary HW IO */
8625 	if (io->sec_hio != NULL &&
8626 	    io->sec_hio->quarantine) {
8627 		/*
8628 		 * If the quarantine flag is set on the
8629 		 * IO, then set it on the secondary IO
8630 		 * based on the quarantine XRI (QX) bit
8631 		 * sent by the FW.
8632 		 */
8633 		io->sec_hio->quarantine = (wcqe->qx == 1);
8634 		/* use the primary io->wq because it is not set on the secondary IO. */
8635 		ocs_hw_io_quarantine(hw, io->wq, io->sec_hio);
8636 	}
8637 
8638 	ocs_hw_remove_io_timed_wqe(hw, io);
8639 
8640 	/* clear xbusy flag if WCQE[XB] is clear */
8641 	if (io->xbusy && wcqe->xb == 0) {
8642 		io->xbusy = FALSE;
8643 	}
8644 
8645 	/* get extended CQE status */
8646 	switch (io->type) {
8647 	case OCS_HW_BLS_ACC:
8648 	case OCS_HW_BLS_ACC_SID:
8649 		break;
8650 	case OCS_HW_ELS_REQ:
8651 		sli_fc_els_did(&hw->sli, cqe, &ext);
8652 		len = sli_fc_response_length(&hw->sli, cqe);
8653 		break;
8654 	case OCS_HW_ELS_RSP:
8655 	case OCS_HW_ELS_RSP_SID:
8656 	case OCS_HW_FC_CT_RSP:
8657 		break;
8658 	case OCS_HW_FC_CT:
8659 		len = sli_fc_response_length(&hw->sli, cqe);
8660 		break;
8661 	case OCS_HW_IO_TARGET_WRITE:
8662 		len = sli_fc_io_length(&hw->sli, cqe);
8663 #if defined(OCS_DISC_SPIN_DELAY)
8664 		if (ocs_get_property("disk_spin_delay", prop_buf, sizeof(prop_buf)) == 0) {
8665 			delay = ocs_strtoul(prop_buf, 0, 0);
8666 			ocs_udelay(delay);
8667 		}
8668 #endif
8669 		break;
8670 	case OCS_HW_IO_TARGET_READ:
8671 		len = sli_fc_io_length(&hw->sli, cqe);
8672 		/*
8673 		 * if_type == 2 seems to return 0 "total length placed" on
8674 		 * FCP_TSEND64_WQE completions. If this appears to happen,
8675 		 * use the CTIO data transfer length instead.
8676 		 */
8677 		if (hw->workaround.retain_tsend_io_length && !len && !status) {
8678 			len = io->length;
8679 		}
8680 
8681 		break;
8682 	case OCS_HW_IO_TARGET_RSP:
8683 		if(io->is_port_owned) {
8684 			ocs_lock(&io->axr_lock);
8685 			lock_taken = 1;
8686 			if(io->axr_buf->call_axr_cmd) {
8687 				out_of_order_axr_cmd = 1;
8688 			}
8689 			if(io->axr_buf->call_axr_data) {
8690 				out_of_order_axr_data = 1;
8691 			}
8692 		}
8693 		break;
8694 	case OCS_HW_IO_INITIATOR_READ:
8695 		len = sli_fc_io_length(&hw->sli, cqe);
8696 		break;
8697 	case OCS_HW_IO_INITIATOR_WRITE:
8698 		len = sli_fc_io_length(&hw->sli, cqe);
8699 		break;
8700 	case OCS_HW_IO_INITIATOR_NODATA:
8701 		break;
8702 	case OCS_HW_IO_DNRX_REQUEUE:
8703 		/* release the count for re-posting the buffer */
8704 		//ocs_hw_io_free(hw, io);
8705 		break;
8706 	default:
8707 		ocs_log_test(hw->os, "XXX unhandled io type %#x for XRI 0x%x\n",
8708 			     io->type, io->indicator);
8709 		break;
8710 	}
8711 	if (status) {
8712 		ext = sli_fc_ext_status(&hw->sli, cqe);
8713 		/* Emulate IAAB=0 for initiator WQEs only; i.e. automatically
8714 		 * abort exchange if an error occurred and exchange is still busy.
8715 		 */
8716 		if (hw->config.i_only_aab &&
8717 		    (ocs_hw_iotype_is_originator(io->type)) &&
8718 		    (ocs_hw_wcqe_abort_needed(status, ext, wcqe->xb))) {
8719 			ocs_hw_rtn_e rc;
8720 
8721 			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
8722 				      io->indicator, io->reqtag);
8723 			/*
8724 			 * Because the initiator will not issue another IO phase, then it is OK to issue the
8725 			 * callback on the abort completion, but for consistency with the target, wait for the
8726 			 * XRI_ABORTED CQE to issue the IO callback.
8727 			 */
8728 			rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
8729 
8730 			if (rc == OCS_HW_RTN_SUCCESS) {
8731 				/* latch status to return after abort is complete */
8732 				io->status_saved = 1;
8733 				io->saved_status = status;
8734 				io->saved_ext = ext;
8735 				io->saved_len = len;
8736 				goto exit_ocs_hw_wq_process_io;
8737 			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8738 				/*
8739 				 * Already being aborted by someone else (ABTS
8740 				 * perhaps). Just fall through and return original
8741 				 * error.
8742 				 */
8743 				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8744 					      io->indicator, io->reqtag);
8745 
8746 			} else {
8747 				/* Failed to abort for some other reason, log error */
8748 				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8749 					     io->indicator, io->reqtag, rc);
8750 			}
8751 		}
8752 
8753 		/*
8754 		 * If we're not an originator IO, and XB is set, then issue abort for the IO from within the HW
8755 		 */
8756 		if ( (! ocs_hw_iotype_is_originator(io->type)) && wcqe->xb) {
8757 			ocs_hw_rtn_e rc;
8758 
8759 			ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", io->indicator, io->reqtag);
8760 
8761 			/*
8762 			 * Because targets may send a response when the IO completes using the same XRI, we must
8763 			 * wait for the XRI_ABORTED CQE to issue the IO callback
8764 			 */
8765 			rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
8766 			if (rc == OCS_HW_RTN_SUCCESS) {
8767 				/* latch status to return after abort is complete */
8768 				io->status_saved = 1;
8769 				io->saved_status = status;
8770 				io->saved_ext = ext;
8771 				io->saved_len = len;
8772 				goto exit_ocs_hw_wq_process_io;
8773 			} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8774 				/*
8775 				 * Already being aborted by someone else (ABTS
8776 				 * perhaps). Just fall through and return original
8777 				 * error.
8778 				 */
8779 				ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8780 					      io->indicator, io->reqtag);
8781 
8782 			} else {
8783 				/* Failed to abort for some other reason, log error */
8784 				ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8785 					     io->indicator, io->reqtag, rc);
8786 			}
8787 		}
8788 	}
8789 	/* BZ 161832 - free secondary HW IO */
8790 	if (io->sec_hio != NULL) {
8791 		ocs_hw_io_free(hw, io->sec_hio);
8792 		io->sec_hio = NULL;
8793 	}
8794 
8795 	if (io->done != NULL) {
8796 		ocs_hw_done_t  done = io->done;
8797 		void		*arg = io->arg;
8798 
8799 		io->done = NULL;
8800 
8801 		if (io->status_saved) {
8802 			/* use latched status if exists */
8803 			status = io->saved_status;
8804 			len = io->saved_len;
8805 			ext = io->saved_ext;
8806 			io->status_saved = 0;
8807 		}
8808 
8809 		/* Restore default SGL */
8810 		ocs_hw_io_restore_sgl(hw, io);
8811 		done(io, io->rnode, len, status, ext, arg);
8812 	}
8813 
8814 	if(out_of_order_axr_cmd) {
8815 		/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8816 		if (hw->config.bounce) {
8817 			fc_header_t *hdr = io->axr_buf->cmd_seq->header->dma.virt;
8818 			uint32_t s_id = fc_be24toh(hdr->s_id);
8819 			uint32_t d_id = fc_be24toh(hdr->d_id);
8820 			uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8821 			if (hw->callback.bounce != NULL) {
8822 				(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, io->axr_buf->cmd_seq, s_id, d_id, ox_id);
8823 			}
8824 		}else {
8825 			hw->callback.unsolicited(hw->args.unsolicited, io->axr_buf->cmd_seq);
8826 		}
8827 
8828 		if(out_of_order_axr_data) {
8829 			/* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8830 			if (hw->config.bounce) {
8831 				fc_header_t *hdr = io->axr_buf->seq.header->dma.virt;
8832 				uint32_t s_id = fc_be24toh(hdr->s_id);
8833 				uint32_t d_id = fc_be24toh(hdr->d_id);
8834 				uint32_t ox_id =  ocs_be16toh(hdr->ox_id);
8835 				if (hw->callback.bounce != NULL) {
8836 					(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, &io->axr_buf->seq, s_id, d_id, ox_id);
8837 				}
8838 			}else {
8839 				hw->callback.unsolicited(hw->args.unsolicited, &io->axr_buf->seq);
8840 			}
8841 		}
8842 	}
8843 
8844 exit_ocs_hw_wq_process_io:
8845 	if(lock_taken) {
8846 		ocs_unlock(&io->axr_lock);
8847 	}
8848 }
8849 
8850 /**
8851  * @brief Process WQ completions for abort requests.
8852  *
8853  * @param arg Generic callback argument.
8854  * @param cqe Pointer to completion queue entry.
8855  * @param status Completion status.
8856  *
8857  * @return None.
8858  */
8859 static void
ocs_hw_wq_process_abort(void * arg,uint8_t * cqe,int32_t status)8860 ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status)
8861 {
8862 	ocs_hw_io_t *io = arg;
8863 	ocs_hw_t *hw = io->hw;
8864 	uint32_t ext = 0;
8865 	uint32_t len = 0;
8866 	hw_wq_callback_t *wqcb;
8867 
8868 	/*
8869 	 * For IOs that were aborted internally, we may need to issue the callback here depending
8870 	 * on whether a XRI_ABORTED CQE is expected ot not. If the status is Local Reject/No XRI, then
8871 	 * issue the callback now.
8872 	*/
8873 	ext = sli_fc_ext_status(&hw->sli, cqe);
8874 	if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
8875 	    ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
8876 		io->done != NULL) {
8877 		ocs_hw_done_t  done = io->done;
8878 		void		*arg = io->arg;
8879 
8880 		io->done = NULL;
8881 
8882 		/*
8883 		 * Use latched status as this is always saved for an internal abort
8884 		 *
8885 		 * Note: We wont have both a done and abort_done function, so don't worry about
8886 		 *       clobbering the len, status and ext fields.
8887 		 */
8888 		status = io->saved_status;
8889 		len = io->saved_len;
8890 		ext = io->saved_ext;
8891 		io->status_saved = 0;
8892 		done(io, io->rnode, len, status, ext, arg);
8893 	}
8894 
8895 	if (io->abort_done != NULL) {
8896 		ocs_hw_done_t  done = io->abort_done;
8897 		void		*arg = io->abort_arg;
8898 
8899 		io->abort_done = NULL;
8900 
8901 		done(io, io->rnode, len, status, ext, arg);
8902 	}
8903 	ocs_lock(&hw->io_abort_lock);
8904 		/* clear abort bit to indicate abort is complete */
8905 		io->abort_in_progress = 0;
8906 	ocs_unlock(&hw->io_abort_lock);
8907 
8908 	/* Free the WQ callback */
8909 	ocs_hw_assert(io->abort_reqtag != UINT32_MAX);
8910 	wqcb = ocs_hw_reqtag_get_instance(hw, io->abort_reqtag);
8911 	ocs_hw_reqtag_free(hw, wqcb);
8912 
8913 	/*
8914 	 * Call ocs_hw_io_free() because this releases the WQ reservation as
8915 	 * well as doing the refcount put. Don't duplicate the code here.
8916 	 */
8917 	(void)ocs_hw_io_free(hw, io);
8918 }
8919 
8920 /**
8921  * @brief Process XABT completions
8922  *
8923  * @param hw Hardware context.
8924  * @param cq Pointer to the HW completion queue object.
8925  * @param cqe Pointer to WQ completion queue.
8926  * @param rid Resource ID (IO tag).
8927  *
8928  *
8929  * @return None.
8930  */
8931 void
ocs_hw_xabt_process(ocs_hw_t * hw,hw_cq_t * cq,uint8_t * cqe,uint16_t rid)8932 ocs_hw_xabt_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, uint16_t rid)
8933 {
8934 	/* search IOs wait free list */
8935 	ocs_hw_io_t *io = NULL;
8936 
8937 	io = ocs_hw_io_lookup(hw, rid);
8938 
8939 	ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_XABT, (void *)cqe, 0, cq->queue->id,
8940 			      ((cq->queue->index - 1) & (cq->queue->length - 1)));
8941 	if (io == NULL) {
8942 		/* IO lookup failure should never happen */
8943 		ocs_log_err(hw->os, "Error: xabt io lookup failed rid=%#x\n", rid);
8944 		return;
8945 	}
8946 
8947 	if (!io->xbusy) {
8948 		ocs_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
8949 	} else {
8950 		/* mark IO as no longer busy */
8951 		io->xbusy = FALSE;
8952 	}
8953 
8954        if (io->is_port_owned) {
8955                ocs_lock(&hw->io_lock);
8956                /* Take reference so that below callback will not free io before reque */
8957                ocs_ref_get(&io->ref);
8958                ocs_unlock(&hw->io_lock);
8959        }
8960 
8961 
8962 
8963 	/* For IOs that were aborted internally, we need to issue any pending callback here. */
8964 	if (io->done != NULL) {
8965 		ocs_hw_done_t  done = io->done;
8966 		void		*arg = io->arg;
8967 
8968 		/* Use latched status as this is always saved for an internal abort */
8969 		int32_t status = io->saved_status;
8970 		uint32_t len = io->saved_len;
8971 		uint32_t ext = io->saved_ext;
8972 
8973 		io->done = NULL;
8974 		io->status_saved = 0;
8975 
8976 		done(io, io->rnode, len, status, ext, arg);
8977 	}
8978 
8979 	/* Check to see if this is a port owned XRI */
8980 	if (io->is_port_owned) {
8981 		ocs_lock(&hw->io_lock);
8982 		ocs_hw_reque_xri(hw, io);
8983 		ocs_unlock(&hw->io_lock);
8984 		/* Not hanlding reque xri completion, free io */
8985 		ocs_hw_io_free(hw, io);
8986 		return;
8987 	}
8988 
8989 	ocs_lock(&hw->io_lock);
8990 		if ((io->state == OCS_HW_IO_STATE_INUSE) || (io->state == OCS_HW_IO_STATE_WAIT_FREE)) {
8991 			/* if on wait_free list, caller has already freed IO;
8992 			 * remove from wait_free list and add to free list.
8993 			 * if on in-use list, already marked as no longer busy;
8994 			 * just leave there and wait for caller to free.
8995 			 */
8996 			if (io->state == OCS_HW_IO_STATE_WAIT_FREE) {
8997 				io->state = OCS_HW_IO_STATE_FREE;
8998 				ocs_list_remove(&hw->io_wait_free, io);
8999 				ocs_hw_io_free_move_correct_list(hw, io);
9000 			}
9001 		}
9002 	ocs_unlock(&hw->io_lock);
9003 }
9004 
9005 /**
9006  * @brief Adjust the number of WQs and CQs within the HW.
9007  *
9008  * @par Description
9009  * Calculates the number of WQs and associated CQs needed in the HW based on
9010  * the number of IOs. Calculates the starting CQ index for each WQ, RQ and
9011  * MQ.
9012  *
9013  * @param hw Hardware context allocated by the caller.
9014  */
9015 static void
ocs_hw_adjust_wqs(ocs_hw_t * hw)9016 ocs_hw_adjust_wqs(ocs_hw_t *hw)
9017 {
9018 	uint32_t max_wq_num = sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ);
9019 	uint32_t max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ];
9020 	uint32_t max_cq_entries = hw->num_qentries[SLI_QTYPE_CQ];
9021 
9022 	/*
9023 	 * possibly adjust the the size of the WQs so that the CQ is twice as
9024 	 * big as the WQ to allow for 2 completions per IO. This allows us to
9025 	 * handle multi-phase as well as aborts.
9026 	 */
9027 	if (max_cq_entries < max_wq_entries * 2) {
9028 		max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ] = max_cq_entries / 2;
9029 	}
9030 
9031 	/*
9032 	 * Calculate the number of WQs to use base on the number of IOs.
9033 	 *
9034 	 * Note: We need to reserve room for aborts which must be sent down
9035 	 *       the same WQ as the IO. So we allocate enough WQ space to
9036 	 *       handle 2 times the number of IOs. Half of the space will be
9037 	 *       used for normal IOs and the other hwf is reserved for aborts.
9038 	 */
9039 	hw->config.n_wq = ((hw->config.n_io * 2) + (max_wq_entries - 1)) / max_wq_entries;
9040 
9041 	/*
9042 	 * For performance reasons, it is best to use use a minimum of 4 WQs
9043 	 * for BE3 and Skyhawk.
9044 	 */
9045 	if (hw->config.n_wq < 4 &&
9046 	    SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
9047 		hw->config.n_wq = 4;
9048 	}
9049 
9050 	/*
9051 	 * For dual-chute support, we need to have at least one WQ per chute.
9052 	 */
9053 	if (hw->config.n_wq < 2 &&
9054 	    ocs_hw_get_num_chutes(hw) > 1) {
9055 		hw->config.n_wq = 2;
9056 	}
9057 
9058 	/* make sure we haven't exceeded the max supported in the HW */
9059 	if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
9060 		hw->config.n_wq = OCS_HW_MAX_NUM_WQ;
9061 	}
9062 
9063 	/* make sure we haven't exceeded the chip maximum */
9064 	if (hw->config.n_wq > max_wq_num) {
9065 		hw->config.n_wq = max_wq_num;
9066 	}
9067 
9068 	/*
9069 	 * Using Queue Topology string, we divide by number of chutes
9070 	 */
9071 	hw->config.n_wq /= ocs_hw_get_num_chutes(hw);
9072 }
9073 
9074 static int32_t
ocs_hw_command_process(ocs_hw_t * hw,int32_t status,uint8_t * mqe,size_t size)9075 ocs_hw_command_process(ocs_hw_t *hw, int32_t status, uint8_t *mqe, size_t size)
9076 {
9077 	ocs_command_ctx_t *ctx = NULL;
9078 
9079 	ocs_lock(&hw->cmd_lock);
9080 		if (NULL == (ctx = ocs_list_remove_head(&hw->cmd_head))) {
9081 			ocs_log_err(hw->os, "XXX no command context?!?\n");
9082 			ocs_unlock(&hw->cmd_lock);
9083 			return -1;
9084 		}
9085 
9086 		hw->cmd_head_count--;
9087 
9088 		/* Post any pending requests */
9089 		ocs_hw_cmd_submit_pending(hw);
9090 
9091 	ocs_unlock(&hw->cmd_lock);
9092 
9093 	if (ctx->cb) {
9094 		if (ctx->buf) {
9095 			ocs_memcpy(ctx->buf, mqe, size);
9096 		}
9097 		ctx->cb(hw, status, ctx->buf, ctx->arg);
9098 	}
9099 
9100 	ocs_memset(ctx, 0, sizeof(ocs_command_ctx_t));
9101 	ocs_free(hw->os, ctx, sizeof(ocs_command_ctx_t));
9102 
9103 	return 0;
9104 }
9105 
9106 
9107 
9108 
9109 /**
9110  * @brief Process entries on the given mailbox queue.
9111  *
9112  * @param hw Hardware context.
9113  * @param status CQE status.
9114  * @param mq Pointer to the mailbox queue object.
9115  *
9116  * @return Returns 0 on success, or a non-zero value on failure.
9117  */
9118 static int32_t
ocs_hw_mq_process(ocs_hw_t * hw,int32_t status,sli4_queue_t * mq)9119 ocs_hw_mq_process(ocs_hw_t *hw, int32_t status, sli4_queue_t *mq)
9120 {
9121 	uint8_t		mqe[SLI4_BMBX_SIZE];
9122 
9123 	if (!sli_queue_read(&hw->sli, mq, mqe)) {
9124 		ocs_hw_command_process(hw, status, mqe, mq->size);
9125 	}
9126 
9127 	return 0;
9128 }
9129 
9130 /**
9131  * @brief Read a FCF table entry.
9132  *
9133  * @param hw Hardware context.
9134  * @param index Table index to read. Use SLI4_FCOE_FCF_TABLE_FIRST for the first
9135  * read and the next_index field from the FCOE_READ_FCF_TABLE command
9136  * for subsequent reads.
9137  *
9138  * @return Returns 0 on success, or a non-zero value on failure.
9139  */
9140 static ocs_hw_rtn_e
ocs_hw_read_fcf(ocs_hw_t * hw,uint32_t index)9141 ocs_hw_read_fcf(ocs_hw_t *hw, uint32_t index)
9142 {
9143 	uint8_t		*buf = NULL;
9144 	int32_t		rc = OCS_HW_RTN_ERROR;
9145 
9146 	buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9147 	if (!buf) {
9148 		ocs_log_err(hw->os, "no buffer for command\n");
9149 		return OCS_HW_RTN_NO_MEMORY;
9150 	}
9151 
9152 	if (sli_cmd_fcoe_read_fcf_table(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->fcf_dmem,
9153 			index)) {
9154 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_read_fcf, &hw->fcf_dmem);
9155 	}
9156 
9157 	if (rc != OCS_HW_RTN_SUCCESS) {
9158 		ocs_log_test(hw->os, "FCOE_READ_FCF_TABLE failed\n");
9159 		ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9160 	}
9161 
9162 	return rc;
9163 }
9164 
9165 /**
9166  * @brief Callback function for the FCOE_READ_FCF_TABLE command.
9167  *
9168  * @par Description
9169  * Note that the caller has allocated:
9170  *  - DMA memory to hold the table contents
9171  *  - DMA memory structure
9172  *  - Command/results buffer
9173  *  .
9174  * Each of these must be freed here.
9175  *
9176  * @param hw Hardware context.
9177  * @param status Hardware status.
9178  * @param mqe Pointer to the mailbox command/results buffer.
9179  * @param arg Pointer to the DMA memory structure.
9180  *
9181  * @return Returns 0 on success, or a non-zero value on failure.
9182  */
9183 static int32_t
ocs_hw_cb_read_fcf(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9184 ocs_hw_cb_read_fcf(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9185 {
9186 	ocs_dma_t	*dma = arg;
9187 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9188 
9189 	if (status || hdr->status) {
9190 		ocs_log_test(hw->os, "bad status cqe=%#x mqe=%#x\n",
9191 				status, hdr->status);
9192 	} else if (dma->virt) {
9193 		sli4_res_fcoe_read_fcf_table_t *read_fcf = dma->virt;
9194 
9195 		/* if FC or FCOE and FCF entry valid, process it */
9196 		if (read_fcf->fcf_entry.fc ||
9197 				(read_fcf->fcf_entry.val && !read_fcf->fcf_entry.sol)) {
9198 			if (hw->callback.domain != NULL) {
9199 				ocs_domain_record_t drec = {0};
9200 
9201 				if (read_fcf->fcf_entry.fc) {
9202 					/*
9203 					 * This is a pseudo FCF entry. Create a domain
9204 					 * record based on the read topology information
9205 					 */
9206 					drec.speed = hw->link.speed;
9207 					drec.fc_id = hw->link.fc_id;
9208 					drec.is_fc = TRUE;
9209 					if (SLI_LINK_TOPO_LOOP == hw->link.topology) {
9210 						drec.is_loop = TRUE;
9211 						ocs_memcpy(drec.map.loop, hw->link.loop_map,
9212 							   sizeof(drec.map.loop));
9213 					} else if (SLI_LINK_TOPO_NPORT == hw->link.topology) {
9214 						drec.is_nport = TRUE;
9215 					}
9216 				} else {
9217 					drec.index = read_fcf->fcf_entry.fcf_index;
9218 					drec.priority = read_fcf->fcf_entry.fip_priority;
9219 
9220 					/* copy address, wwn and vlan_bitmap */
9221 					ocs_memcpy(drec.address, read_fcf->fcf_entry.fcf_mac_address,
9222 						   sizeof(drec.address));
9223 					ocs_memcpy(drec.wwn, read_fcf->fcf_entry.fabric_name_id,
9224 						   sizeof(drec.wwn));
9225 					ocs_memcpy(drec.map.vlan, read_fcf->fcf_entry.vlan_bitmap,
9226 						   sizeof(drec.map.vlan));
9227 
9228 					drec.is_ethernet = TRUE;
9229 					drec.is_nport = TRUE;
9230 				}
9231 
9232 				hw->callback.domain(hw->args.domain,
9233 						OCS_HW_DOMAIN_FOUND,
9234 						&drec);
9235 			}
9236 		} else {
9237 			/* if FCOE and FCF is not valid, ignore it */
9238 			ocs_log_test(hw->os, "ignore invalid FCF entry\n");
9239 		}
9240 
9241 		if (SLI4_FCOE_FCF_TABLE_LAST != read_fcf->next_index) {
9242 			ocs_hw_read_fcf(hw, read_fcf->next_index);
9243 		}
9244 	}
9245 
9246 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9247 	//ocs_dma_free(hw->os, dma);
9248 	//ocs_free(hw->os, dma, sizeof(ocs_dma_t));
9249 
9250 	return 0;
9251 }
9252 
9253 /**
9254  * @brief Callback function for the SLI link events.
9255  *
9256  * @par Description
9257  * This function allocates memory which must be freed in its callback.
9258  *
9259  * @param ctx Hardware context pointer (that is, ocs_hw_t *).
9260  * @param e Event structure pointer (that is, sli4_link_event_t *).
9261  *
9262  * @return Returns 0 on success, or a non-zero value on failure.
9263  */
9264 static int32_t
ocs_hw_cb_link(void * ctx,void * e)9265 ocs_hw_cb_link(void *ctx, void *e)
9266 {
9267 	ocs_hw_t	*hw = ctx;
9268 	sli4_link_event_t *event = e;
9269 	ocs_domain_t	*d = NULL;
9270 	uint32_t	i = 0;
9271 	int32_t		rc = OCS_HW_RTN_ERROR;
9272 	ocs_t 		*ocs = hw->os;
9273 
9274 	ocs_hw_link_event_init(hw);
9275 
9276 	switch (event->status) {
9277 	case SLI_LINK_STATUS_UP:
9278 
9279 		hw->link = *event;
9280 
9281 		if (SLI_LINK_TOPO_NPORT == event->topology) {
9282 			device_printf(ocs->dev, "Link Up, NPORT, speed is %d\n", event->speed);
9283 			ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9284 		} else if (SLI_LINK_TOPO_LOOP == event->topology) {
9285 			uint8_t	*buf = NULL;
9286 			device_printf(ocs->dev, "Link Up, LOOP, speed is %d\n", event->speed);
9287 
9288 			buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9289 			if (!buf) {
9290 				ocs_log_err(hw->os, "no buffer for command\n");
9291 				break;
9292 			}
9293 
9294 			if (sli_cmd_read_topology(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->loop_map)) {
9295 				rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, __ocs_read_topology_cb, NULL);
9296 			}
9297 
9298 			if (rc != OCS_HW_RTN_SUCCESS) {
9299 				ocs_log_test(hw->os, "READ_TOPOLOGY failed\n");
9300 				ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9301 			}
9302 		} else {
9303 			device_printf(ocs->dev, "Link Up, unsupported topology (%#x), speed is %d\n",
9304 					event->topology, event->speed);
9305 		}
9306 		break;
9307 	case SLI_LINK_STATUS_DOWN:
9308 		device_printf(ocs->dev, "Link Down\n");
9309 
9310 		hw->link.status = event->status;
9311 
9312 		for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9313 			if (d != NULL &&
9314 			    hw->callback.domain != NULL) {
9315 				hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, d);
9316 			}
9317 		}
9318 		break;
9319 	default:
9320 		ocs_log_test(hw->os, "unhandled link status %#x\n", event->status);
9321 		break;
9322 	}
9323 
9324 	return 0;
9325 }
9326 
9327 static int32_t
ocs_hw_cb_fip(void * ctx,void * e)9328 ocs_hw_cb_fip(void *ctx, void *e)
9329 {
9330 	ocs_hw_t	*hw = ctx;
9331 	ocs_domain_t	*domain = NULL;
9332 	sli4_fip_event_t *event = e;
9333 
9334 	/* Find the associated domain object */
9335 	if (event->type == SLI4_FCOE_FIP_FCF_CLEAR_VLINK) {
9336 		ocs_domain_t *d = NULL;
9337 		uint32_t	i = 0;
9338 
9339 		/* Clear VLINK is different from the other FIP events as it passes back
9340 		 * a VPI instead of a FCF index. Check all attached SLI ports for a
9341 		 * matching VPI */
9342 		for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9343 			if (d != NULL) {
9344 				ocs_sport_t	*sport = NULL;
9345 
9346 				ocs_list_foreach(&d->sport_list, sport) {
9347 					if (sport->indicator == event->index) {
9348 						domain = d;
9349 						break;
9350 					}
9351 				}
9352 
9353 				if (domain != NULL) {
9354 					break;
9355 				}
9356 			}
9357 		}
9358 	} else {
9359 		domain = ocs_hw_domain_get_indexed(hw, event->index);
9360 	}
9361 
9362 	switch (event->type) {
9363 	case SLI4_FCOE_FIP_FCF_DISCOVERED:
9364 		ocs_hw_read_fcf(hw, event->index);
9365 		break;
9366 	case SLI4_FCOE_FIP_FCF_DEAD:
9367 		if (domain != NULL &&
9368 		    hw->callback.domain != NULL) {
9369 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9370 		}
9371 		break;
9372 	case SLI4_FCOE_FIP_FCF_CLEAR_VLINK:
9373 		if (domain != NULL &&
9374 		    hw->callback.domain != NULL) {
9375 			/*
9376 			 * We will want to issue rediscover FCF when this domain is free'd  in order
9377 			 * to invalidate the FCF table
9378 			 */
9379 			domain->req_rediscover_fcf = TRUE;
9380 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9381 		}
9382 		break;
9383 	case SLI4_FCOE_FIP_FCF_MODIFIED:
9384 		if (domain != NULL &&
9385 		    hw->callback.domain != NULL) {
9386 			hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9387 		}
9388 
9389 		ocs_hw_read_fcf(hw, event->index);
9390 		break;
9391 	default:
9392 		ocs_log_test(hw->os, "unsupported event %#x\n", event->type);
9393 	}
9394 
9395 	return 0;
9396 }
9397 
9398 static int32_t
ocs_hw_cb_node_attach(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9399 ocs_hw_cb_node_attach(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9400 {
9401 	ocs_remote_node_t *rnode = arg;
9402 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9403 	ocs_hw_remote_node_event_e	evt = 0;
9404 
9405 	if (status || hdr->status) {
9406 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9407 				hdr->status);
9408 		ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
9409 		rnode->attached = FALSE;
9410 		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9411 		evt = OCS_HW_NODE_ATTACH_FAIL;
9412 	} else {
9413 		rnode->attached = TRUE;
9414 		ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 1);
9415 		evt = OCS_HW_NODE_ATTACH_OK;
9416 	}
9417 
9418 	if (hw->callback.rnode != NULL) {
9419 		hw->callback.rnode(hw->args.rnode, evt, rnode);
9420 	}
9421 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9422 
9423 	return 0;
9424 }
9425 
9426 static int32_t
ocs_hw_cb_node_free(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9427 ocs_hw_cb_node_free(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9428 {
9429 	ocs_remote_node_t *rnode = arg;
9430 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9431 	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9432 	int32_t		rc = 0;
9433 
9434 	if (status || hdr->status) {
9435 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9436 				hdr->status);
9437 
9438 		/*
9439 		 * In certain cases, a non-zero MQE status is OK (all must be true):
9440 		 *   - node is attached
9441 		 *   - if High Login Mode is enabled, node is part of a node group
9442 		 *   - status is 0x1400
9443 		 */
9444 		if (!rnode->attached || ((sli_get_hlm(&hw->sli) == TRUE) && !rnode->node_group) ||
9445 				(hdr->status != SLI4_MBOX_STATUS_RPI_NOT_REG)) {
9446 			rc = -1;
9447 		}
9448 	}
9449 
9450 	if (rc == 0) {
9451 		rnode->node_group = FALSE;
9452 		rnode->attached = FALSE;
9453 
9454 		if (ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_count) == 0) {
9455 			ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9456 		}
9457 
9458 		evt = OCS_HW_NODE_FREE_OK;
9459 	}
9460 
9461 	if (hw->callback.rnode != NULL) {
9462 		hw->callback.rnode(hw->args.rnode, evt, rnode);
9463 	}
9464 
9465 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9466 
9467 	return rc;
9468 }
9469 
9470 static int32_t
ocs_hw_cb_node_free_all(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9471 ocs_hw_cb_node_free_all(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9472 {
9473 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
9474 	ocs_hw_remote_node_event_e	evt = OCS_HW_NODE_FREE_FAIL;
9475 	int32_t		rc = 0;
9476 	uint32_t	i;
9477 
9478 	if (status || hdr->status) {
9479 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9480 				hdr->status);
9481 	} else {
9482 		evt = OCS_HW_NODE_FREE_ALL_OK;
9483 	}
9484 
9485 	if (evt == OCS_HW_NODE_FREE_ALL_OK) {
9486 		for (i = 0; i < sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI); i++) {
9487 			ocs_atomic_set(&hw->rpi_ref[i].rpi_count, 0);
9488 		}
9489 
9490 		if (sli_resource_reset(&hw->sli, SLI_RSRC_FCOE_RPI)) {
9491 			ocs_log_test(hw->os, "FCOE_RPI free all failure\n");
9492 			rc = -1;
9493 		}
9494 	}
9495 
9496 	if (hw->callback.rnode != NULL) {
9497 		hw->callback.rnode(hw->args.rnode, evt, NULL);
9498 	}
9499 
9500 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9501 
9502 	return rc;
9503 }
9504 
9505 /**
9506  * @brief Initialize the pool of HW IO objects.
9507  *
9508  * @param hw Hardware context.
9509  *
9510  * @return Returns 0 on success, or a non-zero value on failure.
9511  */
9512 static ocs_hw_rtn_e
ocs_hw_setup_io(ocs_hw_t * hw)9513 ocs_hw_setup_io(ocs_hw_t *hw)
9514 {
9515 	uint32_t	i = 0;
9516 	ocs_hw_io_t	*io = NULL;
9517 	uintptr_t	xfer_virt = 0;
9518 	uintptr_t	xfer_phys = 0;
9519 	uint32_t	index;
9520 	uint8_t		new_alloc = TRUE;
9521 
9522 	if (NULL == hw->io) {
9523 		hw->io = ocs_malloc(hw->os, hw->config.n_io * sizeof(ocs_hw_io_t *), OCS_M_ZERO | OCS_M_NOWAIT);
9524 
9525 		if (NULL == hw->io) {
9526 			ocs_log_err(hw->os, "IO pointer memory allocation failed, %d Ios at size %zu\n",
9527 				    hw->config.n_io,
9528 				    sizeof(ocs_hw_io_t *));
9529 			return OCS_HW_RTN_NO_MEMORY;
9530 		}
9531 		for (i = 0; i < hw->config.n_io; i++) {
9532 			hw->io[i] = ocs_malloc(hw->os, sizeof(ocs_hw_io_t),
9533 						OCS_M_ZERO | OCS_M_NOWAIT);
9534 			if (hw->io[i] == NULL) {
9535 				ocs_log_err(hw->os, "IO(%d) memory allocation failed\n", i);
9536 				goto error;
9537 			}
9538 		}
9539 
9540 		/* Create WQE buffs for IO */
9541 		hw->wqe_buffs = ocs_malloc(hw->os, hw->config.n_io * hw->sli.config.wqe_size,
9542 				OCS_M_ZERO | OCS_M_NOWAIT);
9543 		if (NULL == hw->wqe_buffs) {
9544 			ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t));
9545 			ocs_log_err(hw->os, "%s: IO WQE buff allocation failed, %d Ios at size %zu\n",
9546 					__func__, hw->config.n_io, hw->sli.config.wqe_size);
9547 			return OCS_HW_RTN_NO_MEMORY;
9548 		}
9549 
9550 	} else {
9551 		/* re-use existing IOs, including SGLs */
9552 		new_alloc = FALSE;
9553 	}
9554 
9555 	if (new_alloc) {
9556 		if (ocs_dma_alloc(hw->os, &hw->xfer_rdy,
9557 					sizeof(fcp_xfer_rdy_iu_t) * hw->config.n_io,
9558 					4/*XXX what does this need to be? */)) {
9559 			ocs_log_err(hw->os, "XFER_RDY buffer allocation failed\n");
9560 			return OCS_HW_RTN_NO_MEMORY;
9561 		}
9562 	}
9563 	xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
9564 	xfer_phys = hw->xfer_rdy.phys;
9565 
9566 	for (i = 0; i < hw->config.n_io; i++) {
9567 		hw_wq_callback_t *wqcb;
9568 
9569 		io = hw->io[i];
9570 
9571 		/* initialize IO fields */
9572 		io->hw = hw;
9573 
9574 		/* Assign a WQE buff */
9575 		io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.config.wqe_size];
9576 
9577 		/* Allocate the request tag for this IO */
9578 		wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_io, io);
9579 		if (wqcb == NULL) {
9580 			ocs_log_err(hw->os, "can't allocate request tag\n");
9581 			return OCS_HW_RTN_NO_RESOURCES;
9582 		}
9583 		io->reqtag = wqcb->instance_index;
9584 
9585 		/* Now for the fields that are initialized on each free */
9586 		ocs_hw_init_free_io(io);
9587 
9588 		/* The XB flag isn't cleared on IO free, so initialize it to zero here */
9589 		io->xbusy = 0;
9590 
9591 		if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_XRI, &io->indicator, &index)) {
9592 			ocs_log_err(hw->os, "sli_resource_alloc failed @ %d\n", i);
9593 			return OCS_HW_RTN_NO_MEMORY;
9594 		}
9595 
9596 		if (new_alloc && ocs_dma_alloc(hw->os, &io->def_sgl, hw->config.n_sgl * sizeof(sli4_sge_t), 64)) {
9597 			ocs_log_err(hw->os, "ocs_dma_alloc failed @ %d\n", i);
9598 			ocs_memset(&io->def_sgl, 0, sizeof(ocs_dma_t));
9599 			return OCS_HW_RTN_NO_MEMORY;
9600 		}
9601 		io->def_sgl_count = hw->config.n_sgl;
9602 		io->sgl = &io->def_sgl;
9603 		io->sgl_count = io->def_sgl_count;
9604 
9605 		if (hw->xfer_rdy.size) {
9606 			io->xfer_rdy.virt = (void *)xfer_virt;
9607 			io->xfer_rdy.phys = xfer_phys;
9608 			io->xfer_rdy.size = sizeof(fcp_xfer_rdy_iu_t);
9609 
9610 			xfer_virt += sizeof(fcp_xfer_rdy_iu_t);
9611 			xfer_phys += sizeof(fcp_xfer_rdy_iu_t);
9612 		}
9613 	}
9614 
9615 	return OCS_HW_RTN_SUCCESS;
9616 error:
9617 	for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
9618 		ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
9619 		hw->io[i] = NULL;
9620 	}
9621 
9622 	return OCS_HW_RTN_NO_MEMORY;
9623 }
9624 
9625 static ocs_hw_rtn_e
ocs_hw_init_io(ocs_hw_t * hw)9626 ocs_hw_init_io(ocs_hw_t *hw)
9627 {
9628 	uint32_t        i = 0, io_index = 0;
9629 	uint32_t        prereg = 0;
9630 	ocs_hw_io_t	*io = NULL;
9631 	uint8_t		cmd[SLI4_BMBX_SIZE];
9632 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
9633 	uint32_t	nremaining;
9634 	uint32_t	n = 0;
9635 	uint32_t	sgls_per_request = 256;
9636 	ocs_dma_t	**sgls = NULL;
9637 	ocs_dma_t	reqbuf = { 0 };
9638 
9639 	prereg = sli_get_sgl_preregister(&hw->sli);
9640 
9641 	if (prereg) {
9642 		sgls = ocs_malloc(hw->os, sizeof(*sgls) * sgls_per_request, OCS_M_NOWAIT);
9643 		if (sgls == NULL) {
9644 			ocs_log_err(hw->os, "ocs_malloc sgls failed\n");
9645 			return OCS_HW_RTN_NO_MEMORY;
9646 		}
9647 
9648 		rc = ocs_dma_alloc(hw->os, &reqbuf, 32 + sgls_per_request*16, OCS_MIN_DMA_ALIGNMENT);
9649 		if (rc) {
9650 			ocs_log_err(hw->os, "ocs_dma_alloc reqbuf failed\n");
9651 			ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9652 			return OCS_HW_RTN_NO_MEMORY;
9653 		}
9654 	}
9655 
9656 	io = hw->io[io_index];
9657 	for (nremaining = hw->config.n_io; nremaining; nremaining -= n) {
9658 		if (prereg) {
9659 			/* Copy address of SGL's into local sgls[] array, break out if the xri
9660 			 * is not contiguous.
9661 			 */
9662 			for (n = 0; n < MIN(sgls_per_request, nremaining); n++) {
9663 				/* Check that we have contiguous xri values */
9664 				if (n > 0) {
9665 					if (hw->io[io_index + n]->indicator != (hw->io[io_index + n-1]->indicator+1)) {
9666 						break;
9667 					}
9668 				}
9669 				sgls[n] = hw->io[io_index + n]->sgl;
9670 			}
9671 
9672 			if (sli_cmd_fcoe_post_sgl_pages(&hw->sli, cmd, sizeof(cmd),
9673 						io->indicator, n, sgls, NULL, &reqbuf)) {
9674 				if (ocs_hw_command(hw, cmd, OCS_CMD_POLL, NULL, NULL)) {
9675 					rc = OCS_HW_RTN_ERROR;
9676 					ocs_log_err(hw->os, "SGL post failed\n");
9677 					break;
9678 				}
9679 			}
9680 		} else {
9681 			n = nremaining;
9682 		}
9683 
9684 		/* Add to tail if successful */
9685 		for (i = 0; i < n; i ++) {
9686 			io->is_port_owned = 0;
9687 			io->state = OCS_HW_IO_STATE_FREE;
9688 			ocs_list_add_tail(&hw->io_free, io);
9689 			io = hw->io[io_index+1];
9690 			io_index++;
9691 		}
9692 	}
9693 
9694 	if (prereg) {
9695 		ocs_dma_free(hw->os, &reqbuf);
9696 		ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9697 	}
9698 
9699 	return rc;
9700 }
9701 
9702 static int32_t
ocs_hw_flush(ocs_hw_t * hw)9703 ocs_hw_flush(ocs_hw_t *hw)
9704 {
9705 	uint32_t	i = 0;
9706 
9707 	/* Process any remaining completions */
9708 	for (i = 0; i < hw->eq_count; i++) {
9709 		ocs_hw_process(hw, i, ~0);
9710 	}
9711 
9712 	return 0;
9713 }
9714 
9715 static int32_t
ocs_hw_command_cancel(ocs_hw_t * hw)9716 ocs_hw_command_cancel(ocs_hw_t *hw)
9717 {
9718 
9719 	ocs_lock(&hw->cmd_lock);
9720 
9721 	/*
9722 	 * Manually clean up remaining commands. Note: since this calls
9723 	 * ocs_hw_command_process(), we'll also process the cmd_pending
9724 	 * list, so no need to manually clean that out.
9725 	 */
9726 	while (!ocs_list_empty(&hw->cmd_head)) {
9727 		uint8_t		mqe[SLI4_BMBX_SIZE] = { 0 };
9728 		ocs_command_ctx_t *ctx = ocs_list_get_head(&hw->cmd_head);
9729 
9730 		ocs_log_test(hw->os, "hung command %08x\n",
9731 				NULL == ctx ? UINT32_MAX :
9732 				(NULL == ctx->buf ? UINT32_MAX : *((uint32_t *)ctx->buf)));
9733 		ocs_unlock(&hw->cmd_lock);
9734 		ocs_hw_command_process(hw, -1/*Bad status*/, mqe, SLI4_BMBX_SIZE);
9735 		ocs_lock(&hw->cmd_lock);
9736 	}
9737 
9738 	ocs_unlock(&hw->cmd_lock);
9739 
9740 	return 0;
9741 }
9742 
9743 /**
9744  * @brief Find IO given indicator (xri).
9745  *
9746  * @param hw Hal context.
9747  * @param indicator Indicator (xri) to look for.
9748  *
9749  * @return Returns io if found, NULL otherwise.
9750  */
9751 ocs_hw_io_t *
ocs_hw_io_lookup(ocs_hw_t * hw,uint32_t xri)9752 ocs_hw_io_lookup(ocs_hw_t *hw, uint32_t xri)
9753 {
9754 	uint32_t ioindex;
9755 	ioindex = xri - hw->sli.config.extent[SLI_RSRC_FCOE_XRI].base[0];
9756 	return hw->io[ioindex];
9757 }
9758 
9759 /**
9760  * @brief Issue any pending callbacks for an IO and remove off the timer and pending lists.
9761  *
9762  * @param hw Hal context.
9763  * @param io Pointer to the IO to cleanup.
9764  */
9765 static void
ocs_hw_io_cancel_cleanup(ocs_hw_t * hw,ocs_hw_io_t * io)9766 ocs_hw_io_cancel_cleanup(ocs_hw_t *hw, ocs_hw_io_t *io)
9767 {
9768 	ocs_hw_done_t  done = io->done;
9769 	ocs_hw_done_t  abort_done = io->abort_done;
9770 
9771 	/* first check active_wqe list and remove if there */
9772 	if (ocs_list_on_list(&io->wqe_link)) {
9773 		ocs_list_remove(&hw->io_timed_wqe, io);
9774 	}
9775 
9776 	/* Remove from WQ pending list */
9777 	if ((io->wq != NULL) && ocs_list_on_list(&io->wq->pending_list)) {
9778 		ocs_list_remove(&io->wq->pending_list, io);
9779 	}
9780 
9781 	if (io->done) {
9782 		void		*arg = io->arg;
9783 
9784 		io->done = NULL;
9785 		ocs_unlock(&hw->io_lock);
9786 		done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, arg);
9787 		ocs_lock(&hw->io_lock);
9788 	}
9789 
9790 	if (io->abort_done != NULL) {
9791 		void		*abort_arg = io->abort_arg;
9792 
9793 		io->abort_done = NULL;
9794 		ocs_unlock(&hw->io_lock);
9795 		abort_done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, abort_arg);
9796 		ocs_lock(&hw->io_lock);
9797 	}
9798 }
9799 
9800 static int32_t
ocs_hw_io_cancel(ocs_hw_t * hw)9801 ocs_hw_io_cancel(ocs_hw_t *hw)
9802 {
9803 	ocs_hw_io_t	*io = NULL;
9804 	ocs_hw_io_t	*tmp_io = NULL;
9805 	uint32_t	iters = 100; /* One second limit */
9806 
9807 	/*
9808 	 * Manually clean up outstanding IO.
9809 	 * Only walk through list once: the backend will cleanup any IOs when done/abort_done is called.
9810 	 */
9811 	ocs_lock(&hw->io_lock);
9812 	ocs_list_foreach_safe(&hw->io_inuse, io, tmp_io) {
9813 		ocs_hw_done_t  done = io->done;
9814 		ocs_hw_done_t  abort_done = io->abort_done;
9815 
9816 		ocs_hw_io_cancel_cleanup(hw, io);
9817 
9818 		/*
9819 		 * Since this is called in a reset/shutdown
9820 		 * case, If there is no callback, then just
9821 		 * free the IO.
9822 		 *
9823 		 * Note: A port owned XRI cannot be on
9824 		 *       the in use list. We cannot call
9825 		 *       ocs_hw_io_free() because we already
9826 		 *       hold the io_lock.
9827 		 */
9828 		if (done == NULL &&
9829 		    abort_done == NULL) {
9830 			/*
9831 			 * Since this is called in a reset/shutdown
9832 			 * case, If there is no callback, then just
9833 			 * free the IO.
9834 			 */
9835 			ocs_hw_io_free_common(hw, io);
9836 			ocs_list_remove(&hw->io_inuse, io);
9837 			ocs_hw_io_free_move_correct_list(hw, io);
9838 		}
9839 	}
9840 
9841 	/*
9842 	 * For port owned XRIs, they are not on the in use list, so
9843 	 * walk though XRIs and issue any callbacks.
9844 	 */
9845 	ocs_list_foreach_safe(&hw->io_port_owned, io, tmp_io) {
9846 		/* check  list and remove if there */
9847 		if (ocs_list_on_list(&io->dnrx_link)) {
9848 			ocs_list_remove(&hw->io_port_dnrx, io);
9849 			ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
9850 		}
9851 		ocs_hw_io_cancel_cleanup(hw, io);
9852 		ocs_list_remove(&hw->io_port_owned, io);
9853 		ocs_hw_io_free_common(hw, io);
9854 	}
9855 	ocs_unlock(&hw->io_lock);
9856 
9857 	/* Give time for the callbacks to complete */
9858 	do {
9859 		ocs_udelay(10000);
9860 		iters--;
9861 	} while (!ocs_list_empty(&hw->io_inuse) && iters);
9862 
9863 	/* Leave a breadcrumb that cleanup is not yet complete. */
9864 	if (!ocs_list_empty(&hw->io_inuse)) {
9865 		ocs_log_test(hw->os, "io_inuse list is not empty\n");
9866 	}
9867 
9868 	return 0;
9869 }
9870 
9871 static int32_t
ocs_hw_io_ini_sge(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_dma_t * cmnd,uint32_t cmnd_size,ocs_dma_t * rsp)9872 ocs_hw_io_ini_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *cmnd, uint32_t cmnd_size,
9873 		ocs_dma_t *rsp)
9874 {
9875 	sli4_sge_t	*data = NULL;
9876 
9877 	if (!hw || !io) {
9878 		ocs_log_err(NULL, "bad parm hw=%p io=%p\n", hw, io);
9879 		return OCS_HW_RTN_ERROR;
9880 	}
9881 
9882 	data = io->def_sgl.virt;
9883 
9884 	/* setup command pointer */
9885 	data->buffer_address_high = ocs_addr32_hi(cmnd->phys);
9886 	data->buffer_address_low  = ocs_addr32_lo(cmnd->phys);
9887 	data->buffer_length = cmnd_size;
9888 	data++;
9889 
9890 	/* setup response pointer */
9891 	data->buffer_address_high = ocs_addr32_hi(rsp->phys);
9892 	data->buffer_address_low  = ocs_addr32_lo(rsp->phys);
9893 	data->buffer_length = rsp->size;
9894 
9895 	return 0;
9896 }
9897 
9898 static int32_t
__ocs_read_topology_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9899 __ocs_read_topology_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9900 {
9901 	sli4_cmd_read_topology_t *read_topo = (sli4_cmd_read_topology_t *)mqe;
9902 
9903 	if (status || read_topo->hdr.status) {
9904 		ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n",
9905 				status, read_topo->hdr.status);
9906 		ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9907 		return -1;
9908 	}
9909 
9910 	switch (read_topo->attention_type) {
9911 	case SLI4_READ_TOPOLOGY_LINK_UP:
9912 		hw->link.status = SLI_LINK_STATUS_UP;
9913 		break;
9914 	case SLI4_READ_TOPOLOGY_LINK_DOWN:
9915 		hw->link.status = SLI_LINK_STATUS_DOWN;
9916 		break;
9917 	case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
9918 		hw->link.status = SLI_LINK_STATUS_NO_ALPA;
9919 		break;
9920 	default:
9921 		hw->link.status = SLI_LINK_STATUS_MAX;
9922 		break;
9923 	}
9924 
9925 	switch (read_topo->topology) {
9926 	case SLI4_READ_TOPOLOGY_NPORT:
9927 		hw->link.topology = SLI_LINK_TOPO_NPORT;
9928 		break;
9929 	case SLI4_READ_TOPOLOGY_FC_AL:
9930 		hw->link.topology = SLI_LINK_TOPO_LOOP;
9931 		if (SLI_LINK_STATUS_UP == hw->link.status) {
9932 			hw->link.loop_map = hw->loop_map.virt;
9933 		}
9934 		hw->link.fc_id = read_topo->acquired_al_pa;
9935 		break;
9936 	default:
9937 		hw->link.topology = SLI_LINK_TOPO_MAX;
9938 		break;
9939 	}
9940 
9941 	hw->link.medium = SLI_LINK_MEDIUM_FC;
9942 
9943 	switch (read_topo->link_current.link_speed) {
9944 	case SLI4_READ_TOPOLOGY_SPEED_1G:
9945 		hw->link.speed =  1 * 1000;
9946 		break;
9947 	case SLI4_READ_TOPOLOGY_SPEED_2G:
9948 		hw->link.speed =  2 * 1000;
9949 		break;
9950 	case SLI4_READ_TOPOLOGY_SPEED_4G:
9951 		hw->link.speed =  4 * 1000;
9952 		break;
9953 	case SLI4_READ_TOPOLOGY_SPEED_8G:
9954 		hw->link.speed =  8 * 1000;
9955 		break;
9956 	case SLI4_READ_TOPOLOGY_SPEED_16G:
9957 		hw->link.speed = 16 * 1000;
9958 		hw->link.loop_map = NULL;
9959 		break;
9960 	case SLI4_READ_TOPOLOGY_SPEED_32G:
9961 		hw->link.speed = 32 * 1000;
9962 		hw->link.loop_map = NULL;
9963 		break;
9964 	}
9965 
9966 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9967 
9968 	ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9969 
9970 	return 0;
9971 }
9972 
9973 static int32_t
__ocs_hw_port_common(const char * funcname,ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)9974 __ocs_hw_port_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9975 {
9976 	ocs_sli_port_t	*sport = ctx->app;
9977 	ocs_hw_t	*hw = sport->hw;
9978 
9979 	smtrace("port");
9980 
9981 	switch (evt) {
9982 	case OCS_EVT_EXIT:
9983 		/* ignore */
9984 		break;
9985 
9986 	case OCS_EVT_HW_PORT_REQ_FREE:
9987 	case OCS_EVT_HW_PORT_REQ_ATTACH:
9988 		if (data != NULL) {
9989 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
9990 		}
9991 		/* fall through */
9992 	default:
9993 		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
9994 		break;
9995 	}
9996 
9997 	return 0;
9998 }
9999 
10000 static void *
__ocs_hw_port_free_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10001 __ocs_hw_port_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10002 {
10003 	ocs_sli_port_t	*sport = ctx->app;
10004 	ocs_hw_t	*hw = sport->hw;
10005 
10006 	smtrace("port");
10007 
10008 	switch (evt) {
10009 	case OCS_EVT_ENTER:
10010 		if (data != NULL) {
10011 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10012 		}
10013 		if (hw->callback.port != NULL) {
10014 			hw->callback.port(hw->args.port,
10015 					OCS_HW_PORT_FREE_FAIL, sport);
10016 		}
10017 		break;
10018 	default:
10019 		break;
10020 	}
10021 
10022 	return NULL;
10023 }
10024 
10025 static void *
__ocs_hw_port_freed(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10026 __ocs_hw_port_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10027 {
10028 	ocs_sli_port_t	*sport = ctx->app;
10029 	ocs_hw_t	*hw = sport->hw;
10030 
10031 	smtrace("port");
10032 
10033 	switch (evt) {
10034 	case OCS_EVT_ENTER:
10035 		/* free SLI resource */
10036 		if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator)) {
10037 			ocs_log_err(hw->os, "FCOE_VPI free failure addr=%#x\n", sport->fc_id);
10038 		}
10039 
10040 		/* free mailbox buffer */
10041 		if (data != NULL) {
10042 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10043 		}
10044 		if (hw->callback.port != NULL) {
10045 			hw->callback.port(hw->args.port,
10046 					OCS_HW_PORT_FREE_OK, sport);
10047 		}
10048 		break;
10049 	default:
10050 		break;
10051 	}
10052 
10053 	return NULL;
10054 }
10055 
10056 static void *
__ocs_hw_port_attach_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10057 __ocs_hw_port_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10058 {
10059 	ocs_sli_port_t	*sport = ctx->app;
10060 	ocs_hw_t	*hw = sport->hw;
10061 
10062 	smtrace("port");
10063 
10064 	switch (evt) {
10065 	case OCS_EVT_ENTER:
10066 		/* free SLI resource */
10067 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10068 
10069 		/* free mailbox buffer */
10070 		if (data != NULL) {
10071 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10072 		}
10073 
10074 		if (hw->callback.port != NULL) {
10075 			hw->callback.port(hw->args.port,
10076 					OCS_HW_PORT_ATTACH_FAIL, sport);
10077 		}
10078 		if (sport->sm_free_req_pending) {
10079 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10080 		}
10081 		break;
10082 	default:
10083 		__ocs_hw_port_common(__func__, ctx, evt, data);
10084 		break;
10085 	}
10086 
10087 	return NULL;
10088 }
10089 
10090 static void *
__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10091 __ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10092 {
10093 	ocs_sli_port_t	*sport = ctx->app;
10094 	ocs_hw_t	*hw = sport->hw;
10095 	uint8_t		*cmd = NULL;
10096 
10097 	smtrace("port");
10098 
10099 	switch (evt) {
10100 	case OCS_EVT_ENTER:
10101 		/* allocate memory and send unreg_vpi */
10102 		cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10103 		if (!cmd) {
10104 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10105 			break;
10106 		}
10107 
10108 		if (0 == sli_cmd_unreg_vpi(&hw->sli, cmd, SLI4_BMBX_SIZE, sport->indicator,
10109 					   SLI4_UNREG_TYPE_PORT)) {
10110 			ocs_log_err(hw->os, "UNREG_VPI format failure\n");
10111 			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10112 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10113 			break;
10114 		}
10115 
10116 		if (ocs_hw_command(hw, cmd, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10117 			ocs_log_err(hw->os, "UNREG_VPI command failure\n");
10118 			ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10119 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10120 			break;
10121 		}
10122 		break;
10123 	case OCS_EVT_RESPONSE:
10124 		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10125 		break;
10126 	case OCS_EVT_ERROR:
10127 		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10128 		break;
10129 	default:
10130 		__ocs_hw_port_common(__func__, ctx, evt, data);
10131 		break;
10132 	}
10133 
10134 	return NULL;
10135 }
10136 
10137 static void *
__ocs_hw_port_free_nop(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10138 __ocs_hw_port_free_nop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10139 {
10140 	ocs_sli_port_t	*sport = ctx->app;
10141 	ocs_hw_t	*hw = sport->hw;
10142 
10143 	smtrace("port");
10144 
10145 	switch (evt) {
10146 	case OCS_EVT_ENTER:
10147 		/* Forward to execute in mailbox completion processing context */
10148 		if (ocs_hw_async_call(hw, __ocs_hw_port_realloc_cb, sport)) {
10149 			ocs_log_err(hw->os, "ocs_hw_async_call failed\n");
10150 		}
10151 		break;
10152 	case OCS_EVT_RESPONSE:
10153 		ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10154 		break;
10155 	case OCS_EVT_ERROR:
10156 		ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10157 		break;
10158 	default:
10159 		break;
10160 	}
10161 
10162 	return NULL;
10163 }
10164 
10165 static void *
__ocs_hw_port_attached(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10166 __ocs_hw_port_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10167 {
10168 	ocs_sli_port_t	*sport = ctx->app;
10169 	ocs_hw_t	*hw = sport->hw;
10170 
10171 	smtrace("port");
10172 
10173 	switch (evt) {
10174 	case OCS_EVT_ENTER:
10175 		if (data != NULL) {
10176 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10177 		}
10178 		if (hw->callback.port != NULL) {
10179 			hw->callback.port(hw->args.port,
10180 					OCS_HW_PORT_ATTACH_OK, sport);
10181 		}
10182 		if (sport->sm_free_req_pending) {
10183 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10184 		}
10185 		break;
10186 	case OCS_EVT_HW_PORT_REQ_FREE:
10187 		/* virtual/physical port request free */
10188 		ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10189 		break;
10190 	default:
10191 		__ocs_hw_port_common(__func__, ctx, evt, data);
10192 		break;
10193 	}
10194 
10195 	return NULL;
10196 }
10197 
10198 static void *
__ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10199 __ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10200 {
10201 	ocs_sli_port_t	*sport = ctx->app;
10202 	ocs_hw_t	*hw = sport->hw;
10203 
10204 	smtrace("port");
10205 
10206 	switch (evt) {
10207 	case OCS_EVT_ENTER:
10208 		if (0 == sli_cmd_reg_vpi(&hw->sli, data, SLI4_BMBX_SIZE, sport, FALSE)) {
10209 			ocs_log_err(hw->os, "REG_VPI format failure\n");
10210 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10211 			break;
10212 		}
10213 
10214 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10215 			ocs_log_err(hw->os, "REG_VPI command failure\n");
10216 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10217 			break;
10218 		}
10219 		break;
10220 	case OCS_EVT_RESPONSE:
10221 		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10222 		break;
10223 	case OCS_EVT_ERROR:
10224 		ocs_sm_transition(ctx, __ocs_hw_port_attach_report_fail, data);
10225 		break;
10226 	case OCS_EVT_HW_PORT_REQ_FREE:
10227 		/* Wait for attach response and then free */
10228 		sport->sm_free_req_pending = 1;
10229 		break;
10230 	default:
10231 		__ocs_hw_port_common(__func__, ctx, evt, data);
10232 		break;
10233 	}
10234 
10235 	return NULL;
10236 }
10237 
10238 static void *
__ocs_hw_port_done(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10239 __ocs_hw_port_done(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10240 {
10241 	ocs_sli_port_t	*sport = ctx->app;
10242 	ocs_hw_t	*hw = sport->hw;
10243 
10244 	smtrace("port");
10245 
10246 	switch (evt) {
10247 	case OCS_EVT_ENTER:
10248 		/* free SLI resource */
10249 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10250 
10251 		/* free mailbox buffer */
10252 		if (data != NULL) {
10253 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10254 		}
10255 		break;
10256 	default:
10257 		__ocs_hw_port_common(__func__, ctx, evt, data);
10258 		break;
10259 	}
10260 
10261 	return NULL;
10262 }
10263 
10264 static void *
__ocs_hw_port_allocated(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10265 __ocs_hw_port_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10266 {
10267 	ocs_sli_port_t	*sport = ctx->app;
10268 	ocs_hw_t	*hw = sport->hw;
10269 
10270 	smtrace("port");
10271 
10272 	switch (evt) {
10273 	case OCS_EVT_ENTER:
10274 		if (data != NULL) {
10275 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10276 		}
10277 		if (hw->callback.port != NULL) {
10278 			hw->callback.port(hw->args.port,
10279 					OCS_HW_PORT_ALLOC_OK, sport);
10280 		}
10281 		/* If there is a pending free request, then handle it now */
10282 		if (sport->sm_free_req_pending) {
10283 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10284 		}
10285 		break;
10286 	case OCS_EVT_HW_PORT_REQ_ATTACH:
10287 		/* virtual port requests attach */
10288 		ocs_sm_transition(ctx, __ocs_hw_port_attach_reg_vpi, data);
10289 		break;
10290 	case OCS_EVT_HW_PORT_ATTACH_OK:
10291 		/* physical port attached (as part of attaching domain) */
10292 		ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10293 		break;
10294 	case OCS_EVT_HW_PORT_REQ_FREE:
10295 		/* virtual port request free */
10296 		if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
10297 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10298 		} else {
10299 			/*
10300 			 * Note: BE3/Skyhawk will respond with a status of 0x20
10301 			 *       unless the reg_vpi has been issued, so we can
10302 			 *       skip the unreg_vpi for these adapters.
10303 			 *
10304 			 * Send a nop to make sure that free doesn't occur in
10305 			 * same context
10306 			 */
10307 			ocs_sm_transition(ctx, __ocs_hw_port_free_nop, NULL);
10308 		}
10309 		break;
10310 	default:
10311 		__ocs_hw_port_common(__func__, ctx, evt, data);
10312 		break;
10313 	}
10314 
10315 	return NULL;
10316 }
10317 
10318 static void *
__ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10319 __ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10320 {
10321 	ocs_sli_port_t	*sport = ctx->app;
10322 	ocs_hw_t	*hw = sport->hw;
10323 
10324 	smtrace("port");
10325 
10326 	switch (evt) {
10327 	case OCS_EVT_ENTER:
10328 		/* free SLI resource */
10329 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10330 
10331 		/* free mailbox buffer */
10332 		if (data != NULL) {
10333 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10334 		}
10335 
10336 		if (hw->callback.port != NULL) {
10337 			hw->callback.port(hw->args.port,
10338 					OCS_HW_PORT_ALLOC_FAIL, sport);
10339 		}
10340 
10341 		/* If there is a pending free request, then handle it now */
10342 		if (sport->sm_free_req_pending) {
10343 			ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10344 		}
10345 		break;
10346 	default:
10347 		__ocs_hw_port_common(__func__, ctx, evt, data);
10348 		break;
10349 	}
10350 
10351 	return NULL;
10352 }
10353 
10354 static void *
__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10355 __ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10356 {
10357 	ocs_sli_port_t	*sport = ctx->app;
10358 	ocs_hw_t	*hw = sport->hw;
10359 	uint8_t		*payload = NULL;
10360 
10361 	smtrace("port");
10362 
10363 	switch (evt) {
10364 	case OCS_EVT_ENTER:
10365 		/* allocate memory for the service parameters */
10366 		if (ocs_dma_alloc(hw->os, &sport->dma, 112, 4)) {
10367 			ocs_log_err(hw->os, "Failed to allocate DMA memory\n");
10368 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10369 			break;
10370 		}
10371 
10372 		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10373 					&sport->dma, sport->indicator)) {
10374 			ocs_log_err(hw->os, "READ_SPARM64 allocation failure\n");
10375 			ocs_dma_free(hw->os, &sport->dma);
10376 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10377 			break;
10378 		}
10379 
10380 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10381 			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10382 			ocs_dma_free(hw->os, &sport->dma);
10383 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10384 			break;
10385 		}
10386 		break;
10387 	case OCS_EVT_RESPONSE:
10388 		payload = sport->dma.virt;
10389 
10390 		ocs_display_sparams(sport->display_name, "sport sparm64", 0, NULL, payload);
10391 
10392 		ocs_memcpy(&sport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
10393 				sizeof(sport->sli_wwpn));
10394 		ocs_memcpy(&sport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
10395 				sizeof(sport->sli_wwnn));
10396 
10397 		ocs_dma_free(hw->os, &sport->dma);
10398 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_init_vpi, data);
10399 		break;
10400 	case OCS_EVT_ERROR:
10401 		ocs_dma_free(hw->os, &sport->dma);
10402 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10403 		break;
10404 	case OCS_EVT_HW_PORT_REQ_FREE:
10405 		/* Wait for attach response and then free */
10406 		sport->sm_free_req_pending = 1;
10407 		break;
10408 	case OCS_EVT_EXIT:
10409 		break;
10410 	default:
10411 		__ocs_hw_port_common(__func__, ctx, evt, data);
10412 		break;
10413 	}
10414 
10415 	return NULL;
10416 }
10417 
10418 static void *
__ocs_hw_port_alloc_init(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10419 __ocs_hw_port_alloc_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10420 {
10421 	ocs_sli_port_t	*sport = ctx->app;
10422 
10423 	smtrace("port");
10424 
10425 	switch (evt) {
10426 	case OCS_EVT_ENTER:
10427 		/* no-op */
10428 		break;
10429 	case OCS_EVT_HW_PORT_ALLOC_OK:
10430 		ocs_sm_transition(ctx, __ocs_hw_port_allocated, NULL);
10431 		break;
10432 	case OCS_EVT_HW_PORT_ALLOC_FAIL:
10433 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, NULL);
10434 		break;
10435 	case OCS_EVT_HW_PORT_REQ_FREE:
10436 		/* Wait for attach response and then free */
10437 		sport->sm_free_req_pending = 1;
10438 		break;
10439 	default:
10440 		__ocs_hw_port_common(__func__, ctx, evt, data);
10441 		break;
10442 	}
10443 
10444 	return NULL;
10445 }
10446 
10447 static void *
__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10448 __ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10449 {
10450 	ocs_sli_port_t	*sport = ctx->app;
10451 	ocs_hw_t	*hw = sport->hw;
10452 
10453 	smtrace("port");
10454 
10455 	switch (evt) {
10456 	case OCS_EVT_ENTER:
10457 		/* If there is a pending free request, then handle it now */
10458 		if (sport->sm_free_req_pending) {
10459 			ocs_sm_transition(ctx, __ocs_hw_port_freed, NULL);
10460 			return NULL;
10461 		}
10462 
10463 		/* TODO XXX transitioning to done only works if this is called
10464 		 * directly from ocs_hw_port_alloc BUT not if called from
10465 		 * read_sparm64. In the later case, we actually want to go
10466 		 * through report_ok/fail
10467 		 */
10468 		if (0 == sli_cmd_init_vpi(&hw->sli, data, SLI4_BMBX_SIZE,
10469 					sport->indicator, sport->domain->indicator)) {
10470 			ocs_log_err(hw->os, "INIT_VPI allocation failure\n");
10471 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10472 			break;
10473 		}
10474 
10475 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10476 			ocs_log_err(hw->os, "INIT_VPI command failure\n");
10477 			ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10478 			break;
10479 		}
10480 		break;
10481 	case OCS_EVT_RESPONSE:
10482 		ocs_sm_transition(ctx, __ocs_hw_port_allocated, data);
10483 		break;
10484 	case OCS_EVT_ERROR:
10485 		ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10486 		break;
10487 	case OCS_EVT_HW_PORT_REQ_FREE:
10488 		/* Wait for attach response and then free */
10489 		sport->sm_free_req_pending = 1;
10490 		break;
10491 	case OCS_EVT_EXIT:
10492 		break;
10493 	default:
10494 		__ocs_hw_port_common(__func__, ctx, evt, data);
10495 		break;
10496 	}
10497 
10498 	return NULL;
10499 }
10500 
10501 static int32_t
__ocs_hw_port_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)10502 __ocs_hw_port_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10503 {
10504 	ocs_sli_port_t *sport = arg;
10505 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10506 	ocs_sm_event_t	evt;
10507 
10508 	if (status || hdr->status) {
10509 		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10510 			      sport->indicator, status, hdr->status);
10511 		evt = OCS_EVT_ERROR;
10512 	} else {
10513 		evt = OCS_EVT_RESPONSE;
10514 	}
10515 
10516 	ocs_sm_post_event(&sport->ctx, evt, mqe);
10517 
10518 	return 0;
10519 }
10520 
10521 static int32_t
__ocs_hw_port_realloc_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)10522 __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10523 {
10524 	ocs_sli_port_t *sport = arg;
10525 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
10526 	ocs_sm_event_t	evt;
10527 	uint8_t *mqecpy;
10528 
10529 	if (status || hdr->status) {
10530 		ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10531 			      sport->indicator, status, hdr->status);
10532 		evt = OCS_EVT_ERROR;
10533 	} else {
10534 		evt = OCS_EVT_RESPONSE;
10535 	}
10536 
10537 	/*
10538 	 * In this case we have to malloc a mailbox command buffer, as it is reused
10539 	 * in the state machine post event call, and eventually freed
10540 	 */
10541 	mqecpy = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10542 	if (mqecpy == NULL) {
10543 		ocs_log_err(hw->os, "malloc mqecpy failed\n");
10544 		return -1;
10545 	}
10546 	ocs_memcpy(mqecpy, mqe, SLI4_BMBX_SIZE);
10547 
10548 	ocs_sm_post_event(&sport->ctx, evt, mqecpy);
10549 
10550 	return 0;
10551 }
10552 
10553 /***************************************************************************
10554  * Domain state machine
10555  */
10556 
10557 static int32_t
__ocs_hw_domain_common(const char * funcname,ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10558 __ocs_hw_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10559 {
10560 	ocs_domain_t	*domain = ctx->app;
10561 	ocs_hw_t	*hw = domain->hw;
10562 
10563 	smtrace("domain");
10564 
10565 	switch (evt) {
10566 	case OCS_EVT_EXIT:
10567 		/* ignore */
10568 		break;
10569 
10570 	default:
10571 		ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
10572 		break;
10573 	}
10574 
10575 	return 0;
10576 }
10577 
10578 static void *
__ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10579 __ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10580 {
10581 	ocs_domain_t	*domain = ctx->app;
10582 	ocs_hw_t	*hw = domain->hw;
10583 
10584 	smtrace("domain");
10585 
10586 	switch (evt) {
10587 	case OCS_EVT_ENTER:
10588 		/* free command buffer */
10589 		if (data != NULL) {
10590 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10591 		}
10592 		/* free SLI resources */
10593 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10594 		/* TODO how to free FCFI (or do we at all)? */
10595 
10596 		if (hw->callback.domain != NULL) {
10597 			hw->callback.domain(hw->args.domain,
10598 					OCS_HW_DOMAIN_ALLOC_FAIL,
10599 					domain);
10600 		}
10601 		break;
10602 	default:
10603 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10604 		break;
10605 	}
10606 
10607 	return NULL;
10608 }
10609 
10610 static void *
__ocs_hw_domain_attached(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10611 __ocs_hw_domain_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10612 {
10613 	ocs_domain_t	*domain = ctx->app;
10614 	ocs_hw_t	*hw = domain->hw;
10615 
10616 	smtrace("domain");
10617 
10618 	switch (evt) {
10619 	case OCS_EVT_ENTER:
10620 		/* free mailbox buffer and send alloc ok to physical sport */
10621 		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10622 		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ATTACH_OK, NULL);
10623 
10624 		/* now inform registered callbacks */
10625 		if (hw->callback.domain != NULL) {
10626 			hw->callback.domain(hw->args.domain,
10627 					OCS_HW_DOMAIN_ATTACH_OK,
10628 					domain);
10629 		}
10630 		break;
10631 	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10632 		ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10633 		break;
10634 	default:
10635 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10636 		break;
10637 	}
10638 
10639 	return NULL;
10640 }
10641 
10642 static void *
__ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10643 __ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10644 {
10645 	ocs_domain_t	*domain = ctx->app;
10646 	ocs_hw_t	*hw = domain->hw;
10647 
10648 	smtrace("domain");
10649 
10650 	switch (evt) {
10651 	case OCS_EVT_ENTER:
10652 		if (data != NULL) {
10653 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10654 		}
10655 		/* free SLI resources */
10656 		sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10657 		/* TODO how to free FCFI (or do we at all)? */
10658 
10659 		if (hw->callback.domain != NULL) {
10660 			hw->callback.domain(hw->args.domain,
10661 					OCS_HW_DOMAIN_ATTACH_FAIL,
10662 					domain);
10663 		}
10664 		break;
10665 	case OCS_EVT_EXIT:
10666 		break;
10667 	default:
10668 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10669 		break;
10670 	}
10671 
10672 	return NULL;
10673 }
10674 
10675 static void *
__ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10676 __ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10677 {
10678 	ocs_domain_t	*domain = ctx->app;
10679 	ocs_hw_t	*hw = domain->hw;
10680 
10681 	smtrace("domain");
10682 
10683 	switch (evt) {
10684 	case OCS_EVT_ENTER:
10685 
10686 		ocs_display_sparams("", "reg vpi", 0, NULL, domain->dma.virt);
10687 
10688 		if (0 == sli_cmd_reg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain)) {
10689 			ocs_log_err(hw->os, "REG_VFI format failure\n");
10690 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10691 			break;
10692 		}
10693 
10694 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10695 			ocs_log_err(hw->os, "REG_VFI command failure\n");
10696 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10697 			break;
10698 		}
10699 		break;
10700 	case OCS_EVT_RESPONSE:
10701 		ocs_sm_transition(ctx, __ocs_hw_domain_attached, data);
10702 		break;
10703 	case OCS_EVT_ERROR:
10704 		ocs_sm_transition(ctx, __ocs_hw_domain_attach_report_fail, data);
10705 		break;
10706 	default:
10707 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10708 		break;
10709 	}
10710 
10711 	return NULL;
10712 }
10713 
10714 static void *
__ocs_hw_domain_allocated(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10715 __ocs_hw_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10716 {
10717 	ocs_domain_t	*domain = ctx->app;
10718 	ocs_hw_t	*hw = domain->hw;
10719 
10720 	smtrace("domain");
10721 
10722 	switch (evt) {
10723 	case OCS_EVT_ENTER:
10724 		/* free mailbox buffer and send alloc ok to physical sport */
10725 		ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10726 		ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ALLOC_OK, NULL);
10727 
10728 		ocs_hw_domain_add(hw, domain);
10729 
10730 		/* now inform registered callbacks */
10731 		if (hw->callback.domain != NULL) {
10732 			hw->callback.domain(hw->args.domain,
10733 					OCS_HW_DOMAIN_ALLOC_OK,
10734 					domain);
10735 		}
10736 		break;
10737 	case OCS_EVT_HW_DOMAIN_REQ_ATTACH:
10738 		ocs_sm_transition(ctx, __ocs_hw_domain_attach_reg_vfi, data);
10739 		break;
10740 	case OCS_EVT_HW_DOMAIN_REQ_FREE:
10741 		/* unreg_fcfi/vfi */
10742 		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10743 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, NULL);
10744 		} else {
10745 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10746 		}
10747 		break;
10748 	default:
10749 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10750 		break;
10751 	}
10752 
10753 	return NULL;
10754 }
10755 
10756 static void *
__ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10757 __ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10758 {
10759 	ocs_domain_t	*domain = ctx->app;
10760 	ocs_hw_t	*hw = domain->hw;
10761 
10762 	smtrace("domain");
10763 
10764 	switch (evt) {
10765 	case OCS_EVT_ENTER:
10766 		if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10767 					&domain->dma, SLI4_READ_SPARM64_VPI_DEFAULT)) {
10768 			ocs_log_err(hw->os, "READ_SPARM64 format failure\n");
10769 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10770 			break;
10771 		}
10772 
10773 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10774 			ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10775 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10776 			break;
10777 		}
10778 		break;
10779 	case OCS_EVT_EXIT:
10780 		break;
10781 	case OCS_EVT_RESPONSE:
10782 		ocs_display_sparams(domain->display_name, "domain sparm64", 0, NULL, domain->dma.virt);
10783 
10784 		ocs_sm_transition(ctx, __ocs_hw_domain_allocated, data);
10785 		break;
10786 	case OCS_EVT_ERROR:
10787 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10788 		break;
10789 	default:
10790 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10791 		break;
10792 	}
10793 
10794 	return NULL;
10795 }
10796 
10797 static void *
__ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10798 __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10799 {
10800 	ocs_domain_t	*domain = ctx->app;
10801 	ocs_sli_port_t	*sport = domain->sport;
10802 	ocs_hw_t	*hw = domain->hw;
10803 
10804 	smtrace("domain");
10805 
10806 	switch (evt) {
10807 	case OCS_EVT_ENTER:
10808 		if (0 == sli_cmd_init_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->indicator,
10809 					domain->fcf_indicator, sport->indicator)) {
10810 			ocs_log_err(hw->os, "INIT_VFI format failure\n");
10811 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10812 			break;
10813 		}
10814 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10815 			ocs_log_err(hw->os, "INIT_VFI command failure\n");
10816 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10817 			break;
10818 		}
10819 		break;
10820 	case OCS_EVT_EXIT:
10821 		break;
10822 	case OCS_EVT_RESPONSE:
10823 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10824 		break;
10825 	case OCS_EVT_ERROR:
10826 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10827 		break;
10828 	default:
10829 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10830 		break;
10831 	}
10832 
10833 	return NULL;
10834 }
10835 
10836 static void *
__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10837 __ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10838 {
10839 	ocs_domain_t	*domain = ctx->app;
10840 	ocs_hw_t	*hw = domain->hw;
10841 
10842 	smtrace("domain");
10843 
10844 	switch (evt) {
10845 	case OCS_EVT_ENTER: {
10846 		sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
10847 		uint32_t i;
10848 
10849 		/* Set the filter match/mask values from hw's filter_def values */
10850 		for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
10851 			rq_cfg[i].rq_id = 0xffff;
10852 			rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
10853 			rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
10854 			rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
10855 			rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
10856 		}
10857 
10858 		/* Set the rq_id for each, in order of RQ definition */
10859 		for (i = 0; i < hw->hw_rq_count; i++) {
10860 			if (i >= ARRAY_SIZE(rq_cfg)) {
10861 				ocs_log_warn(hw->os, "more RQs than REG_FCFI filter entries\n");
10862 				break;
10863 			}
10864 			rq_cfg[i].rq_id = hw->hw_rq[i]->hdr->id;
10865 		}
10866 
10867 		if (!data) {
10868 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10869 			break;
10870 		}
10871 
10872 		if (hw->hw_mrq_count) {
10873 			if (OCS_HW_RTN_SUCCESS != ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE,
10874 				 domain->vlan_id, domain->fcf)) {
10875 				ocs_log_err(hw->os, "REG_FCFI_MRQ format failure\n");
10876 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10877 				break;
10878 			}
10879 
10880 		} else {
10881 			if (0 == sli_cmd_reg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf,
10882 						rq_cfg, domain->vlan_id)) {
10883 				ocs_log_err(hw->os, "REG_FCFI format failure\n");
10884 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10885 				break;
10886 			}
10887 		}
10888 
10889 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10890 			ocs_log_err(hw->os, "REG_FCFI command failure\n");
10891 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10892 			break;
10893 		}
10894 		break;
10895 	}
10896 	case OCS_EVT_EXIT:
10897 		break;
10898 	case OCS_EVT_RESPONSE:
10899 		if (!data) {
10900 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10901 			break;
10902 		}
10903 
10904 		domain->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)data)->fcfi;
10905 
10906 		/*
10907 		 * IF_TYPE 0 devices do not support explicit VFI and VPI initialization
10908 		 * and instead rely on implicit initialization during VFI registration.
10909 		 * Short circuit normal processing here for those devices.
10910 		 */
10911 		if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10912 			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10913 		} else {
10914 			ocs_sm_transition(ctx, __ocs_hw_domain_alloc_init_vfi, data);
10915 		}
10916 		break;
10917 	case OCS_EVT_ERROR:
10918 		ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10919 		break;
10920 	default:
10921 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10922 		break;
10923 	}
10924 
10925 	return NULL;
10926 }
10927 
10928 static void *
__ocs_hw_domain_init(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10929 __ocs_hw_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10930 {
10931 	ocs_domain_t	*domain = ctx->app;
10932 	ocs_hw_t	*hw = domain->hw;
10933 
10934 	smtrace("domain");
10935 
10936 	switch (evt) {
10937 	case OCS_EVT_ENTER:
10938 		if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
10939 			/*
10940 			 * For FC, the HW alread registered a FCFI
10941 			 * Copy FCF information into the domain and jump to INIT_VFI
10942 			 */
10943 			domain->fcf_indicator = hw->fcf_indicator;
10944 			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_init_vfi, data);
10945 		} else {
10946 			ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_reg_fcfi, data);
10947 		}
10948 		break;
10949 	default:
10950 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10951 		break;
10952 	}
10953 
10954 	return NULL;
10955 }
10956 
10957 static void *
__ocs_hw_domain_free_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10958 __ocs_hw_domain_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10959 {
10960 	ocs_domain_t	*domain = ctx->app;
10961 
10962 	smtrace("domain");
10963 
10964 	switch (evt) {
10965 	case OCS_EVT_ENTER:
10966 		if (domain != NULL) {
10967 			ocs_hw_t	*hw = domain->hw;
10968 
10969 			ocs_hw_domain_del(hw, domain);
10970 
10971 			if (hw->callback.domain != NULL) {
10972 				hw->callback.domain(hw->args.domain,
10973 						     OCS_HW_DOMAIN_FREE_FAIL,
10974 						     domain);
10975 			}
10976 		}
10977 
10978 		/* free command buffer */
10979 		if (data != NULL) {
10980 			ocs_free(domain != NULL ? domain->hw->os : NULL, data, SLI4_BMBX_SIZE);
10981 		}
10982 		break;
10983 	case OCS_EVT_EXIT:
10984 		break;
10985 	default:
10986 		__ocs_hw_domain_common(__func__, ctx, evt, data);
10987 		break;
10988 	}
10989 
10990 	return NULL;
10991 }
10992 
10993 static void *
__ocs_hw_domain_freed(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10994 __ocs_hw_domain_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10995 {
10996 	ocs_domain_t	*domain = ctx->app;
10997 
10998 	smtrace("domain");
10999 
11000 	switch (evt) {
11001 	case OCS_EVT_ENTER:
11002 		/* Free DMA and mailbox buffer */
11003 		if (domain != NULL) {
11004 			ocs_hw_t *hw = domain->hw;
11005 
11006 			/* free VFI resource */
11007 			sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI,
11008 					  domain->indicator);
11009 
11010 			ocs_hw_domain_del(hw, domain);
11011 
11012 			/* inform registered callbacks */
11013 			if (hw->callback.domain != NULL) {
11014 				hw->callback.domain(hw->args.domain,
11015 						     OCS_HW_DOMAIN_FREE_OK,
11016 						     domain);
11017 			}
11018 		}
11019 		if (data != NULL) {
11020 			ocs_free(NULL, data, SLI4_BMBX_SIZE);
11021 		}
11022 		break;
11023 	case OCS_EVT_EXIT:
11024 		break;
11025 	default:
11026 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11027 		break;
11028 	}
11029 
11030 	return NULL;
11031 }
11032 
11033 
11034 static void *
__ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11035 __ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11036 {
11037 	ocs_domain_t	*domain = ctx->app;
11038 	ocs_hw_t	*hw = domain->hw;
11039 
11040 	smtrace("domain");
11041 
11042 	switch (evt) {
11043 	case OCS_EVT_ENTER:
11044 		/* if we're in the middle of a teardown, skip sending rediscover */
11045 		if (hw->state == OCS_HW_STATE_TEARDOWN_IN_PROGRESS) {
11046 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11047 			break;
11048 		}
11049 		if (0 == sli_cmd_fcoe_rediscover_fcf(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf)) {
11050 			ocs_log_err(hw->os, "REDISCOVER_FCF format failure\n");
11051 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11052 			break;
11053 		}
11054 
11055 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11056 			ocs_log_err(hw->os, "REDISCOVER_FCF command failure\n");
11057 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11058 		}
11059 		break;
11060 	case OCS_EVT_RESPONSE:
11061 	case OCS_EVT_ERROR:
11062 		/* REDISCOVER_FCF can fail if none exist */
11063 		ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11064 		break;
11065 	case OCS_EVT_EXIT:
11066 		break;
11067 	default:
11068 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11069 		break;
11070 	}
11071 
11072 	return NULL;
11073 }
11074 
11075 static void *
__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11076 __ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11077 {
11078 	ocs_domain_t	*domain = ctx->app;
11079 	ocs_hw_t	*hw = domain->hw;
11080 
11081 	smtrace("domain");
11082 
11083 	switch (evt) {
11084 	case OCS_EVT_ENTER:
11085 		if (data == NULL) {
11086 			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11087 			if (!data) {
11088 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11089 				break;
11090 			}
11091 		}
11092 
11093 		if (0 == sli_cmd_unreg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf_indicator)) {
11094 			ocs_log_err(hw->os, "UNREG_FCFI format failure\n");
11095 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11096 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11097 			break;
11098 		}
11099 
11100 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11101 			ocs_log_err(hw->os, "UNREG_FCFI command failure\n");
11102 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11103 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11104 			break;
11105 		}
11106 		break;
11107 	case OCS_EVT_RESPONSE:
11108 		if (domain->req_rediscover_fcf) {
11109 			domain->req_rediscover_fcf = FALSE;
11110 			ocs_sm_transition(ctx, __ocs_hw_domain_free_redisc_fcf, data);
11111 		} else {
11112 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11113 		}
11114 		break;
11115 	case OCS_EVT_ERROR:
11116 		ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11117 		break;
11118 	case OCS_EVT_EXIT:
11119 		break;
11120 	default:
11121 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11122 		break;
11123 	}
11124 
11125 	return NULL;
11126 }
11127 
11128 static void *
__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11129 __ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11130 {
11131 	ocs_domain_t	*domain = ctx->app;
11132 	ocs_hw_t	*hw = domain->hw;
11133 	uint8_t		is_fc = FALSE;
11134 
11135 	smtrace("domain");
11136 
11137 	is_fc = (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC);
11138 
11139 	switch (evt) {
11140 	case OCS_EVT_ENTER:
11141 		if (data == NULL) {
11142 			data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11143 			if (!data) {
11144 				ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11145 				break;
11146 			}
11147 		}
11148 
11149 		if (0 == sli_cmd_unreg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain,
11150 					SLI4_UNREG_TYPE_DOMAIN)) {
11151 			ocs_log_err(hw->os, "UNREG_VFI format failure\n");
11152 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11153 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11154 			break;
11155 		}
11156 
11157 		if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11158 			ocs_log_err(hw->os, "UNREG_VFI command failure\n");
11159 			ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11160 			ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11161 			break;
11162 		}
11163 		break;
11164 	case OCS_EVT_ERROR:
11165 		if (is_fc) {
11166 			ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11167 		} else {
11168 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11169 		}
11170 		break;
11171 	case OCS_EVT_RESPONSE:
11172 		if (is_fc) {
11173 			ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11174 		} else {
11175 			ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11176 		}
11177 		break;
11178 	default:
11179 		__ocs_hw_domain_common(__func__, ctx, evt, data);
11180 		break;
11181 	}
11182 
11183 	return NULL;
11184 }
11185 
11186 /* callback for domain alloc/attach/free */
11187 static int32_t
__ocs_hw_domain_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11188 __ocs_hw_domain_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11189 {
11190 	ocs_domain_t	*domain = arg;
11191 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11192 	ocs_sm_event_t	evt;
11193 
11194 	if (status || hdr->status) {
11195 		ocs_log_debug(hw->os, "bad status vfi=%#x st=%x hdr=%x\n",
11196 			      domain->indicator, status, hdr->status);
11197 		evt = OCS_EVT_ERROR;
11198 	} else {
11199 		evt = OCS_EVT_RESPONSE;
11200 	}
11201 
11202 	ocs_sm_post_event(&domain->sm, evt, mqe);
11203 
11204 	return 0;
11205 }
11206 
11207 static int32_t
target_wqe_timer_nop_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11208 target_wqe_timer_nop_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11209 {
11210 	ocs_hw_io_t *io = NULL;
11211 	ocs_hw_io_t *io_next = NULL;
11212 	uint64_t ticks_current = ocs_get_os_ticks();
11213 	uint32_t sec_elapsed;
11214 
11215 	sli4_mbox_command_header_t	*hdr = (sli4_mbox_command_header_t *)mqe;
11216 
11217 	if (status || hdr->status) {
11218 		ocs_log_debug(hw->os, "bad status st=%x hdr=%x\n",
11219 			      status, hdr->status);
11220 		/* go ahead and proceed with wqe timer checks... */
11221 	}
11222 
11223 	/* loop through active WQE list and check for timeouts */
11224 	ocs_lock(&hw->io_lock);
11225 		ocs_list_foreach_safe(&hw->io_timed_wqe, io, io_next) {
11226 			sec_elapsed = ((ticks_current - io->submit_ticks) / ocs_get_os_tick_freq());
11227 
11228 			/*
11229 			 * If elapsed time > timeout, abort it. No need to check type since
11230 			 * it wouldn't be on this list unless it was a target WQE
11231 			 */
11232 			if (sec_elapsed > io->tgt_wqe_timeout) {
11233 				ocs_log_test(hw->os, "IO timeout xri=0x%x tag=0x%x type=%d\n",
11234 					     io->indicator, io->reqtag, io->type);
11235 
11236 				/* remove from active_wqe list so won't try to abort again */
11237 				ocs_list_remove(&hw->io_timed_wqe, io);
11238 
11239 				/* save status of "timed out" for when abort completes */
11240 				io->status_saved = 1;
11241 				io->saved_status = SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT;
11242 				io->saved_ext = 0;
11243 				io->saved_len = 0;
11244 
11245 				/* now abort outstanding IO */
11246 				ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
11247 			}
11248 			/*
11249 			 * need to go through entire list since each IO could have a
11250 			 * different timeout value
11251 			 */
11252 		}
11253 	ocs_unlock(&hw->io_lock);
11254 
11255 	/* if we're not in the middle of shutting down, schedule next timer */
11256 	if (!hw->active_wqe_timer_shutdown) {
11257 		ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw, OCS_HW_WQ_TIMER_PERIOD_MS);
11258 	}
11259 	hw->in_active_wqe_timer = FALSE;
11260 	return 0;
11261 }
11262 
11263 static void
target_wqe_timer_cb(void * arg)11264 target_wqe_timer_cb(void *arg)
11265 {
11266 	ocs_hw_t *hw = (ocs_hw_t *)arg;
11267 
11268 	/* delete existing timer; will kick off new timer after checking wqe timeouts */
11269 	hw->in_active_wqe_timer = TRUE;
11270 	ocs_del_timer(&hw->wqe_timer);
11271 
11272 	/* Forward timer callback to execute in the mailbox completion processing context */
11273 	if (ocs_hw_async_call(hw, target_wqe_timer_nop_cb, hw)) {
11274 		ocs_log_test(hw->os, "ocs_hw_async_call failed\n");
11275 	}
11276 }
11277 
11278 static void
shutdown_target_wqe_timer(ocs_hw_t * hw)11279 shutdown_target_wqe_timer(ocs_hw_t *hw)
11280 {
11281 	uint32_t	iters = 100;
11282 
11283 	if (hw->config.emulate_tgt_wqe_timeout) {
11284 		/* request active wqe timer shutdown, then wait for it to complete */
11285 		hw->active_wqe_timer_shutdown = TRUE;
11286 
11287 		/* delete WQE timer and wait for timer handler to complete (if necessary) */
11288 		ocs_del_timer(&hw->wqe_timer);
11289 
11290 		/* now wait for timer handler to complete (if necessary) */
11291 		while (hw->in_active_wqe_timer && iters) {
11292 			/*
11293 			 * if we happen to have just sent NOP mailbox command, make sure
11294 			 * completions are being processed
11295 			 */
11296 			ocs_hw_flush(hw);
11297 			iters--;
11298 		}
11299 
11300 		if (iters == 0) {
11301 			ocs_log_test(hw->os, "Failed to shutdown active wqe timer\n");
11302 		}
11303 	}
11304 }
11305 
11306 /**
11307  * @brief Determine if HW IO is owned by the port.
11308  *
11309  * @par Description
11310  * Determines if the given HW IO has been posted to the chip.
11311  *
11312  * @param hw Hardware context allocated by the caller.
11313  * @param io HW IO.
11314  *
11315  * @return Returns TRUE if given HW IO is port-owned.
11316  */
11317 uint8_t
ocs_hw_is_io_port_owned(ocs_hw_t * hw,ocs_hw_io_t * io)11318 ocs_hw_is_io_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
11319 {
11320 	/* Check to see if this is a port owned XRI */
11321 	return io->is_port_owned;
11322 }
11323 
11324 /**
11325  * @brief Return TRUE if exchange is port-owned.
11326  *
11327  * @par Description
11328  * Test to see if the xri is a port-owned xri.
11329  *
11330  * @param hw Hardware context.
11331  * @param xri Exchange indicator.
11332  *
11333  * @return Returns TRUE if XRI is a port owned XRI.
11334  */
11335 
11336 uint8_t
ocs_hw_is_xri_port_owned(ocs_hw_t * hw,uint32_t xri)11337 ocs_hw_is_xri_port_owned(ocs_hw_t *hw, uint32_t xri)
11338 {
11339 	ocs_hw_io_t *io = ocs_hw_io_lookup(hw, xri);
11340 	return (io == NULL ? FALSE : io->is_port_owned);
11341 }
11342 
11343 /**
11344  * @brief Returns an XRI from the port owned list to the host.
11345  *
11346  * @par Description
11347  * Used when the POST_XRI command fails as well as when the RELEASE_XRI completes.
11348  *
11349  * @param hw Hardware context.
11350  * @param xri_base The starting XRI number.
11351  * @param xri_count The number of XRIs to free from the base.
11352  */
11353 static void
ocs_hw_reclaim_xri(ocs_hw_t * hw,uint16_t xri_base,uint16_t xri_count)11354 ocs_hw_reclaim_xri(ocs_hw_t *hw, uint16_t xri_base, uint16_t xri_count)
11355 {
11356 	ocs_hw_io_t	*io;
11357 	uint32_t i;
11358 
11359 	for (i = 0; i < xri_count; i++) {
11360 		io = ocs_hw_io_lookup(hw, xri_base + i);
11361 
11362 		/*
11363 		 * if this is an auto xfer rdy XRI, then we need to release any
11364 		 * buffer attached to the XRI before moving the XRI back to the free pool.
11365 		 */
11366 		if (hw->auto_xfer_rdy_enabled) {
11367 			ocs_hw_rqpair_auto_xfer_rdy_move_to_host(hw, io);
11368 		}
11369 
11370 		ocs_lock(&hw->io_lock);
11371 			ocs_list_remove(&hw->io_port_owned, io);
11372 			io->is_port_owned = 0;
11373 			ocs_list_add_tail(&hw->io_free, io);
11374 		ocs_unlock(&hw->io_lock);
11375 	}
11376 }
11377 
11378 /**
11379  * @brief Called when the POST_XRI command completes.
11380  *
11381  * @par Description
11382  * Free the mailbox command buffer and reclaim the XRIs on failure.
11383  *
11384  * @param hw Hardware context.
11385  * @param status Status field from the mbox completion.
11386  * @param mqe Mailbox response structure.
11387  * @param arg Pointer to a callback function that signals the caller that the command is done.
11388  *
11389  * @return Returns 0.
11390  */
11391 static int32_t
ocs_hw_cb_post_xri(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11392 ocs_hw_cb_post_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11393 {
11394 	sli4_cmd_post_xri_t	*post_xri = (sli4_cmd_post_xri_t*)mqe;
11395 
11396 	/* Reclaim the XRIs as host owned if the command fails */
11397 	if (status != 0) {
11398 		ocs_log_debug(hw->os, "Status 0x%x for XRI base 0x%x, cnt =x%x\n",
11399 			      status, post_xri->xri_base, post_xri->xri_count);
11400 		ocs_hw_reclaim_xri(hw, post_xri->xri_base, post_xri->xri_count);
11401 	}
11402 
11403 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11404 	return 0;
11405 }
11406 
11407 /**
11408  * @brief Issues a mailbox command to move XRIs from the host-controlled pool to the port.
11409  *
11410  * @param hw Hardware context.
11411  * @param xri_start The starting XRI to post.
11412  * @param num_to_post The number of XRIs to post.
11413  *
11414  * @return Returns OCS_HW_RTN_NO_MEMORY, OCS_HW_RTN_ERROR, or OCS_HW_RTN_SUCCESS.
11415  */
11416 
11417 static ocs_hw_rtn_e
ocs_hw_post_xri(ocs_hw_t * hw,uint32_t xri_start,uint32_t num_to_post)11418 ocs_hw_post_xri(ocs_hw_t *hw, uint32_t xri_start, uint32_t num_to_post)
11419 {
11420 	uint8_t	*post_xri;
11421 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11422 
11423 	/* Since we need to allocate for mailbox queue, just always allocate */
11424 	post_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11425 	if (post_xri == NULL) {
11426 		ocs_log_err(hw->os, "no buffer for command\n");
11427 		return OCS_HW_RTN_NO_MEMORY;
11428 	}
11429 
11430 	/* Register the XRIs */
11431 	if (sli_cmd_post_xri(&hw->sli, post_xri, SLI4_BMBX_SIZE,
11432 			     xri_start, num_to_post)) {
11433 		rc = ocs_hw_command(hw, post_xri, OCS_CMD_NOWAIT, ocs_hw_cb_post_xri, NULL);
11434 		if (rc != OCS_HW_RTN_SUCCESS) {
11435 			ocs_free(hw->os, post_xri, SLI4_BMBX_SIZE);
11436 			ocs_log_err(hw->os, "post_xri failed\n");
11437 		}
11438 	}
11439 	return rc;
11440 }
11441 
11442 /**
11443  * @brief Move XRIs from the host-controlled pool to the port.
11444  *
11445  * @par Description
11446  * Removes IOs from the free list and moves them to the port.
11447  *
11448  * @param hw Hardware context.
11449  * @param num_xri The number of XRIs being requested to move to the chip.
11450  *
11451  * @return Returns the number of XRIs that were moved.
11452  */
11453 
11454 uint32_t
ocs_hw_xri_move_to_port_owned(ocs_hw_t * hw,uint32_t num_xri)11455 ocs_hw_xri_move_to_port_owned(ocs_hw_t *hw, uint32_t num_xri)
11456 {
11457 	ocs_hw_io_t	*io;
11458 	uint32_t i;
11459 	uint32_t num_posted = 0;
11460 
11461 	/*
11462 	 * Note: We cannot use ocs_hw_io_alloc() because that would place the
11463 	 *       IO on the io_inuse list. We need to move from the io_free to
11464 	 *       the io_port_owned list.
11465 	 */
11466 	ocs_lock(&hw->io_lock);
11467 
11468 	for (i = 0; i < num_xri; i++) {
11469 
11470 		if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
11471 			ocs_hw_rtn_e rc;
11472 
11473 			/*
11474 			 * if this is an auto xfer rdy XRI, then we need to attach a
11475 			 * buffer to the XRI before submitting it to the chip. If a
11476 			 * buffer is unavailable, then we cannot post it, so return it
11477 			 * to the free pool.
11478 			 */
11479 			if (hw->auto_xfer_rdy_enabled) {
11480 				/* Note: uses the IO lock to get the auto xfer rdy buffer */
11481 				ocs_unlock(&hw->io_lock);
11482 				rc = ocs_hw_rqpair_auto_xfer_rdy_move_to_port(hw, io);
11483 				ocs_lock(&hw->io_lock);
11484 				if (rc != OCS_HW_RTN_SUCCESS) {
11485 					ocs_list_add_head(&hw->io_free, io);
11486 					break;
11487 				}
11488 			}
11489 			ocs_lock_init(hw->os, &io->axr_lock, "HW_axr_lock[%d]", io->indicator);
11490 			io->is_port_owned = 1;
11491 			ocs_list_add_tail(&hw->io_port_owned, io);
11492 
11493 			/* Post XRI */
11494 			if (ocs_hw_post_xri(hw, io->indicator, 1) != OCS_HW_RTN_SUCCESS ) {
11495 				ocs_hw_reclaim_xri(hw, io->indicator, i);
11496 				break;
11497 			}
11498 			num_posted++;
11499 		} else {
11500 			/* no more free XRIs */
11501 			break;
11502 		}
11503 	}
11504 	ocs_unlock(&hw->io_lock);
11505 
11506 	return num_posted;
11507 }
11508 
11509 /**
11510  * @brief Called when the RELEASE_XRI command completes.
11511  *
11512  * @par Description
11513  * Move the IOs back to the free pool on success.
11514  *
11515  * @param hw Hardware context.
11516  * @param status Status field from the mbox completion.
11517  * @param mqe Mailbox response structure.
11518  * @param arg Pointer to a callback function that signals the caller that the command is done.
11519  *
11520  * @return Returns 0.
11521  */
11522 static int32_t
ocs_hw_cb_release_xri(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11523 ocs_hw_cb_release_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void  *arg)
11524 {
11525 	sli4_cmd_release_xri_t	*release_xri = (sli4_cmd_release_xri_t*)mqe;
11526 	uint8_t i;
11527 
11528 	/* Reclaim the XRIs as host owned if the command fails */
11529 	if (status != 0) {
11530 		ocs_log_err(hw->os, "Status 0x%x\n", status);
11531 	} else {
11532 		for (i = 0; i < release_xri->released_xri_count; i++) {
11533 			uint16_t xri = ((i & 1) == 0 ? release_xri->xri_tbl[i/2].xri_tag0 :
11534 					release_xri->xri_tbl[i/2].xri_tag1);
11535 			ocs_hw_reclaim_xri(hw, xri, 1);
11536 		}
11537 	}
11538 
11539 	ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11540 	return 0;
11541 }
11542 
11543 /**
11544  * @brief Move XRIs from the port-controlled pool to the host.
11545  *
11546  * Requests XRIs from the FW to return to the host-owned pool.
11547  *
11548  * @param hw Hardware context.
11549  * @param num_xri The number of XRIs being requested to moved from the chip.
11550  *
11551  * @return Returns 0 for success, or a negative error code value for failure.
11552  */
11553 
11554 ocs_hw_rtn_e
ocs_hw_xri_move_to_host_owned(ocs_hw_t * hw,uint8_t num_xri)11555 ocs_hw_xri_move_to_host_owned(ocs_hw_t *hw, uint8_t num_xri)
11556 {
11557 	uint8_t	*release_xri;
11558 	ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11559 
11560 	/* non-local buffer required for mailbox queue */
11561 	release_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11562 	if (release_xri == NULL) {
11563 		ocs_log_err(hw->os, "no buffer for command\n");
11564 		return OCS_HW_RTN_NO_MEMORY;
11565 	}
11566 
11567 	/* release the XRIs */
11568 	if (sli_cmd_release_xri(&hw->sli, release_xri, SLI4_BMBX_SIZE, num_xri)) {
11569 		rc = ocs_hw_command(hw, release_xri, OCS_CMD_NOWAIT, ocs_hw_cb_release_xri, NULL);
11570 		if (rc != OCS_HW_RTN_SUCCESS) {
11571 			ocs_log_err(hw->os, "release_xri failed\n");
11572 		}
11573 	}
11574 	/* If we are polling or an error occurred, then free the mailbox buffer */
11575 	if (release_xri != NULL && rc != OCS_HW_RTN_SUCCESS) {
11576 		ocs_free(hw->os, release_xri, SLI4_BMBX_SIZE);
11577 	}
11578 	return rc;
11579 }
11580 
11581 
11582 /**
11583  * @brief Allocate an ocs_hw_rx_buffer_t array.
11584  *
11585  * @par Description
11586  * An ocs_hw_rx_buffer_t array is allocated, along with the required DMA memory.
11587  *
11588  * @param hw Pointer to HW object.
11589  * @param rqindex RQ index for this buffer.
11590  * @param count Count of buffers in array.
11591  * @param size Size of buffer.
11592  *
11593  * @return Returns the pointer to the allocated ocs_hw_rq_buffer_t array.
11594  */
11595 static ocs_hw_rq_buffer_t *
ocs_hw_rx_buffer_alloc(ocs_hw_t * hw,uint32_t rqindex,uint32_t count,uint32_t size)11596 ocs_hw_rx_buffer_alloc(ocs_hw_t *hw, uint32_t rqindex, uint32_t count, uint32_t size)
11597 {
11598 	ocs_t *ocs = hw->os;
11599 	ocs_hw_rq_buffer_t *rq_buf = NULL;
11600 	ocs_hw_rq_buffer_t *prq;
11601 	uint32_t i;
11602 
11603 	if (count != 0) {
11604 		rq_buf = ocs_malloc(hw->os, sizeof(*rq_buf) * count, OCS_M_NOWAIT | OCS_M_ZERO);
11605 		if (rq_buf == NULL) {
11606 			ocs_log_err(hw->os, "Failure to allocate unsolicited DMA trackers\n");
11607 			return NULL;
11608 		}
11609 
11610 		for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
11611 			prq->rqindex = rqindex;
11612 			if (ocs_dma_alloc(ocs, &prq->dma, size, OCS_MIN_DMA_ALIGNMENT)) {
11613 				ocs_log_err(hw->os, "DMA allocation failed\n");
11614 				ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11615 				rq_buf = NULL;
11616 				break;
11617 			}
11618 		}
11619 	}
11620 	return rq_buf;
11621 }
11622 
11623 /**
11624  * @brief Free an ocs_hw_rx_buffer_t array.
11625  *
11626  * @par Description
11627  * The ocs_hw_rx_buffer_t array is freed, along with allocated DMA memory.
11628  *
11629  * @param hw Pointer to HW object.
11630  * @param rq_buf Pointer to ocs_hw_rx_buffer_t array.
11631  * @param count Count of buffers in array.
11632  *
11633  * @return None.
11634  */
11635 static void
ocs_hw_rx_buffer_free(ocs_hw_t * hw,ocs_hw_rq_buffer_t * rq_buf,uint32_t count)11636 ocs_hw_rx_buffer_free(ocs_hw_t *hw, ocs_hw_rq_buffer_t *rq_buf, uint32_t count)
11637 {
11638 	ocs_t *ocs = hw->os;
11639 	uint32_t i;
11640 	ocs_hw_rq_buffer_t *prq;
11641 
11642 	if (rq_buf != NULL) {
11643 		for (i = 0, prq = rq_buf; i < count; i++, prq++) {
11644 			ocs_dma_free(ocs, &prq->dma);
11645 		}
11646 		ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11647 	}
11648 }
11649 
11650 /**
11651  * @brief Allocate the RQ data buffers.
11652  *
11653  * @param hw Pointer to HW object.
11654  *
11655  * @return Returns 0 on success, or a non-zero value on failure.
11656  */
11657 ocs_hw_rtn_e
ocs_hw_rx_allocate(ocs_hw_t * hw)11658 ocs_hw_rx_allocate(ocs_hw_t *hw)
11659 {
11660 	ocs_t *ocs = hw->os;
11661 	uint32_t i;
11662 	int32_t rc = OCS_HW_RTN_SUCCESS;
11663 	uint32_t rqindex = 0;
11664 	hw_rq_t *rq;
11665 	uint32_t hdr_size = OCS_HW_RQ_SIZE_HDR;
11666 	uint32_t payload_size = hw->config.rq_default_buffer_size;
11667 
11668 	rqindex = 0;
11669 
11670 	for (i = 0; i < hw->hw_rq_count; i++) {
11671 		rq = hw->hw_rq[i];
11672 
11673 		/* Allocate header buffers */
11674 		rq->hdr_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, hdr_size);
11675 		if (rq->hdr_buf == NULL) {
11676 			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc hdr_buf failed\n");
11677 			rc = OCS_HW_RTN_ERROR;
11678 			break;
11679 		}
11680 
11681 		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d header  %4d by %4d bytes\n", i, rq->hdr->id,
11682 			      rq->entry_count, hdr_size);
11683 
11684 		rqindex++;
11685 
11686 		/* Allocate payload buffers */
11687 		rq->payload_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, payload_size);
11688 		if (rq->payload_buf == NULL) {
11689 			ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc fb_buf failed\n");
11690 			rc = OCS_HW_RTN_ERROR;
11691 			break;
11692 		}
11693 		ocs_log_debug(hw->os, "rq[%2d] rq_id %02d default %4d by %4d bytes\n", i, rq->data->id,
11694 			      rq->entry_count, payload_size);
11695 		rqindex++;
11696 	}
11697 
11698 	return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
11699 }
11700 
11701 /**
11702  * @brief Post the RQ data buffers to the chip.
11703  *
11704  * @param hw Pointer to HW object.
11705  *
11706  * @return Returns 0 on success, or a non-zero value on failure.
11707  */
11708 ocs_hw_rtn_e
ocs_hw_rx_post(ocs_hw_t * hw)11709 ocs_hw_rx_post(ocs_hw_t *hw)
11710 {
11711 	uint32_t i;
11712 	uint32_t idx;
11713 	uint32_t rq_idx;
11714 	int32_t rc = 0;
11715 
11716 	/*
11717 	 * In RQ pair mode, we MUST post the header and payload buffer at the
11718 	 * same time.
11719 	 */
11720 	for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
11721 		hw_rq_t *rq = hw->hw_rq[rq_idx];
11722 
11723 		for (i = 0; i < rq->entry_count-1; i++) {
11724 			ocs_hw_sequence_t *seq = ocs_array_get(hw->seq_pool, idx++);
11725 			ocs_hw_assert(seq != NULL);
11726 
11727 			seq->header = &rq->hdr_buf[i];
11728 
11729 			seq->payload = &rq->payload_buf[i];
11730 
11731 			rc = ocs_hw_sequence_free(hw, seq);
11732 			if (rc) {
11733 				break;
11734 			}
11735 		}
11736 		if (rc) {
11737 			break;
11738 		}
11739 	}
11740 
11741 	return rc;
11742 }
11743 
11744 /**
11745  * @brief Free the RQ data buffers.
11746  *
11747  * @param hw Pointer to HW object.
11748  *
11749  */
11750 void
ocs_hw_rx_free(ocs_hw_t * hw)11751 ocs_hw_rx_free(ocs_hw_t *hw)
11752 {
11753 	hw_rq_t *rq;
11754 	uint32_t i;
11755 
11756 	/* Free hw_rq buffers */
11757 	for (i = 0; i < hw->hw_rq_count; i++) {
11758 		rq = hw->hw_rq[i];
11759 		if (rq != NULL) {
11760 			ocs_hw_rx_buffer_free(hw, rq->hdr_buf, rq->entry_count);
11761 			rq->hdr_buf = NULL;
11762 			ocs_hw_rx_buffer_free(hw, rq->payload_buf, rq->entry_count);
11763 			rq->payload_buf = NULL;
11764 		}
11765 	}
11766 }
11767 
11768 /**
11769  * @brief HW async call context structure.
11770  */
11771 typedef struct {
11772 	ocs_hw_async_cb_t callback;
11773 	void *arg;
11774 	uint8_t cmd[SLI4_BMBX_SIZE];
11775 } ocs_hw_async_call_ctx_t;
11776 
11777 /**
11778  * @brief HW async callback handler
11779  *
11780  * @par Description
11781  * This function is called when the NOP mailbox command completes.  The callback stored
11782  * in the requesting context is invoked.
11783  *
11784  * @param hw Pointer to HW object.
11785  * @param status Completion status.
11786  * @param mqe Pointer to mailbox completion queue entry.
11787  * @param arg Caller-provided argument.
11788  *
11789  * @return None.
11790  */
11791 static void
ocs_hw_async_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11792 ocs_hw_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11793 {
11794 	ocs_hw_async_call_ctx_t *ctx = arg;
11795 
11796 	if (ctx != NULL) {
11797 		if (ctx->callback != NULL) {
11798 			(*ctx->callback)(hw, status, mqe, ctx->arg);
11799 		}
11800 		ocs_free(hw->os, ctx, sizeof(*ctx));
11801 	}
11802 }
11803 
11804 /**
11805  * @brief Make an async callback using NOP mailbox command
11806  *
11807  * @par Description
11808  * Post a NOP mailbox command; the callback with argument is invoked upon completion
11809  * while in the event processing context.
11810  *
11811  * @param hw Pointer to HW object.
11812  * @param callback Pointer to callback function.
11813  * @param arg Caller-provided callback.
11814  *
11815  * @return Returns 0 on success, or a negative error code value on failure.
11816  */
11817 int32_t
ocs_hw_async_call(ocs_hw_t * hw,ocs_hw_async_cb_t callback,void * arg)11818 ocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
11819 {
11820 	ocs_hw_async_call_ctx_t *ctx;
11821 
11822 	/*
11823 	 * Allocate a callback context (which includes the mailbox command buffer), we need
11824 	 * this to be persistent as the mailbox command submission may be queued and executed later
11825 	 * execution.
11826 	 */
11827 	ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
11828 	if (ctx == NULL) {
11829 		ocs_log_err(hw->os, "failed to malloc async call context\n");
11830 		return OCS_HW_RTN_NO_MEMORY;
11831 	}
11832 	ctx->callback = callback;
11833 	ctx->arg = arg;
11834 
11835 	/* Build and send a NOP mailbox command */
11836 	if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
11837 		ocs_log_err(hw->os, "COMMON_NOP format failure\n");
11838 		ocs_free(hw->os, ctx, sizeof(*ctx));
11839 		return OCS_HW_RTN_ERROR;
11840 	}
11841 
11842 	if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
11843 		ocs_log_err(hw->os, "COMMON_NOP command failure\n");
11844 		ocs_free(hw->os, ctx, sizeof(*ctx));
11845 		return OCS_HW_RTN_ERROR;
11846 	}
11847 	return OCS_HW_RTN_SUCCESS;
11848 }
11849 
11850 /**
11851  * @brief Initialize the reqtag pool.
11852  *
11853  * @par Description
11854  * The WQ request tag pool is initialized.
11855  *
11856  * @param hw Pointer to HW object.
11857  *
11858  * @return Returns 0 on success, or a negative error code value on failure.
11859  */
11860 ocs_hw_rtn_e
ocs_hw_reqtag_init(ocs_hw_t * hw)11861 ocs_hw_reqtag_init(ocs_hw_t *hw)
11862 {
11863 	if (hw->wq_reqtag_pool == NULL) {
11864 		hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
11865 		if (hw->wq_reqtag_pool == NULL) {
11866 			ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
11867 			return OCS_HW_RTN_NO_MEMORY;
11868 		}
11869 	}
11870 	ocs_hw_reqtag_reset(hw);
11871 	return OCS_HW_RTN_SUCCESS;
11872 }
11873 
11874 /**
11875  * @brief Allocate a WQ request tag.
11876  *
11877  * Allocate and populate a WQ request tag from the WQ request tag pool.
11878  *
11879  * @param hw Pointer to HW object.
11880  * @param callback Callback function.
11881  * @param arg Pointer to callback argument.
11882  *
11883  * @return Returns pointer to allocated WQ request tag, or NULL if object cannot be allocated.
11884  */
11885 hw_wq_callback_t *
ocs_hw_reqtag_alloc(ocs_hw_t * hw,void (* callback)(void * arg,uint8_t * cqe,int32_t status),void * arg)11886 ocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
11887 {
11888 	hw_wq_callback_t *wqcb;
11889 
11890 	ocs_hw_assert(callback != NULL);
11891 
11892 	wqcb = ocs_pool_get(hw->wq_reqtag_pool);
11893 	if (wqcb != NULL) {
11894 		ocs_hw_assert(wqcb->callback == NULL);
11895 		wqcb->callback = callback;
11896 		wqcb->arg = arg;
11897 	}
11898 	return wqcb;
11899 }
11900 
11901 /**
11902  * @brief Free a WQ request tag.
11903  *
11904  * Free the passed in WQ request tag.
11905  *
11906  * @param hw Pointer to HW object.
11907  * @param wqcb Pointer to WQ request tag object to free.
11908  *
11909  * @return None.
11910  */
11911 void
ocs_hw_reqtag_free(ocs_hw_t * hw,hw_wq_callback_t * wqcb)11912 ocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
11913 {
11914 	ocs_hw_assert(wqcb->callback != NULL);
11915 	wqcb->callback = NULL;
11916 	wqcb->arg = NULL;
11917 	ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11918 }
11919 
11920 /**
11921  * @brief Return WQ request tag by index.
11922  *
11923  * @par Description
11924  * Return pointer to WQ request tag object given an index.
11925  *
11926  * @param hw Pointer to HW object.
11927  * @param instance_index Index of WQ request tag to return.
11928  *
11929  * @return Pointer to WQ request tag, or NULL.
11930  */
11931 hw_wq_callback_t *
ocs_hw_reqtag_get_instance(ocs_hw_t * hw,uint32_t instance_index)11932 ocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
11933 {
11934 	hw_wq_callback_t *wqcb;
11935 
11936 	wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
11937 	if (wqcb == NULL) {
11938 		ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
11939 	}
11940 	return wqcb;
11941 }
11942 
11943 /**
11944  * @brief Reset the WQ request tag pool.
11945  *
11946  * @par Description
11947  * Reset the WQ request tag pool, returning all to the free list.
11948  *
11949  * @param hw pointer to HW object.
11950  *
11951  * @return None.
11952  */
11953 void
ocs_hw_reqtag_reset(ocs_hw_t * hw)11954 ocs_hw_reqtag_reset(ocs_hw_t *hw)
11955 {
11956 	hw_wq_callback_t *wqcb;
11957 	uint32_t i;
11958 
11959 	/* Remove all from freelist */
11960 	while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
11961 		;
11962 	}
11963 
11964 	/* Put them all back */
11965 	for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
11966 		wqcb->instance_index = i;
11967 		wqcb->callback = NULL;
11968 		wqcb->arg = NULL;
11969 		ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11970 	}
11971 }
11972 
11973 /**
11974  * @brief Handle HW assertion
11975  *
11976  * HW assert, display diagnostic message, and abort.
11977  *
11978  * @param cond string describing failing assertion condition
11979  * @param filename file name
11980  * @param linenum line number
11981  *
11982  * @return none
11983  */
11984 void
_ocs_hw_assert(const char * cond,const char * filename,int linenum)11985 _ocs_hw_assert(const char *cond, const char *filename, int linenum)
11986 {
11987 	ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
11988 	ocs_abort();
11989 		/* no return */
11990 }
11991 
11992 /**
11993  * @brief Handle HW verify
11994  *
11995  * HW verify, display diagnostic message, dump stack and return.
11996  *
11997  * @param cond string describing failing verify condition
11998  * @param filename file name
11999  * @param linenum line number
12000  *
12001  * @return none
12002  */
12003 void
_ocs_hw_verify(const char * cond,const char * filename,int linenum)12004 _ocs_hw_verify(const char *cond, const char *filename, int linenum)
12005 {
12006 	ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
12007 	ocs_print_stack();
12008 }
12009 
12010 /**
12011  * @brief Reque XRI
12012  *
12013  * @par Description
12014  * Reque XRI
12015  *
12016  * @param hw Pointer to HW object.
12017  * @param io Pointer to HW IO
12018  *
12019  * @return Return 0 if successful else returns -1
12020  */
12021 int32_t
ocs_hw_reque_xri(ocs_hw_t * hw,ocs_hw_io_t * io)12022 ocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
12023 {
12024 	int32_t rc = 0;
12025 
12026 	rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
12027 	if (rc) {
12028 		ocs_list_add_tail(&hw->io_port_dnrx, io);
12029 		rc = -1;
12030 		goto exit_ocs_hw_reque_xri;
12031 	}
12032 
12033 	io->auto_xfer_rdy_dnrx = 0;
12034 	io->type = OCS_HW_IO_DNRX_REQUEUE;
12035 	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)) {
12036 		/* Clear buffer from XRI */
12037 		ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
12038 		io->axr_buf = NULL;
12039 
12040 		ocs_log_err(hw->os, "requeue_xri WQE error\n");
12041 		ocs_list_add_tail(&hw->io_port_dnrx, io);
12042 
12043 		rc = -1;
12044 		goto exit_ocs_hw_reque_xri;
12045 	}
12046 
12047 	if (io->wq == NULL) {
12048 		io->wq = ocs_hw_queue_next_wq(hw, io);
12049 		ocs_hw_assert(io->wq != NULL);
12050 	}
12051 
12052 	/*
12053 	 * Add IO to active io wqe list before submitting, in case the
12054 	 * wcqe processing preempts this thread.
12055 	 */
12056 	OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
12057 	OCS_STAT(io->wq->use_count++);
12058 
12059 	rc = hw_wq_write(io->wq, &io->wqe);
12060 	if (rc < 0) {
12061 		ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
12062 		rc = -1;
12063 	}
12064 
12065 exit_ocs_hw_reque_xri:
12066 	return 0;
12067 }
12068 
12069 uint32_t
ocs_hw_get_def_wwn(ocs_t * ocs,uint32_t chan,uint64_t * wwpn,uint64_t * wwnn)12070 ocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
12071 {
12072 	sli4_t *sli4 = &ocs->hw.sli;
12073 	ocs_dma_t       dma;
12074 	uint8_t		*payload = NULL;
12075 
12076 	int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
12077 
12078 	/* allocate memory for the service parameters */
12079 	if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
12080 		ocs_log_err(ocs, "Failed to allocate DMA memory\n");
12081 		return 1;
12082 	}
12083 
12084 	if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
12085 				&dma, indicator)) {
12086 		ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
12087 		ocs_dma_free(ocs, &dma);
12088 		return 1;
12089 	}
12090 
12091 	if (sli_bmbx_command(sli4)) {
12092 		ocs_log_err(ocs, "READ_SPARM64 command failure\n");
12093 		ocs_dma_free(ocs, &dma);
12094 		return 1;
12095 	}
12096 
12097 	payload = dma.virt;
12098 	ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
12099 	ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
12100 	ocs_dma_free(ocs, &dma);
12101 	return 0;
12102 }
12103 
12104 uint32_t
ocs_hw_get_config_persistent_topology(ocs_hw_t * hw)12105 ocs_hw_get_config_persistent_topology(ocs_hw_t *hw)
12106 {
12107         uint32_t topology = OCS_HW_TOPOLOGY_AUTO;
12108 	sli4_t *sli = &hw->sli;
12109 
12110         if (!sli_persist_topology_enabled(sli))
12111                 return topology;
12112 
12113         switch (sli->config.pt) {
12114                 case SLI4_INIT_LINK_F_P2P_ONLY:
12115                         topology = OCS_HW_TOPOLOGY_NPORT;
12116                         break;
12117                 case SLI4_INIT_LINK_F_FCAL_ONLY:
12118                         topology = OCS_HW_TOPOLOGY_LOOP;
12119                         break;
12120                 default:
12121                         break;
12122         }
12123 
12124         return topology;
12125 }
12126 
12127 /*
12128  * @brief Persistent topology configuration callback argument.
12129  */
12130 typedef struct ocs_hw_persistent_topo_cb_arg {
12131 	ocs_sem_t semaphore;
12132 	int32_t status;
12133 } ocs_hw_persistent_topo_cb_arg_t;
12134 
12135 /*
12136  * @brief Called after the completion of set persistent topology request
12137  *
12138  * @par Description
12139  * This is callback fn for the set_persistent_topology
12140  * function. This callback is called when the common feature mbx cmd
12141  * completes.
12142  *
12143  * @param hw Hardware context.
12144  * @param status The status from the MQE.
12145  * @param mqe Pointer to mailbox command buffer.
12146  * @param arg Pointer to a callback argument.
12147  *
12148  * @return 0 on success, non-zero otherwise
12149  */
12150 static int32_t
ocs_hw_set_persistent_topolgy_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)12151 ocs_hw_set_persistent_topolgy_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
12152 {
12153 	ocs_hw_persistent_topo_cb_arg_t *req = (ocs_hw_persistent_topo_cb_arg_t *)arg;
12154 
12155 	req->status = status;
12156 
12157 	ocs_sem_v(&req->semaphore);
12158 
12159 	return 0;
12160 }
12161 
12162 /**
12163  * @brief Set persistent topology
12164  *
12165  * Sets the persistent topology(PT) feature using
12166  * COMMON_SET_FEATURES cmd. If mbx cmd succeeds, update the
12167  * topology into sli config. PT stores the value to be set into link_flags
12168  * of the cmd INIT_LINK, to bring up the link.
12169  *
12170  * SLI specs defines following for PT:
12171  *     When TF is set to 0:
12172  *       0 Reserved
12173  *       1 Attempt point-to-point initialization (direct attach or Fabric topology).
12174  *       2 Attempt FC-AL loop initialization.
12175  *       3 Reserved
12176  *
12177  *      When TF is set to 1:
12178  *       0 Attempt FC-AL loop initialization; if it fails, attempt point-to-point initialization.
12179  *       1 Attempt point-to-point initialization; if it fails, attempt FC-AL loop initialization.
12180  *       2 Reserved
12181  *      3 Reserved
12182  *
12183  *     Note: Topology failover is only available on Lancer G5. This command will fail
12184  *     if TF is set to 1 on any other ASICs
12185  *
12186  * @param hw Pointer to hw
12187  * @param topology topology value to be set, provided through
12188  *        elxsdkutil set-topology cmd
12189  *
12190  * @return Returns 0 on success, or a non-zero value on failure.
12191  */
12192 ocs_hw_rtn_e
ocs_hw_set_persistent_topology(ocs_hw_t * hw,uint32_t topology,uint32_t opts)12193 ocs_hw_set_persistent_topology(ocs_hw_t *hw, uint32_t topology, uint32_t opts)
12194 {
12195 	ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
12196 	uint8_t buf[SLI4_BMBX_SIZE];
12197 	sli4_req_common_set_features_persistent_topo_param_t param;
12198 	ocs_hw_persistent_topo_cb_arg_t request;
12199 
12200 	ocs_memset(&param, 0, sizeof(param));
12201 	param.persistent_topo = topology;
12202 
12203 	switch (topology) {
12204 	case OCS_HW_TOPOLOGY_AUTO:
12205 		if (sli_get_asic_type(&hw->sli) == SLI4_ASIC_TYPE_LANCER) {
12206 			param.persistent_topo = SLI4_INIT_LINK_F_P2P_FAIL_OVER;
12207 			param.topo_failover = 1;
12208 		} else {
12209 			param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;;
12210 			param.topo_failover = 0;
12211 		}
12212 		break;
12213 
12214 	case OCS_HW_TOPOLOGY_NPORT:
12215 		param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;
12216 		param.topo_failover = 0;
12217 		break;
12218 
12219 	case OCS_HW_TOPOLOGY_LOOP:
12220 		param.persistent_topo = SLI4_INIT_LINK_F_FCAL_ONLY;
12221 		param.topo_failover = 0;
12222 		break;
12223 
12224 	default:
12225 		ocs_log_err(hw->os, "unsupported topology %#x\n", topology);
12226 		return -1;
12227 	}
12228 
12229 	ocs_sem_init(&request.semaphore, 0, "set_persistent_topo");
12230 
12231 	/* build the set_features command */
12232 	sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
12233 		SLI4_SET_FEATURES_PERSISTENT_TOPOLOGY, sizeof(param), &param);
12234 
12235 	if (opts == OCS_CMD_POLL) {
12236 		rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
12237 		if (rc) {
12238 			ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12239 			return rc;
12240 		}
12241 	} else {
12242 
12243 		// there's no response for this feature command
12244 		rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_set_persistent_topolgy_cb, &request);
12245 		if (rc) {
12246 			ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12247 			return rc;
12248 		}
12249 
12250 		if (ocs_sem_p(&request.semaphore, OCS_SEM_FOREVER)) {
12251 			ocs_log_err(hw->os, "ocs_sem_p failed\n");
12252 			return -ENXIO;
12253 		}
12254 
12255 		if (request.status) {
12256 			ocs_log_err(hw->os, "set persistent topology failed; status: %d\n", request.status);
12257 			return -EFAULT;
12258 		}
12259 	}
12260 
12261 	sli_config_persistent_topology(&hw->sli, &param);
12262 
12263 	return rc;
12264 }
12265 
12266 /**
12267  * @page fc_hw_api_overview HW APIs
12268  * - @ref devInitShutdown
12269  * - @ref domain
12270  * - @ref port
12271  * - @ref node
12272  * - @ref io
12273  * - @ref interrupt
12274  *
12275  * <div class="overview">
12276  * The Hardware Abstraction Layer (HW) insulates the higher-level code from the SLI-4
12277  * message details, but the higher level code must still manage domains, ports,
12278  * IT nexuses, and IOs. The HW API is designed to help the higher level manage
12279  * these objects.<br><br>
12280  *
12281  * The HW uses function callbacks to notify the higher-level code of events
12282  * that are received from the chip. There are currently three types of
12283  * functions that may be registered:
12284  *
12285  * <ul><li>domain – This function is called whenever a domain event is generated
12286  * within the HW. Examples include a new FCF is discovered, a connection
12287  * to a domain is disrupted, and allocation callbacks.</li>
12288  * <li>unsolicited – This function is called whenever new data is received in
12289  * the SLI-4 receive queue.</li>
12290  * <li>rnode – This function is called for remote node events, such as attach status
12291  * and  allocation callbacks.</li></ul>
12292  *
12293  * Upper layer functions may be registered by using the ocs_hw_callback() function.
12294  *
12295  * <img src="elx_fc_hw.jpg" alt="FC/FCoE HW" title="FC/FCoE HW" align="right"/>
12296  * <h2>FC/FCoE HW API</h2>
12297  * The FC/FCoE HW component builds upon the SLI-4 component to establish a flexible
12298  * interface for creating the necessary common objects and sending I/Os. It may be used
12299  * “as is” in customer implementations or it can serve as an example of typical interactions
12300  * between a driver and the SLI-4 hardware. The broad categories of functionality include:
12301  *
12302  * <ul><li>Setting-up and tearing-down of the HW.</li>
12303  * <li>Allocating and using the common objects (SLI Port, domain, remote node).</li>
12304  * <li>Sending and receiving I/Os.</li></ul>
12305  *
12306  * <h3>HW Setup</h3>
12307  * To set up the HW:
12308  *
12309  * <ol>
12310  * <li>Set up the HW object using ocs_hw_setup().<br>
12311  * This step performs a basic configuration of the SLI-4 component and the HW to
12312  * enable querying the hardware for its capabilities. At this stage, the HW is not
12313  * capable of general operations (such as, receiving events or sending I/Os).</li><br><br>
12314  * <li>Configure the HW according to the driver requirements.<br>
12315  * The HW provides functions to discover hardware capabilities (ocs_hw_get()), as
12316  * well as configures the amount of resources required (ocs_hw_set()). The driver
12317  * must also register callback functions (ocs_hw_callback()) to receive notification of
12318  * various asynchronous events.<br><br>
12319  * @b Note: Once configured, the driver must initialize the HW (ocs_hw_init()). This
12320  * step creates the underlying queues, commits resources to the hardware, and
12321  * prepares the hardware for operation. While the hardware is operational, the
12322  * port is not online, and cannot send or receive data.</li><br><br>
12323  * <br><br>
12324  * <li>Finally, the driver can bring the port online (ocs_hw_port_control()).<br>
12325  * When the link comes up, the HW determines if a domain is present and notifies the
12326  * driver using the domain callback function. This is the starting point of the driver's
12327  * interaction with the common objects.<br><br>
12328  * @b Note: For FCoE, there may be more than one domain available and, therefore,
12329  * more than one callback.</li>
12330  * </ol>
12331  *
12332  * <h3>Allocating and Using Common Objects</h3>
12333  * Common objects provide a mechanism through which the various OneCore Storage
12334  * driver components share and track information. These data structures are primarily
12335  * used to track SLI component information but can be extended by other components, if
12336  * needed. The main objects are:
12337  *
12338  * <ul><li>DMA – the ocs_dma_t object describes a memory region suitable for direct
12339  * memory access (DMA) transactions.</li>
12340  * <li>SCSI domain – the ocs_domain_t object represents the SCSI domain, including
12341  * any infrastructure devices such as FC switches and FC forwarders. The domain
12342  * object contains both an FCFI and a VFI.</li>
12343  * <li>SLI Port (sport) – the ocs_sli_port_t object represents the connection between
12344  * the driver and the SCSI domain. The SLI Port object contains a VPI.</li>
12345  * <li>Remote node – the ocs_remote_node_t represents a connection between the SLI
12346  * Port and another device in the SCSI domain. The node object contains an RPI.</li></ul>
12347  *
12348  * Before the driver can send I/Os, it must allocate the SCSI domain, SLI Port, and remote
12349  * node common objects and establish the connections between them. The goal is to
12350  * connect the driver to the SCSI domain to exchange I/Os with other devices. These
12351  * common object connections are shown in the following figure, FC Driver Common Objects:
12352  * <img src="elx_fc_common_objects.jpg"
12353  * alt="FC Driver Common Objects" title="FC Driver Common Objects" align="center"/>
12354  *
12355  * The first step is to create a connection to the domain by allocating an SLI Port object.
12356  * The SLI Port object represents a particular FC ID and must be initialized with one. With
12357  * the SLI Port object, the driver can discover the available SCSI domain(s). On identifying
12358  * a domain, the driver allocates a domain object and attaches to it using the previous SLI
12359  * port object.<br><br>
12360  *
12361  * @b Note: In some cases, the driver may need to negotiate service parameters (that is,
12362  * FLOGI) with the domain before attaching.<br><br>
12363  *
12364  * Once attached to the domain, the driver can discover and attach to other devices
12365  * (remote nodes). The exact discovery method depends on the driver, but it typically
12366  * includes using a position map, querying the fabric name server, or an out-of-band
12367  * method. In most cases, it is necessary to log in with devices before performing I/Os.
12368  * Prior to sending login-related ELS commands (ocs_hw_srrs_send()), the driver must
12369  * allocate a remote node object (ocs_hw_node_alloc()). If the login negotiation is
12370  * successful, the driver must attach the nodes (ocs_hw_node_attach()) to the SLI Port
12371  * before exchanging FCP I/O.<br><br>
12372  *
12373  * @b Note: The HW manages both the well known fabric address and the name server as
12374  * nodes in the domain. Therefore, the driver must allocate node objects prior to
12375  * communicating with either of these entities.
12376  *
12377  * <h3>Sending and Receiving I/Os</h3>
12378  * The HW provides separate interfaces for sending BLS/ ELS/ FC-CT and FCP, but the
12379  * commands are conceptually similar. Since the commands complete asynchronously,
12380  * the caller must provide a HW I/O object that maintains the I/O state, as well as
12381  * provide a callback function. The driver may use the same callback function for all I/O
12382  * operations, but each operation must use a unique HW I/O object. In the SLI-4
12383  * architecture, there is a direct association between the HW I/O object and the SGL used
12384  * to describe the data. Therefore, a driver typically performs the following operations:
12385  *
12386  * <ul><li>Allocates a HW I/O object (ocs_hw_io_alloc()).</li>
12387  * <li>Formats the SGL, specifying both the HW I/O object and the SGL.
12388  * (ocs_hw_io_init_sges() and ocs_hw_io_add_sge()).</li>
12389  * <li>Sends the HW I/O (ocs_hw_io_send()).</li></ul>
12390  *
12391  * <h3>HW Tear Down</h3>
12392  * To tear-down the HW:
12393  *
12394  * <ol><li>Take the port offline (ocs_hw_port_control()) to prevent receiving further
12395  * data andevents.</li>
12396  * <li>Destroy the HW object (ocs_hw_teardown()).</li>
12397  * <li>Free any memory used by the HW, such as buffers for unsolicited data.</li></ol>
12398  * <br>
12399  * </div><!-- overview -->
12400  *
12401  */
12402 
12403 
12404 
12405 
12406 /**
12407  * This contains all hw runtime workaround code.  Based on the asic type,
12408  * asic revision, and range of fw revisions, a particular workaround may be enabled.
12409  *
12410  * A workaround may consist of overriding a particular HW/SLI4 value that was initialized
12411  * during ocs_hw_setup() (for example the MAX_QUEUE overrides for mis-reported queue
12412  * sizes). Or if required, elements of the ocs_hw_workaround_t structure may be set to
12413  * control specific runtime behavior.
12414  *
12415  * It is intended that the controls in ocs_hw_workaround_t be defined functionally.  So we
12416  * would have the driver look like:  "if (hw->workaround.enable_xxx) then ...", rather than
12417  * what we might previously see as "if this is a BE3, then do xxx"
12418  *
12419  */
12420 
12421 
12422 #define HW_FWREV_ZERO		(0ull)
12423 #define HW_FWREV_MAX		(~0ull)
12424 
12425 #define SLI4_ASIC_TYPE_ANY	0
12426 #define SLI4_ASIC_REV_ANY	0
12427 
12428 /**
12429  * @brief Internal definition of workarounds
12430  */
12431 
12432 typedef enum {
12433 	HW_WORKAROUND_TEST = 1,
12434 	HW_WORKAROUND_MAX_QUEUE,	/**< Limits all queues */
12435 	HW_WORKAROUND_MAX_RQ,		/**< Limits only the RQ */
12436 	HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
12437 	HW_WORKAROUND_WQE_COUNT_METHOD,
12438 	HW_WORKAROUND_RQE_COUNT_METHOD,
12439 	HW_WORKAROUND_USE_UNREGISTERD_RPI,
12440 	HW_WORKAROUND_DISABLE_AR_TGT_DIF, /**< Disable of auto-response target DIF */
12441 	HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
12442 	HW_WORKAROUND_USE_DIF_QUARANTINE,
12443 	HW_WORKAROUND_USE_DIF_SEC_XRI,		/**< Use secondary xri for multiple data phases */
12444 	HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB,	/**< FCFI reported in SRB not correct, use "first" registered domain */
12445 	HW_WORKAROUND_FW_VERSION_TOO_LOW,	/**< The FW version is not the min version supported by this driver */
12446 	HW_WORKAROUND_SGLC_MISREPORTED,	/**< Chip supports SGL Chaining but SGLC is not set in SLI4_PARAMS */
12447 	HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE,	/**< Don't use SEND_FRAME capable if FW version is too old */
12448 } hw_workaround_e;
12449 
12450 /**
12451  * @brief Internal workaround structure instance
12452  */
12453 
12454 typedef struct {
12455 	sli4_asic_type_e asic_type;
12456 	sli4_asic_rev_e asic_rev;
12457 	uint64_t fwrev_low;
12458 	uint64_t fwrev_high;
12459 
12460 	hw_workaround_e workaround;
12461 	uint32_t value;
12462 } hw_workaround_t;
12463 
12464 static hw_workaround_t hw_workarounds[] = {
12465 	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12466 		HW_WORKAROUND_TEST, 999},
12467 
12468 	/* Bug: 127585: if_type == 2 returns 0 for total length placed on
12469 	 * FCP_TSEND64_WQE completions.   Note, original driver code enables this
12470 	 * workaround for all asic types
12471 	 */
12472 	{SLI4_ASIC_TYPE_ANY,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12473 		HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
12474 
12475 	/* Bug: unknown, Lancer A0 has mis-reported max queue depth */
12476 	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
12477 		HW_WORKAROUND_MAX_QUEUE, 2048},
12478 
12479 	/* Bug: 143399, BE3 has mis-reported max RQ queue depth */
12480 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
12481 		HW_WORKAROUND_MAX_RQ, 2048},
12482 
12483 	/* Bug: 143399, skyhawk has mis-reported max RQ queue depth */
12484 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
12485 		HW_WORKAROUND_MAX_RQ, 2048},
12486 
12487 	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported WQE count method */
12488 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12489 		HW_WORKAROUND_WQE_COUNT_METHOD, 1},
12490 
12491 	/* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported RQE count method */
12492 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12493 		HW_WORKAROUND_RQE_COUNT_METHOD, 1},
12494 
12495 	/* Bug: 142968, BE3 UE with RPI == 0xffff */
12496 	{SLI4_ASIC_TYPE_BE3,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12497 		HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
12498 
12499 	/* Bug: unknown, Skyhawk won't support auto-response on target T10-PI  */
12500 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12501 		HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
12502 
12503 	{SLI4_ASIC_TYPE_LANCER,	SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
12504 		HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
12505 
12506 	/* Bug: 160124, Skyhawk quarantine DIF XRIs  */
12507 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12508 		HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
12509 
12510 	/* Bug: 161832, Skyhawk use secondary XRI for multiple data phase TRECV */
12511 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12512 		HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
12513 
12514 	/* Bug: xxxxxx, FCFI reported in SRB not corrrect */
12515 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12516 		HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
12517 #if 0
12518 	/* Bug: 165642, FW version check for driver */
12519 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
12520 		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12521 #endif
12522 	{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
12523 		HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12524 
12525 	/* Bug 177061, Lancer FW does not set the SGLC bit */
12526 	{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12527 		HW_WORKAROUND_SGLC_MISREPORTED, 0},
12528 
12529 	/* BZ 181208/183914, enable this workaround for ALL revisions */
12530 	{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12531 		HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
12532 };
12533 
12534 /**
12535  * @brief Function prototypes
12536  */
12537 
12538 static int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
12539 
12540 /**
12541  * @brief Parse the firmware version (name)
12542  *
12543  * Parse a string of the form a.b.c.d, returning a uint64_t packed as defined
12544  * by the HW_FWREV() macro
12545  *
12546  * @param fwrev_string pointer to the firmware string
12547  *
12548  * @return packed firmware revision value
12549  */
12550 
12551 static uint64_t
parse_fw_version(const char * fwrev_string)12552 parse_fw_version(const char *fwrev_string)
12553 {
12554 	int v[4] = {0};
12555 	const char *p;
12556 	int i;
12557 
12558 	for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
12559 		v[i] = ocs_strtoul(p, 0, 0);
12560 		while(*p && *p != '.') {
12561 			p ++;
12562 		}
12563 		if (*p) {
12564 			p ++;
12565 		}
12566 	}
12567 
12568 	/* Special case for bootleg releases with f/w rev 0.0.9999.0, set to max value */
12569 	if (v[2] == 9999) {
12570 		return HW_FWREV_MAX;
12571 	} else {
12572 		return HW_FWREV(v[0], v[1], v[2], v[3]);
12573 	}
12574 }
12575 
12576 /**
12577  * @brief Test for a workaround match
12578  *
12579  * Looks at the asic type, asic revision, and fw revision, and returns TRUE if match.
12580  *
12581  * @param hw Pointer to the HW structure
12582  * @param w Pointer to a workaround structure entry
12583  *
12584  * @return Return TRUE for a match
12585  */
12586 
12587 static int32_t
ocs_hw_workaround_match(ocs_hw_t * hw,hw_workaround_t * w)12588 ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
12589 {
12590 	return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
12591 		    ((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
12592 		    (w->fwrev_low <= hw->workaround.fwrev) &&
12593 		    ((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
12594 }
12595 
12596 /**
12597  * @brief Setup HW runtime workarounds
12598  *
12599  * The function is called at the end of ocs_hw_setup() to setup any runtime workarounds
12600  * based on the HW/SLI setup.
12601  *
12602  * @param hw Pointer to HW structure
12603  *
12604  * @return none
12605  */
12606 
12607 void
ocs_hw_workaround_setup(struct ocs_hw_s * hw)12608 ocs_hw_workaround_setup(struct ocs_hw_s *hw)
12609 {
12610 	hw_workaround_t *w;
12611 	sli4_t *sli4 = &hw->sli;
12612 	uint32_t i;
12613 
12614 	/* Initialize the workaround settings */
12615 	ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
12616 
12617 	/* If hw_war_version is non-null, then its a value that was set by a module parameter
12618 	 * (sorry for the break in abstraction, but workarounds are ... well, workarounds)
12619 	 */
12620 
12621 	if (hw->hw_war_version) {
12622 		hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
12623 	} else {
12624 		hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
12625 	}
12626 
12627 	/* Walk the workaround list, if a match is found, then handle it */
12628 	for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
12629 		if (ocs_hw_workaround_match(hw, w)) {
12630 			switch(w->workaround) {
12631 
12632 			case HW_WORKAROUND_TEST: {
12633 				ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
12634 				break;
12635 			}
12636 
12637 			case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
12638 				ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
12639 				hw->workaround.retain_tsend_io_length = 1;
12640 				break;
12641 			}
12642 			case HW_WORKAROUND_MAX_QUEUE: {
12643 				sli4_qtype_e q;
12644 
12645 				ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
12646 				for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
12647 					if (hw->num_qentries[q] > w->value) {
12648 						hw->num_qentries[q] = w->value;
12649 					}
12650 				}
12651 				break;
12652 			}
12653 			case HW_WORKAROUND_MAX_RQ: {
12654 				ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
12655 				if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
12656 					hw->num_qentries[SLI_QTYPE_RQ] = w->value;
12657 				}
12658 				break;
12659 			}
12660 			case HW_WORKAROUND_WQE_COUNT_METHOD: {
12661 				ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
12662 				sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
12663 				sli_calc_max_qentries(sli4);
12664 				break;
12665 			}
12666 			case HW_WORKAROUND_RQE_COUNT_METHOD: {
12667 				ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
12668 				sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
12669 				sli_calc_max_qentries(sli4);
12670 				break;
12671 			}
12672 			case HW_WORKAROUND_USE_UNREGISTERD_RPI:
12673 				ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
12674 				hw->workaround.use_unregistered_rpi = TRUE;
12675 				/*
12676 				 * Allocate an RPI that is never registered, to be used in the case where
12677 				 * a node has been unregistered, and its indicator (RPI) value is set to 0xFFFF
12678 				 */
12679 				if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
12680 					&hw->workaround.unregistered_index)) {
12681 					ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
12682 					hw->workaround.use_unregistered_rpi = FALSE;
12683 				}
12684 				break;
12685 			case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
12686 				ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
12687 				hw->workaround.disable_ar_tgt_dif = TRUE;
12688 				break;
12689 			case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
12690 				ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
12691 				hw->workaround.disable_dump_loc = TRUE;
12692 				break;
12693 			case HW_WORKAROUND_USE_DIF_QUARANTINE:
12694 				ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
12695 				hw->workaround.use_dif_quarantine = TRUE;
12696 				break;
12697 			case HW_WORKAROUND_USE_DIF_SEC_XRI:
12698 				ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
12699 				hw->workaround.use_dif_sec_xri = TRUE;
12700 				break;
12701 			case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
12702 				ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
12703 				hw->workaround.override_fcfi = TRUE;
12704 				break;
12705 
12706 			case HW_WORKAROUND_FW_VERSION_TOO_LOW:
12707 				ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
12708 				hw->workaround.fw_version_too_low = TRUE;
12709 				break;
12710 			case HW_WORKAROUND_SGLC_MISREPORTED:
12711 				ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
12712 				hw->workaround.sglc_misreported = TRUE;
12713 				break;
12714 			case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
12715 				ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
12716 				hw->workaround.ignore_send_frame = TRUE;
12717 				break;
12718 			} /* switch(w->workaround) */
12719 		}
12720 	}
12721 }
12722