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