1 /*-
2 * Copyright (c) 2017 Broadcom. All rights reserved.
3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $FreeBSD: stable/12/sys/dev/ocs_fc/ocs_hw.c 372539 2022-09-13 05:28:59Z git2svn $
32 */
33
34 /**
35 * @file
36 * Defines and implements the Hardware Abstraction Layer (HW).
37 * All interaction with the hardware is performed through the HW, which abstracts
38 * the details of the underlying SLI-4 implementation.
39 */
40
41 /**
42 * @defgroup devInitShutdown Device Initialization and Shutdown
43 * @defgroup domain Domain Functions
44 * @defgroup port Port Functions
45 * @defgroup node Remote Node Functions
46 * @defgroup io IO Functions
47 * @defgroup interrupt Interrupt handling
48 * @defgroup os OS Required Functions
49 */
50
51 #include "ocs.h"
52 #include "ocs_os.h"
53 #include "ocs_hw.h"
54 #include "ocs_hw_queues.h"
55
56 #define OCS_HW_MQ_DEPTH 128
57 #define OCS_HW_READ_FCF_SIZE 4096
58 #define OCS_HW_DEFAULT_AUTO_XFER_RDY_IOS 256
59 #define OCS_HW_WQ_TIMER_PERIOD_MS 500
60
61 /* values used for setting the auto xfer rdy parameters */
62 #define OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT 0 /* 512 bytes */
63 #define OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT TRUE
64 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT FALSE
65 #define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT 0
66 #define OCS_HW_REQUE_XRI_REGTAG 65534
67 /* max command and response buffer lengths -- arbitrary at the moment */
68 #define OCS_HW_DMTF_CLP_CMD_MAX 256
69 #define OCS_HW_DMTF_CLP_RSP_MAX 256
70
71 /* HW global data */
72 ocs_hw_global_t hw_global;
73
74 static void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
75 static void ocs_hw_adjust_wqs(ocs_hw_t *hw);
76 static uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
77 static int32_t ocs_hw_cb_link(void *, void *);
78 static int32_t ocs_hw_cb_fip(void *, void *);
79 static int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
80 static int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
81 static int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
82 static int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
83 static int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
84 static int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
85 static ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
86 static ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
87 static int32_t ocs_hw_flush(ocs_hw_t *);
88 static int32_t ocs_hw_command_cancel(ocs_hw_t *);
89 static int32_t ocs_hw_io_cancel(ocs_hw_t *);
90 static void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
91 static void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
92 static int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
93 static ocs_hw_rtn_e ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg);
94 static int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void *);
95 static int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void *);
96 static int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void *);
97 static int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void *);
98 static int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
99 static void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
100 static int32_t ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len);
101 typedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
102 static ocs_hw_rtn_e ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg);
103 static void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
104
105 static int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
106 static ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
107 static ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
108 static ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
109 static ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
110 static ocs_hw_rtn_e ocs_hw_set_linkcfg_lancer(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
111 static ocs_hw_rtn_e ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
112 static void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
113 static ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
114 static ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
115 static ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
116 static void ocs_hw_io_free_internal(void *arg);
117 static void ocs_hw_io_free_port_owned(void *arg);
118 static ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
119 static ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
120 static void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
121 static int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
122 static ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
123 static ocs_hw_rtn_e ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable);
124
125 /* HW domain database operations */
126 static int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
127 static int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
128
129
130 /* Port state machine */
131 static void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
132 static void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
133 static void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
134 static void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
135 static void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
136
137 /* Domain state machine */
138 static void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
139 static void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
140 static void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
141 static void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
142 static void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
143 static int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
144 static int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
145 static int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
146
147 /* BZ 161832 */
148 static void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
149
150 /* WQE timeouts */
151 static void target_wqe_timer_cb(void *arg);
152 static void shutdown_target_wqe_timer(ocs_hw_t *hw);
153
154 static inline void
ocs_hw_add_io_timed_wqe(ocs_hw_t * hw,ocs_hw_io_t * io)155 ocs_hw_add_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
156 {
157 if (hw->config.emulate_tgt_wqe_timeout && io->tgt_wqe_timeout) {
158 /*
159 * Active WQE list currently only used for
160 * target WQE timeouts.
161 */
162 ocs_lock(&hw->io_lock);
163 ocs_list_add_tail(&hw->io_timed_wqe, io);
164 io->submit_ticks = ocs_get_os_ticks();
165 ocs_unlock(&hw->io_lock);
166 }
167 }
168
169 static inline void
ocs_hw_remove_io_timed_wqe(ocs_hw_t * hw,ocs_hw_io_t * io)170 ocs_hw_remove_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
171 {
172 if (hw->config.emulate_tgt_wqe_timeout) {
173 /*
174 * If target wqe timeouts are enabled,
175 * remove from active wqe list.
176 */
177 ocs_lock(&hw->io_lock);
178 if (ocs_list_on_list(&io->wqe_link)) {
179 ocs_list_remove(&hw->io_timed_wqe, io);
180 }
181 ocs_unlock(&hw->io_lock);
182 }
183 }
184
ocs_hw_iotype_is_originator(uint16_t io_type)185 static uint8_t ocs_hw_iotype_is_originator(uint16_t io_type)
186 {
187 switch (io_type) {
188 case OCS_HW_IO_INITIATOR_READ:
189 case OCS_HW_IO_INITIATOR_WRITE:
190 case OCS_HW_IO_INITIATOR_NODATA:
191 case OCS_HW_FC_CT:
192 case OCS_HW_ELS_REQ:
193 return 1;
194 default:
195 return 0;
196 }
197 }
198
ocs_hw_wcqe_abort_needed(uint16_t status,uint8_t ext,uint8_t xb)199 static uint8_t ocs_hw_wcqe_abort_needed(uint16_t status, uint8_t ext, uint8_t xb)
200 {
201 /* if exchange not active, nothing to abort */
202 if (!xb) {
203 return FALSE;
204 }
205 if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT) {
206 switch (ext) {
207 /* exceptions where abort is not needed */
208 case SLI4_FC_LOCAL_REJECT_INVALID_RPI: /* lancer returns this after unreg_rpi */
209 case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED: /* abort already in progress */
210 return FALSE;
211 default:
212 break;
213 }
214 }
215 return TRUE;
216 }
217
218 /**
219 * @brief Determine the number of chutes on the device.
220 *
221 * @par Description
222 * Some devices require queue resources allocated per protocol processor
223 * (chute). This function returns the number of chutes on this device.
224 *
225 * @param hw Hardware context allocated by the caller.
226 *
227 * @return Returns the number of chutes on the device for protocol.
228 */
229 static uint32_t
ocs_hw_get_num_chutes(ocs_hw_t * hw)230 ocs_hw_get_num_chutes(ocs_hw_t *hw)
231 {
232 uint32_t num_chutes = 1;
233
234 if (sli_get_is_dual_ulp_capable(&hw->sli) &&
235 sli_get_is_ulp_enabled(&hw->sli, 0) &&
236 sli_get_is_ulp_enabled(&hw->sli, 1)) {
237 num_chutes = 2;
238 }
239 return num_chutes;
240 }
241
242 static ocs_hw_rtn_e
ocs_hw_link_event_init(ocs_hw_t * hw)243 ocs_hw_link_event_init(ocs_hw_t *hw)
244 {
245 if (hw == NULL) {
246 ocs_log_err(hw->os, "bad parameter hw=%p\n", hw);
247 return OCS_HW_RTN_ERROR;
248 }
249
250 hw->link.status = SLI_LINK_STATUS_MAX;
251 hw->link.topology = SLI_LINK_TOPO_NONE;
252 hw->link.medium = SLI_LINK_MEDIUM_MAX;
253 hw->link.speed = 0;
254 hw->link.loop_map = NULL;
255 hw->link.fc_id = UINT32_MAX;
256
257 return OCS_HW_RTN_SUCCESS;
258 }
259
260 /**
261 * @ingroup devInitShutdown
262 * @brief If this is physical port 0, then read the max dump size.
263 *
264 * @par Description
265 * Queries the FW for the maximum dump size
266 *
267 * @param hw Hardware context allocated by the caller.
268 *
269 * @return Returns 0 on success, or a non-zero value on failure.
270 */
271 static ocs_hw_rtn_e
ocs_hw_read_max_dump_size(ocs_hw_t * hw)272 ocs_hw_read_max_dump_size(ocs_hw_t *hw)
273 {
274 uint8_t buf[SLI4_BMBX_SIZE];
275 uint8_t bus, dev, func;
276 int rc;
277
278 /* lancer only */
279 if ((SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) &&
280 (SLI4_IF_TYPE_LANCER_G7 != sli_get_if_type(&hw->sli))) {
281 ocs_log_debug(hw->os, "Function only supported for I/F type 2\n");
282 return OCS_HW_RTN_ERROR;
283 }
284
285 /*
286 * Make sure the FW is new enough to support this command. If the FW
287 * is too old, the FW will UE.
288 */
289 if (hw->workaround.disable_dump_loc) {
290 ocs_log_test(hw->os, "FW version is too old for this feature\n");
291 return OCS_HW_RTN_ERROR;
292 }
293
294 /* attempt to detemine the dump size for function 0 only. */
295 ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
296 if (func == 0) {
297 if (sli_cmd_common_set_dump_location(&hw->sli, buf,
298 SLI4_BMBX_SIZE, 1, 0, NULL, 0)) {
299 sli4_res_common_set_dump_location_t *rsp =
300 (sli4_res_common_set_dump_location_t *)
301 (buf + offsetof(sli4_cmd_sli_config_t,
302 payload.embed));
303
304 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
305 if (rc != OCS_HW_RTN_SUCCESS) {
306 ocs_log_test(hw->os, "set dump location command failed\n");
307 return rc;
308 } else {
309 hw->dump_size = rsp->buffer_length;
310 ocs_log_debug(hw->os, "Dump size %x\n", rsp->buffer_length);
311 }
312 }
313 }
314 return OCS_HW_RTN_SUCCESS;
315 }
316
317 /**
318 * @ingroup devInitShutdown
319 * @brief Set up the Hardware Abstraction Layer module.
320 *
321 * @par Description
322 * Calls set up to configure the hardware.
323 *
324 * @param hw Hardware context allocated by the caller.
325 * @param os Device abstraction.
326 * @param port_type Protocol type of port, such as FC and NIC.
327 *
328 * @todo Why is port_type a parameter?
329 *
330 * @return Returns 0 on success, or a non-zero value on failure.
331 */
332 ocs_hw_rtn_e
ocs_hw_setup(ocs_hw_t * hw,ocs_os_handle_t os,sli4_port_type_e port_type)333 ocs_hw_setup(ocs_hw_t *hw, ocs_os_handle_t os, sli4_port_type_e port_type)
334 {
335 uint32_t i;
336 char prop_buf[32];
337
338 if (hw == NULL) {
339 ocs_log_err(os, "bad parameter(s) hw=%p\n", hw);
340 return OCS_HW_RTN_ERROR;
341 }
342
343 if (hw->hw_setup_called) {
344 /* Setup run-time workarounds.
345 * Call for each setup, to allow for hw_war_version
346 */
347 ocs_hw_workaround_setup(hw);
348 return OCS_HW_RTN_SUCCESS;
349 }
350
351 /*
352 * ocs_hw_init() relies on NULL pointers indicating that a structure
353 * needs allocation. If a structure is non-NULL, ocs_hw_init() won't
354 * free/realloc that memory
355 */
356 ocs_memset(hw, 0, sizeof(ocs_hw_t));
357
358 hw->hw_setup_called = TRUE;
359
360 hw->os = os;
361
362 ocs_lock_init(hw->os, &hw->cmd_lock, "HW_cmd_lock[%d]", ocs_instance(hw->os));
363 ocs_list_init(&hw->cmd_head, ocs_command_ctx_t, link);
364 ocs_list_init(&hw->cmd_pending, ocs_command_ctx_t, link);
365 hw->cmd_head_count = 0;
366
367 ocs_lock_init(hw->os, &hw->io_lock, "HW_io_lock[%d]", ocs_instance(hw->os));
368 ocs_lock_init(hw->os, &hw->io_abort_lock, "HW_io_abort_lock[%d]", ocs_instance(hw->os));
369
370 ocs_atomic_init(&hw->io_alloc_failed_count, 0);
371
372 hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
373 hw->config.dif_seed = 0;
374 hw->config.auto_xfer_rdy_blk_size_chip = OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT;
375 hw->config.auto_xfer_rdy_ref_tag_is_lba = OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT;
376 hw->config.auto_xfer_rdy_app_tag_valid = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT;
377 hw->config.auto_xfer_rdy_app_tag_value = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT;
378
379
380 if (sli_setup(&hw->sli, hw->os, port_type)) {
381 ocs_log_err(hw->os, "SLI setup failed\n");
382 return OCS_HW_RTN_ERROR;
383 }
384
385 ocs_memset(hw->domains, 0, sizeof(hw->domains));
386
387 ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
388
389 ocs_hw_link_event_init(hw);
390
391 sli_callback(&hw->sli, SLI4_CB_LINK, ocs_hw_cb_link, hw);
392 sli_callback(&hw->sli, SLI4_CB_FIP, ocs_hw_cb_fip, hw);
393
394 /*
395 * Set all the queue sizes to the maximum allowed. These values may
396 * be changes later by the adjust and workaround functions.
397 */
398 for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) {
399 hw->num_qentries[i] = sli_get_max_qentries(&hw->sli, i);
400 }
401
402 /*
403 * The RQ assignment for RQ pair mode.
404 */
405 hw->config.rq_default_buffer_size = OCS_HW_RQ_SIZE_PAYLOAD;
406 hw->config.n_io = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
407 if (ocs_get_property("auto_xfer_rdy_xri_cnt", prop_buf, sizeof(prop_buf)) == 0) {
408 hw->config.auto_xfer_rdy_xri_cnt = ocs_strtoul(prop_buf, 0, 0);
409 }
410
411 /* by default, enable initiator-only auto-ABTS emulation */
412 hw->config.i_only_aab = TRUE;
413
414 /* Setup run-time workarounds */
415 ocs_hw_workaround_setup(hw);
416
417 /* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
418 if (hw->workaround.override_fcfi) {
419 hw->first_domain_idx = -1;
420 }
421
422 /* Must be done after the workaround setup */
423 if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
424 (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))) {
425
426 (void)ocs_hw_read_max_dump_size(hw);
427 }
428
429 /* calculate the number of WQs required. */
430 ocs_hw_adjust_wqs(hw);
431
432 /* Set the default dif mode */
433 if (! sli_is_dif_inline_capable(&hw->sli)) {
434 ocs_log_test(hw->os, "not inline capable, setting mode to separate\n");
435 hw->config.dif_mode = OCS_HW_DIF_MODE_SEPARATE;
436 }
437 /* Workaround: BZ 161832 */
438 if (hw->workaround.use_dif_sec_xri) {
439 ocs_list_init(&hw->sec_hio_wait_list, ocs_hw_io_t, link);
440 }
441
442 /*
443 * Figure out the starting and max ULP to spread the WQs across the
444 * ULPs.
445 */
446 if (sli_get_is_dual_ulp_capable(&hw->sli)) {
447 if (sli_get_is_ulp_enabled(&hw->sli, 0) &&
448 sli_get_is_ulp_enabled(&hw->sli, 1)) {
449 hw->ulp_start = 0;
450 hw->ulp_max = 1;
451 } else if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
452 hw->ulp_start = 0;
453 hw->ulp_max = 0;
454 } else {
455 hw->ulp_start = 1;
456 hw->ulp_max = 1;
457 }
458 } else {
459 if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
460 hw->ulp_start = 0;
461 hw->ulp_max = 0;
462 } else {
463 hw->ulp_start = 1;
464 hw->ulp_max = 1;
465 }
466 }
467 ocs_log_debug(hw->os, "ulp_start %d, ulp_max %d\n",
468 hw->ulp_start, hw->ulp_max);
469 hw->config.queue_topology = hw_global.queue_topology_string;
470
471 hw->qtop = ocs_hw_qtop_parse(hw, hw->config.queue_topology);
472
473 hw->config.n_eq = hw->qtop->entry_counts[QTOP_EQ];
474 hw->config.n_cq = hw->qtop->entry_counts[QTOP_CQ];
475 hw->config.n_rq = hw->qtop->entry_counts[QTOP_RQ];
476 hw->config.n_wq = hw->qtop->entry_counts[QTOP_WQ];
477 hw->config.n_mq = hw->qtop->entry_counts[QTOP_MQ];
478
479 /* Verify qtop configuration against driver supported configuration */
480 if (hw->config.n_rq > OCE_HW_MAX_NUM_MRQ_PAIRS) {
481 ocs_log_crit(hw->os, "Max supported MRQ pairs = %d\n",
482 OCE_HW_MAX_NUM_MRQ_PAIRS);
483 return OCS_HW_RTN_ERROR;
484 }
485
486 if (hw->config.n_eq > OCS_HW_MAX_NUM_EQ) {
487 ocs_log_crit(hw->os, "Max supported EQs = %d\n",
488 OCS_HW_MAX_NUM_EQ);
489 return OCS_HW_RTN_ERROR;
490 }
491
492 if (hw->config.n_cq > OCS_HW_MAX_NUM_CQ) {
493 ocs_log_crit(hw->os, "Max supported CQs = %d\n",
494 OCS_HW_MAX_NUM_CQ);
495 return OCS_HW_RTN_ERROR;
496 }
497
498 if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
499 ocs_log_crit(hw->os, "Max supported WQs = %d\n",
500 OCS_HW_MAX_NUM_WQ);
501 return OCS_HW_RTN_ERROR;
502 }
503
504 if (hw->config.n_mq > OCS_HW_MAX_NUM_MQ) {
505 ocs_log_crit(hw->os, "Max supported MQs = %d\n",
506 OCS_HW_MAX_NUM_MQ);
507 return OCS_HW_RTN_ERROR;
508 }
509
510 return OCS_HW_RTN_SUCCESS;
511 }
512
513 /**
514 * @ingroup devInitShutdown
515 * @brief Allocate memory structures to prepare for the device operation.
516 *
517 * @par Description
518 * Allocates memory structures needed by the device and prepares the device
519 * for operation.
520 * @n @n @b Note: This function may be called more than once (for example, at
521 * initialization and then after a reset), but the size of the internal resources
522 * may not be changed without tearing down the HW (ocs_hw_teardown()).
523 *
524 * @param hw Hardware context allocated by the caller.
525 *
526 * @return Returns 0 on success, or a non-zero value on failure.
527 */
528 ocs_hw_rtn_e
ocs_hw_init(ocs_hw_t * hw)529 ocs_hw_init(ocs_hw_t *hw)
530 {
531 ocs_hw_rtn_e rc;
532 uint32_t i = 0;
533 uint8_t buf[SLI4_BMBX_SIZE];
534 uint32_t max_rpi;
535 int rem_count;
536 int written_size = 0;
537 uint32_t count;
538 char prop_buf[32];
539 uint32_t ramdisc_blocksize = 512;
540 uint32_t q_count = 0;
541 /*
542 * Make sure the command lists are empty. If this is start-of-day,
543 * they'll be empty since they were just initialized in ocs_hw_setup.
544 * If we've just gone through a reset, the command and command pending
545 * lists should have been cleaned up as part of the reset (ocs_hw_reset()).
546 */
547 ocs_lock(&hw->cmd_lock);
548 if (!ocs_list_empty(&hw->cmd_head)) {
549 ocs_log_test(hw->os, "command found on cmd list\n");
550 ocs_unlock(&hw->cmd_lock);
551 return OCS_HW_RTN_ERROR;
552 }
553 if (!ocs_list_empty(&hw->cmd_pending)) {
554 ocs_log_test(hw->os, "command found on pending list\n");
555 ocs_unlock(&hw->cmd_lock);
556 return OCS_HW_RTN_ERROR;
557 }
558 ocs_unlock(&hw->cmd_lock);
559
560 /* Free RQ buffers if prevously allocated */
561 ocs_hw_rx_free(hw);
562
563 /*
564 * The IO queues must be initialized here for the reset case. The
565 * ocs_hw_init_io() function will re-add the IOs to the free list.
566 * The cmd_head list should be OK since we free all entries in
567 * ocs_hw_command_cancel() that is called in the ocs_hw_reset().
568 */
569
570 /* If we are in this function due to a reset, there may be stale items
571 * on lists that need to be removed. Clean them up.
572 */
573 rem_count=0;
574 if (ocs_list_valid(&hw->io_wait_free)) {
575 while ((!ocs_list_empty(&hw->io_wait_free))) {
576 rem_count++;
577 ocs_list_remove_head(&hw->io_wait_free);
578 }
579 if (rem_count > 0) {
580 ocs_log_debug(hw->os, "removed %d items from io_wait_free list\n", rem_count);
581 }
582 }
583 rem_count=0;
584 if (ocs_list_valid(&hw->io_inuse)) {
585 while ((!ocs_list_empty(&hw->io_inuse))) {
586 rem_count++;
587 ocs_list_remove_head(&hw->io_inuse);
588 }
589 if (rem_count > 0) {
590 ocs_log_debug(hw->os, "removed %d items from io_inuse list\n", rem_count);
591 }
592 }
593 rem_count=0;
594 if (ocs_list_valid(&hw->io_free)) {
595 while ((!ocs_list_empty(&hw->io_free))) {
596 rem_count++;
597 ocs_list_remove_head(&hw->io_free);
598 }
599 if (rem_count > 0) {
600 ocs_log_debug(hw->os, "removed %d items from io_free list\n", rem_count);
601 }
602 }
603 if (ocs_list_valid(&hw->io_port_owned)) {
604 while ((!ocs_list_empty(&hw->io_port_owned))) {
605 ocs_list_remove_head(&hw->io_port_owned);
606 }
607 }
608 ocs_list_init(&hw->io_inuse, ocs_hw_io_t, link);
609 ocs_list_init(&hw->io_free, ocs_hw_io_t, link);
610 ocs_list_init(&hw->io_port_owned, ocs_hw_io_t, link);
611 ocs_list_init(&hw->io_wait_free, ocs_hw_io_t, link);
612 ocs_list_init(&hw->io_timed_wqe, ocs_hw_io_t, wqe_link);
613 ocs_list_init(&hw->io_port_dnrx, ocs_hw_io_t, dnrx_link);
614
615 /* If MRQ not required, Make sure we dont request feature. */
616 if (hw->config.n_rq == 1) {
617 hw->sli.config.features.flag.mrqp = FALSE;
618 }
619
620 if (sli_init(&hw->sli)) {
621 ocs_log_err(hw->os, "SLI failed to initialize\n");
622 return OCS_HW_RTN_ERROR;
623 }
624
625 /*
626 * Enable the auto xfer rdy feature if requested.
627 */
628 hw->auto_xfer_rdy_enabled = FALSE;
629 if (sli_get_auto_xfer_rdy_capable(&hw->sli) &&
630 hw->config.auto_xfer_rdy_size > 0) {
631 if (hw->config.esoc){
632 if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
633 ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
634 }
635 written_size = sli_cmd_config_auto_xfer_rdy_hp(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size, 1, ramdisc_blocksize);
636 } else {
637 written_size = sli_cmd_config_auto_xfer_rdy(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size);
638 }
639 if (written_size) {
640 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
641 if (rc != OCS_HW_RTN_SUCCESS) {
642 ocs_log_err(hw->os, "config auto xfer rdy failed\n");
643 return rc;
644 }
645 }
646 hw->auto_xfer_rdy_enabled = TRUE;
647
648 if (hw->config.auto_xfer_rdy_t10_enable) {
649 rc = ocs_hw_config_auto_xfer_rdy_t10pi(hw, buf);
650 if (rc != OCS_HW_RTN_SUCCESS) {
651 ocs_log_err(hw->os, "set parameters auto xfer rdy T10 PI failed\n");
652 return rc;
653 }
654 }
655 }
656
657 if(hw->sliport_healthcheck) {
658 rc = ocs_hw_config_sli_port_health_check(hw, 0, 1);
659 if (rc != OCS_HW_RTN_SUCCESS) {
660 ocs_log_err(hw->os, "Enabling Sliport Health check failed \n");
661 return rc;
662 }
663 }
664
665 /*
666 * Set FDT transfer hint, only works on Lancer
667 */
668 if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) && (OCS_HW_FDT_XFER_HINT != 0)) {
669 /*
670 * Non-fatal error. In particular, we can disregard failure to set OCS_HW_FDT_XFER_HINT on
671 * devices with legacy firmware that do not support OCS_HW_FDT_XFER_HINT feature.
672 */
673 ocs_hw_config_set_fdt_xfer_hint(hw, OCS_HW_FDT_XFER_HINT);
674 }
675
676 /*
677 * Verify that we have not exceeded any queue sizes
678 */
679 q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_EQ),
680 OCS_HW_MAX_NUM_EQ);
681 if (hw->config.n_eq > q_count) {
682 ocs_log_err(hw->os, "requested %d EQ but %d allowed\n",
683 hw->config.n_eq, q_count);
684 return OCS_HW_RTN_ERROR;
685 }
686
687 q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_CQ),
688 OCS_HW_MAX_NUM_CQ);
689 if (hw->config.n_cq > q_count) {
690 ocs_log_err(hw->os, "requested %d CQ but %d allowed\n",
691 hw->config.n_cq, q_count);
692 return OCS_HW_RTN_ERROR;
693 }
694
695 q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_MQ),
696 OCS_HW_MAX_NUM_MQ);
697 if (hw->config.n_mq > q_count) {
698 ocs_log_err(hw->os, "requested %d MQ but %d allowed\n",
699 hw->config.n_mq, q_count);
700 return OCS_HW_RTN_ERROR;
701 }
702
703 q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_RQ),
704 OCS_HW_MAX_NUM_RQ);
705 if (hw->config.n_rq > q_count) {
706 ocs_log_err(hw->os, "requested %d RQ but %d allowed\n",
707 hw->config.n_rq, q_count);
708 return OCS_HW_RTN_ERROR;
709 }
710
711 q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ),
712 OCS_HW_MAX_NUM_WQ);
713 if (hw->config.n_wq > q_count) {
714 ocs_log_err(hw->os, "requested %d WQ but %d allowed\n",
715 hw->config.n_wq, q_count);
716 return OCS_HW_RTN_ERROR;
717 }
718
719 /* zero the hashes */
720 ocs_memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
721 ocs_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
722 OCS_HW_MAX_NUM_CQ, OCS_HW_Q_HASH_SIZE);
723
724 ocs_memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
725 ocs_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
726 OCS_HW_MAX_NUM_RQ, OCS_HW_Q_HASH_SIZE);
727
728 ocs_memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
729 ocs_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
730 OCS_HW_MAX_NUM_WQ, OCS_HW_Q_HASH_SIZE);
731
732
733 rc = ocs_hw_init_queues(hw, hw->qtop);
734 if (rc != OCS_HW_RTN_SUCCESS) {
735 return rc;
736 }
737
738 max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
739 i = sli_fc_get_rpi_requirements(&hw->sli, max_rpi);
740 if (i) {
741 ocs_dma_t payload_memory;
742
743 rc = OCS_HW_RTN_ERROR;
744
745 if (hw->rnode_mem.size) {
746 ocs_dma_free(hw->os, &hw->rnode_mem);
747 }
748
749 if (ocs_dma_alloc(hw->os, &hw->rnode_mem, i, 4096)) {
750 ocs_log_err(hw->os, "remote node memory allocation fail\n");
751 return OCS_HW_RTN_NO_MEMORY;
752 }
753
754 payload_memory.size = 0;
755 if (sli_cmd_fcoe_post_hdr_templates(&hw->sli, buf, SLI4_BMBX_SIZE,
756 &hw->rnode_mem, UINT16_MAX, &payload_memory)) {
757 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
758
759 if (payload_memory.size != 0) {
760 /* The command was non-embedded - need to free the dma buffer */
761 ocs_dma_free(hw->os, &payload_memory);
762 }
763 }
764
765 if (rc != OCS_HW_RTN_SUCCESS) {
766 ocs_log_err(hw->os, "header template registration failed\n");
767 return rc;
768 }
769 }
770
771 /* Allocate and post RQ buffers */
772 rc = ocs_hw_rx_allocate(hw);
773 if (rc) {
774 ocs_log_err(hw->os, "rx_allocate failed\n");
775 return rc;
776 }
777
778 /* Populate hw->seq_free_list */
779 if (hw->seq_pool == NULL) {
780 uint32_t count = 0;
781 uint32_t i;
782
783 /* Sum up the total number of RQ entries, to use to allocate the sequence object pool */
784 for (i = 0; i < hw->hw_rq_count; i++) {
785 count += hw->hw_rq[i]->entry_count;
786 }
787
788 hw->seq_pool = ocs_array_alloc(hw->os, sizeof(ocs_hw_sequence_t), count);
789 if (hw->seq_pool == NULL) {
790 ocs_log_err(hw->os, "malloc seq_pool failed\n");
791 return OCS_HW_RTN_NO_MEMORY;
792 }
793 }
794
795 if(ocs_hw_rx_post(hw)) {
796 ocs_log_err(hw->os, "WARNING - error posting RQ buffers\n");
797 }
798
799 /* Allocate rpi_ref if not previously allocated */
800 if (hw->rpi_ref == NULL) {
801 hw->rpi_ref = ocs_malloc(hw->os, max_rpi * sizeof(*hw->rpi_ref),
802 OCS_M_ZERO | OCS_M_NOWAIT);
803 if (hw->rpi_ref == NULL) {
804 ocs_log_err(hw->os, "rpi_ref allocation failure (%d)\n", i);
805 return OCS_HW_RTN_NO_MEMORY;
806 }
807 }
808
809 for (i = 0; i < max_rpi; i ++) {
810 ocs_atomic_init(&hw->rpi_ref[i].rpi_count, 0);
811 ocs_atomic_init(&hw->rpi_ref[i].rpi_attached, 0);
812 }
813
814 ocs_memset(hw->domains, 0, sizeof(hw->domains));
815
816 /* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
817 if (hw->workaround.override_fcfi) {
818 hw->first_domain_idx = -1;
819 }
820
821 ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
822
823 /* Register a FCFI to allow unsolicited frames to be routed to the driver */
824 if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
825
826 if (hw->hw_mrq_count) {
827 ocs_log_debug(hw->os, "using REG_FCFI MRQ\n");
828
829 rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0, 0);
830 if (rc != OCS_HW_RTN_SUCCESS) {
831 ocs_log_err(hw->os, "REG_FCFI_MRQ FCFI registration failed\n");
832 return rc;
833 }
834
835 rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0, 0);
836 if (rc != OCS_HW_RTN_SUCCESS) {
837 ocs_log_err(hw->os, "REG_FCFI_MRQ MRQ registration failed\n");
838 return rc;
839 }
840 } else {
841 sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
842
843 ocs_log_debug(hw->os, "using REG_FCFI standard\n");
844
845 /* Set the filter match/mask values from hw's filter_def values */
846 for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
847 rq_cfg[i].rq_id = 0xffff;
848 rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
849 rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
850 rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
851 rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
852 }
853
854 /*
855 * Update the rq_id's of the FCF configuration (don't update more than the number
856 * of rq_cfg elements)
857 */
858 for (i = 0; i < OCS_MIN(hw->hw_rq_count, SLI4_CMD_REG_FCFI_NUM_RQ_CFG); i++) {
859 hw_rq_t *rq = hw->hw_rq[i];
860 uint32_t j;
861 for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
862 uint32_t mask = (rq->filter_mask != 0) ? rq->filter_mask : 1;
863 if (mask & (1U << j)) {
864 rq_cfg[j].rq_id = rq->hdr->id;
865 ocs_log_debug(hw->os, "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
866 j, hw->config.filter_def[j], i, rq->hdr->id);
867 }
868 }
869 }
870
871 rc = OCS_HW_RTN_ERROR;
872
873 if (sli_cmd_reg_fcfi(&hw->sli, buf, SLI4_BMBX_SIZE, 0, rq_cfg, 0)) {
874 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
875 }
876
877 if (rc != OCS_HW_RTN_SUCCESS) {
878 ocs_log_err(hw->os, "FCFI registration failed\n");
879 return rc;
880 }
881 hw->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)buf)->fcfi;
882 }
883
884 }
885
886 /*
887 * Allocate the WQ request tag pool, if not previously allocated (the request tag value is 16 bits,
888 * thus the pool allocation size of 64k)
889 */
890 rc = ocs_hw_reqtag_init(hw);
891 if (rc) {
892 ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed: %d\n", rc);
893 return rc;
894 }
895
896 rc = ocs_hw_setup_io(hw);
897 if (rc) {
898 ocs_log_err(hw->os, "IO allocation failure\n");
899 return rc;
900 }
901
902 rc = ocs_hw_init_io(hw);
903 if (rc) {
904 ocs_log_err(hw->os, "IO initialization failure\n");
905 return rc;
906 }
907
908 ocs_queue_history_init(hw->os, &hw->q_hist);
909
910 /* get hw link config; polling, so callback will be called immediately */
911 hw->linkcfg = OCS_HW_LINKCFG_NA;
912 ocs_hw_get_linkcfg(hw, OCS_CMD_POLL, ocs_hw_init_linkcfg_cb, hw);
913
914 /* if lancer ethernet, ethernet ports need to be enabled */
915 if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) &&
916 (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_ETHERNET)) {
917 if (ocs_hw_set_eth_license(hw, hw->eth_license)) {
918 /* log warning but continue */
919 ocs_log_err(hw->os, "Failed to set ethernet license\n");
920 }
921 }
922
923 /* Set the DIF seed - only for lancer right now */
924 if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli) &&
925 ocs_hw_set_dif_seed(hw) != OCS_HW_RTN_SUCCESS) {
926 ocs_log_err(hw->os, "Failed to set DIF seed value\n");
927 return rc;
928 }
929
930 /* Set the DIF mode - skyhawk only */
931 if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli) &&
932 sli_get_dif_capable(&hw->sli)) {
933 rc = ocs_hw_set_dif_mode(hw);
934 if (rc != OCS_HW_RTN_SUCCESS) {
935 ocs_log_err(hw->os, "Failed to set DIF mode value\n");
936 return rc;
937 }
938 }
939
940 /*
941 * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ entries
942 */
943 for (i = 0; i < hw->eq_count; i++) {
944 sli_queue_arm(&hw->sli, &hw->eq[i], TRUE);
945 }
946
947 /*
948 * Initialize RQ hash
949 */
950 for (i = 0; i < hw->rq_count; i++) {
951 ocs_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
952 }
953
954 /*
955 * Initialize WQ hash
956 */
957 for (i = 0; i < hw->wq_count; i++) {
958 ocs_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
959 }
960
961 /*
962 * Arming the CQ allows (e.g.) MQ completions to write CQ entries
963 */
964 for (i = 0; i < hw->cq_count; i++) {
965 ocs_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
966 sli_queue_arm(&hw->sli, &hw->cq[i], TRUE);
967 }
968
969 /* record the fact that the queues are functional */
970 hw->state = OCS_HW_STATE_ACTIVE;
971
972 /* Note: Must be after the IOs are setup and the state is active*/
973 if (ocs_hw_rqpair_init(hw)) {
974 ocs_log_err(hw->os, "WARNING - error initializing RQ pair\n");
975 }
976
977 /* finally kick off periodic timer to check for timed out target WQEs */
978 if (hw->config.emulate_tgt_wqe_timeout) {
979 ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw,
980 OCS_HW_WQ_TIMER_PERIOD_MS);
981 }
982
983 /*
984 * Allocate a HW IOs for send frame. Allocate one for each Class 1 WQ, or if there
985 * are none of those, allocate one for WQ[0]
986 */
987 if ((count = ocs_varray_get_count(hw->wq_class_array[1])) > 0) {
988 for (i = 0; i < count; i++) {
989 hw_wq_t *wq = ocs_varray_iter_next(hw->wq_class_array[1]);
990 wq->send_frame_io = ocs_hw_io_alloc(hw);
991 if (wq->send_frame_io == NULL) {
992 ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
993 }
994 }
995 } else {
996 hw->hw_wq[0]->send_frame_io = ocs_hw_io_alloc(hw);
997 if (hw->hw_wq[0]->send_frame_io == NULL) {
998 ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
999 }
1000 }
1001
1002 /* Initialize send frame frame sequence id */
1003 ocs_atomic_init(&hw->send_frame_seq_id, 0);
1004
1005 /* Initialize watchdog timer if enabled by user */
1006 hw->expiration_logged = 0;
1007 if(hw->watchdog_timeout) {
1008 if((hw->watchdog_timeout < 1) || (hw->watchdog_timeout > 65534)) {
1009 ocs_log_err(hw->os, "watchdog_timeout out of range: Valid range is 1 - 65534\n");
1010 }else if(!ocs_hw_config_watchdog_timer(hw)) {
1011 ocs_log_info(hw->os, "watchdog timer configured with timeout = %d seconds \n", hw->watchdog_timeout);
1012 }
1013 }
1014
1015 if (ocs_dma_alloc(hw->os, &hw->domain_dmem, 112, 4)) {
1016 ocs_log_err(hw->os, "domain node memory allocation fail\n");
1017 return OCS_HW_RTN_NO_MEMORY;
1018 }
1019
1020 if (ocs_dma_alloc(hw->os, &hw->fcf_dmem, OCS_HW_READ_FCF_SIZE, OCS_HW_READ_FCF_SIZE)) {
1021 ocs_log_err(hw->os, "domain fcf memory allocation fail\n");
1022 return OCS_HW_RTN_NO_MEMORY;
1023 }
1024
1025 if ((0 == hw->loop_map.size) && ocs_dma_alloc(hw->os, &hw->loop_map,
1026 SLI4_MIN_LOOP_MAP_BYTES, 4)) {
1027 ocs_log_err(hw->os, "Loop dma alloc failed size:%d \n", hw->loop_map.size);
1028 }
1029
1030 return OCS_HW_RTN_SUCCESS;
1031 }
1032
1033 /**
1034 * @brief Configure Multi-RQ
1035 *
1036 * @param hw Hardware context allocated by the caller.
1037 * @param mode 1 to set MRQ filters and 0 to set FCFI index
1038 * @param vlanid valid in mode 0
1039 * @param fcf_index valid in mode 0
1040 *
1041 * @return Returns 0 on success, or a non-zero value on failure.
1042 */
1043 static int32_t
ocs_hw_config_mrq(ocs_hw_t * hw,uint8_t mode,uint16_t vlanid,uint16_t fcf_index)1044 ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t mode, uint16_t vlanid, uint16_t fcf_index)
1045 {
1046 uint8_t buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
1047 hw_rq_t *rq;
1048 sli4_cmd_reg_fcfi_mrq_t *rsp = NULL;
1049 uint32_t i, j;
1050 sli4_cmd_rq_cfg_t rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
1051 int32_t rc;
1052
1053 if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1054 goto issue_cmd;
1055 }
1056
1057 /* Set the filter match/mask values from hw's filter_def values */
1058 for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
1059 rq_filter[i].rq_id = 0xffff;
1060 rq_filter[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
1061 rq_filter[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
1062 rq_filter[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
1063 rq_filter[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
1064 }
1065
1066 /* Accumulate counts for each filter type used, build rq_ids[] list */
1067 for (i = 0; i < hw->hw_rq_count; i++) {
1068 rq = hw->hw_rq[i];
1069 for (j = 0; j < SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG; j++) {
1070 if (rq->filter_mask & (1U << j)) {
1071 if (rq_filter[j].rq_id != 0xffff) {
1072 /* Already used. Bailout ifts not RQset case */
1073 if (!rq->is_mrq || (rq_filter[j].rq_id != rq->base_mrq_id)) {
1074 ocs_log_err(hw->os, "Wrong queue topology.\n");
1075 return OCS_HW_RTN_ERROR;
1076 }
1077 continue;
1078 }
1079
1080 if (rq->is_mrq) {
1081 rq_filter[j].rq_id = rq->base_mrq_id;
1082 mrq_bitmask |= (1U << j);
1083 } else {
1084 rq_filter[j].rq_id = rq->hdr->id;
1085 }
1086 }
1087 }
1088 }
1089
1090 issue_cmd:
1091 /* Invoke REG_FCFI_MRQ */
1092 rc = sli_cmd_reg_fcfi_mrq(&hw->sli,
1093 buf, /* buf */
1094 SLI4_BMBX_SIZE, /* size */
1095 mode, /* mode 1 */
1096 fcf_index, /* fcf_index */
1097 vlanid, /* vlan_id */
1098 hw->config.rq_selection_policy, /* RQ selection policy*/
1099 mrq_bitmask, /* MRQ bitmask */
1100 hw->hw_mrq_count, /* num_mrqs */
1101 rq_filter); /* RQ filter */
1102 if (rc == 0) {
1103 ocs_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed: %d\n", rc);
1104 return OCS_HW_RTN_ERROR;
1105 }
1106
1107 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
1108
1109 rsp = (sli4_cmd_reg_fcfi_mrq_t *)buf;
1110
1111 if ((rc != OCS_HW_RTN_SUCCESS) || (rsp->hdr.status)) {
1112 ocs_log_err(hw->os, "FCFI MRQ registration failed. cmd = %x status = %x\n",
1113 rsp->hdr.command, rsp->hdr.status);
1114 return OCS_HW_RTN_ERROR;
1115 }
1116
1117 if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
1118 hw->fcf_indicator = rsp->fcfi;
1119 }
1120 return 0;
1121 }
1122
1123 /**
1124 * @brief Callback function for getting linkcfg during HW initialization.
1125 *
1126 * @param status Status of the linkcfg get operation.
1127 * @param value Link configuration enum to which the link configuration is set.
1128 * @param arg Callback argument (ocs_hw_t *).
1129 *
1130 * @return None.
1131 */
1132 static void
ocs_hw_init_linkcfg_cb(int32_t status,uintptr_t value,void * arg)1133 ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
1134 {
1135 ocs_hw_t *hw = (ocs_hw_t *)arg;
1136 if (status == 0) {
1137 hw->linkcfg = (ocs_hw_linkcfg_e)value;
1138 } else {
1139 hw->linkcfg = OCS_HW_LINKCFG_NA;
1140 }
1141 ocs_log_debug(hw->os, "linkcfg=%d\n", hw->linkcfg);
1142 }
1143
1144 /**
1145 * @ingroup devInitShutdown
1146 * @brief Tear down the Hardware Abstraction Layer module.
1147 *
1148 * @par Description
1149 * Frees memory structures needed by the device, and shuts down the device. Does
1150 * not free the HW context memory (which is done by the caller).
1151 *
1152 * @param hw Hardware context allocated by the caller.
1153 *
1154 * @return Returns 0 on success, or a non-zero value on failure.
1155 */
1156 ocs_hw_rtn_e
ocs_hw_teardown(ocs_hw_t * hw)1157 ocs_hw_teardown(ocs_hw_t *hw)
1158 {
1159 uint32_t i = 0;
1160 uint32_t iters = 10;/*XXX*/
1161 uint32_t max_rpi;
1162 uint32_t destroy_queues;
1163 uint32_t free_memory;
1164
1165 if (!hw) {
1166 ocs_log_err(NULL, "bad parameter(s) hw=%p\n", hw);
1167 return OCS_HW_RTN_ERROR;
1168 }
1169
1170 destroy_queues = (hw->state == OCS_HW_STATE_ACTIVE);
1171 free_memory = (hw->state != OCS_HW_STATE_UNINITIALIZED);
1172
1173 /* shutdown target wqe timer */
1174 shutdown_target_wqe_timer(hw);
1175
1176 /* Cancel watchdog timer if enabled */
1177 if(hw->watchdog_timeout) {
1178 hw->watchdog_timeout = 0;
1179 ocs_hw_config_watchdog_timer(hw);
1180 }
1181
1182 /* Cancel Sliport Healthcheck */
1183 if(hw->sliport_healthcheck) {
1184 hw->sliport_healthcheck = 0;
1185 ocs_hw_config_sli_port_health_check(hw, 0, 0);
1186 }
1187
1188 if (hw->state != OCS_HW_STATE_QUEUES_ALLOCATED) {
1189
1190 hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1191
1192 ocs_hw_flush(hw);
1193
1194 /* If there are outstanding commands, wait for them to complete */
1195 while (!ocs_list_empty(&hw->cmd_head) && iters) {
1196 ocs_udelay(10000);
1197 ocs_hw_flush(hw);
1198 iters--;
1199 }
1200
1201 if (ocs_list_empty(&hw->cmd_head)) {
1202 ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1203 } else {
1204 ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1205 }
1206
1207 /* Cancel any remaining commands */
1208 ocs_hw_command_cancel(hw);
1209 } else {
1210 hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
1211 }
1212
1213 ocs_lock_free(&hw->cmd_lock);
1214
1215 /* Free unregistered RPI if workaround is in force */
1216 if (hw->workaround.use_unregistered_rpi) {
1217 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, hw->workaround.unregistered_rid);
1218 }
1219
1220 max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1221 if (hw->rpi_ref) {
1222 for (i = 0; i < max_rpi; i++) {
1223 if (ocs_atomic_read(&hw->rpi_ref[i].rpi_count)) {
1224 ocs_log_debug(hw->os, "non-zero ref [%d]=%d\n",
1225 i, ocs_atomic_read(&hw->rpi_ref[i].rpi_count));
1226 }
1227 }
1228 ocs_free(hw->os, hw->rpi_ref, max_rpi * sizeof(*hw->rpi_ref));
1229 hw->rpi_ref = NULL;
1230 }
1231
1232 ocs_dma_free(hw->os, &hw->rnode_mem);
1233
1234 if (hw->io) {
1235 for (i = 0; i < hw->config.n_io; i++) {
1236 if (hw->io[i] && (hw->io[i]->sgl != NULL) &&
1237 (hw->io[i]->sgl->virt != NULL)) {
1238 if(hw->io[i]->is_port_owned) {
1239 ocs_lock_free(&hw->io[i]->axr_lock);
1240 }
1241 ocs_dma_free(hw->os, hw->io[i]->sgl);
1242 }
1243 ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
1244 hw->io[i] = NULL;
1245 }
1246 ocs_free(hw->os, hw->wqe_buffs, hw->config.n_io * hw->sli.config.wqe_size);
1247 hw->wqe_buffs = NULL;
1248 ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t *));
1249 hw->io = NULL;
1250 }
1251
1252 ocs_dma_free(hw->os, &hw->xfer_rdy);
1253 ocs_dma_free(hw->os, &hw->dump_sges);
1254 ocs_dma_free(hw->os, &hw->loop_map);
1255
1256 ocs_lock_free(&hw->io_lock);
1257 ocs_lock_free(&hw->io_abort_lock);
1258
1259
1260 for (i = 0; i < hw->wq_count; i++) {
1261 sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues, free_memory);
1262 }
1263
1264
1265 for (i = 0; i < hw->rq_count; i++) {
1266 sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, free_memory);
1267 }
1268
1269 for (i = 0; i < hw->mq_count; i++) {
1270 sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, free_memory);
1271 }
1272
1273 for (i = 0; i < hw->cq_count; i++) {
1274 sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, free_memory);
1275 }
1276
1277 for (i = 0; i < hw->eq_count; i++) {
1278 sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, free_memory);
1279 }
1280
1281 ocs_hw_qtop_free(hw->qtop);
1282
1283 /* Free rq buffers */
1284 ocs_hw_rx_free(hw);
1285
1286 hw_queue_teardown(hw);
1287
1288 ocs_hw_rqpair_teardown(hw);
1289
1290 if (sli_teardown(&hw->sli)) {
1291 ocs_log_err(hw->os, "SLI teardown failed\n");
1292 }
1293
1294 ocs_queue_history_free(&hw->q_hist);
1295
1296 /* record the fact that the queues are non-functional */
1297 hw->state = OCS_HW_STATE_UNINITIALIZED;
1298
1299 /* free sequence free pool */
1300 ocs_array_free(hw->seq_pool);
1301 hw->seq_pool = NULL;
1302
1303 /* free hw_wq_callback pool */
1304 ocs_pool_free(hw->wq_reqtag_pool);
1305
1306 ocs_dma_free(hw->os, &hw->domain_dmem);
1307 ocs_dma_free(hw->os, &hw->fcf_dmem);
1308 /* Mark HW setup as not having been called */
1309 hw->hw_setup_called = FALSE;
1310
1311 return OCS_HW_RTN_SUCCESS;
1312 }
1313
1314 ocs_hw_rtn_e
ocs_hw_reset(ocs_hw_t * hw,ocs_hw_reset_e reset)1315 ocs_hw_reset(ocs_hw_t *hw, ocs_hw_reset_e reset)
1316 {
1317 uint32_t i;
1318 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1319 uint32_t iters;
1320 ocs_hw_state_e prev_state = hw->state;
1321
1322 if (hw->state != OCS_HW_STATE_ACTIVE) {
1323 ocs_log_test(hw->os, "HW state %d is not active\n", hw->state);
1324 }
1325
1326 hw->state = OCS_HW_STATE_RESET_IN_PROGRESS;
1327
1328 /* shutdown target wqe timer */
1329 shutdown_target_wqe_timer(hw);
1330
1331 ocs_hw_flush(hw);
1332
1333 /*
1334 * If an mailbox command requiring a DMA is outstanding (i.e. SFP/DDM),
1335 * then the FW will UE when the reset is issued. So attempt to complete
1336 * all mailbox commands.
1337 */
1338 iters = 10;
1339 while (!ocs_list_empty(&hw->cmd_head) && iters) {
1340 ocs_udelay(10000);
1341 ocs_hw_flush(hw);
1342 iters--;
1343 }
1344
1345 if (ocs_list_empty(&hw->cmd_head)) {
1346 ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
1347 } else {
1348 ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
1349 }
1350
1351 /* Reset the chip */
1352 switch(reset) {
1353 case OCS_HW_RESET_FUNCTION:
1354 ocs_log_debug(hw->os, "issuing function level reset\n");
1355 if (sli_reset(&hw->sli)) {
1356 ocs_log_err(hw->os, "sli_reset failed\n");
1357 rc = OCS_HW_RTN_ERROR;
1358 }
1359 break;
1360 case OCS_HW_RESET_FIRMWARE:
1361 ocs_log_debug(hw->os, "issuing firmware reset\n");
1362 if (sli_fw_reset(&hw->sli)) {
1363 ocs_log_err(hw->os, "sli_soft_reset failed\n");
1364 rc = OCS_HW_RTN_ERROR;
1365 }
1366 /*
1367 * Because the FW reset leaves the FW in a non-running state,
1368 * follow that with a regular reset.
1369 */
1370 ocs_log_debug(hw->os, "issuing function level reset\n");
1371 if (sli_reset(&hw->sli)) {
1372 ocs_log_err(hw->os, "sli_reset failed\n");
1373 rc = OCS_HW_RTN_ERROR;
1374 }
1375 break;
1376 default:
1377 ocs_log_test(hw->os, "unknown reset type - no reset performed\n");
1378 hw->state = prev_state;
1379 return OCS_HW_RTN_ERROR;
1380 }
1381
1382 /* Not safe to walk command/io lists unless they've been initialized */
1383 if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1384 ocs_hw_command_cancel(hw);
1385
1386 /* Clean up the inuse list, the free list and the wait free list */
1387 ocs_hw_io_cancel(hw);
1388
1389 ocs_memset(hw->domains, 0, sizeof(hw->domains));
1390 ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
1391
1392 ocs_hw_link_event_init(hw);
1393
1394 ocs_lock(&hw->io_lock);
1395 /* The io lists should be empty, but remove any that didn't get cleaned up. */
1396 while (!ocs_list_empty(&hw->io_timed_wqe)) {
1397 ocs_list_remove_head(&hw->io_timed_wqe);
1398 }
1399 /* Don't clean up the io_inuse list, the backend will do that when it finishes the IO */
1400
1401 while (!ocs_list_empty(&hw->io_free)) {
1402 ocs_list_remove_head(&hw->io_free);
1403 }
1404 while (!ocs_list_empty(&hw->io_wait_free)) {
1405 ocs_list_remove_head(&hw->io_wait_free);
1406 }
1407
1408 /* Reset the request tag pool, the HW IO request tags are reassigned in ocs_hw_setup_io() */
1409 ocs_hw_reqtag_reset(hw);
1410
1411 ocs_unlock(&hw->io_lock);
1412 }
1413
1414 if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
1415 for (i = 0; i < hw->wq_count; i++) {
1416 sli_queue_reset(&hw->sli, &hw->wq[i]);
1417 }
1418
1419 for (i = 0; i < hw->rq_count; i++) {
1420 sli_queue_reset(&hw->sli, &hw->rq[i]);
1421 }
1422
1423 for (i = 0; i < hw->hw_rq_count; i++) {
1424 hw_rq_t *rq = hw->hw_rq[i];
1425 if (rq->rq_tracker != NULL) {
1426 uint32_t j;
1427
1428 for (j = 0; j < rq->entry_count; j++) {
1429 rq->rq_tracker[j] = NULL;
1430 }
1431 }
1432 }
1433
1434 for (i = 0; i < hw->mq_count; i++) {
1435 sli_queue_reset(&hw->sli, &hw->mq[i]);
1436 }
1437
1438 for (i = 0; i < hw->cq_count; i++) {
1439 sli_queue_reset(&hw->sli, &hw->cq[i]);
1440 }
1441
1442 for (i = 0; i < hw->eq_count; i++) {
1443 sli_queue_reset(&hw->sli, &hw->eq[i]);
1444 }
1445
1446 /* Free rq buffers */
1447 ocs_hw_rx_free(hw);
1448
1449 /* Teardown the HW queue topology */
1450 hw_queue_teardown(hw);
1451 } else {
1452
1453 /* Free rq buffers */
1454 ocs_hw_rx_free(hw);
1455 }
1456
1457 /*
1458 * Re-apply the run-time workarounds after clearing the SLI config
1459 * fields in sli_reset.
1460 */
1461 ocs_hw_workaround_setup(hw);
1462 hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
1463
1464 return rc;
1465 }
1466
1467 int32_t
ocs_hw_get_num_eq(ocs_hw_t * hw)1468 ocs_hw_get_num_eq(ocs_hw_t *hw)
1469 {
1470 return hw->eq_count;
1471 }
1472
1473 static int32_t
ocs_hw_get_fw_timed_out(ocs_hw_t * hw)1474 ocs_hw_get_fw_timed_out(ocs_hw_t *hw)
1475 {
1476 /* The error values below are taken from LOWLEVEL_SET_WATCHDOG_TIMER_rev1.pdf
1477 * No further explanation is given in the document.
1478 * */
1479 return (sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1) == 0x2 &&
1480 sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2) == 0x10);
1481 }
1482
1483
1484 ocs_hw_rtn_e
ocs_hw_get(ocs_hw_t * hw,ocs_hw_property_e prop,uint32_t * value)1485 ocs_hw_get(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t *value)
1486 {
1487 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1488 int32_t tmp;
1489
1490 if (!value) {
1491 return OCS_HW_RTN_ERROR;
1492 }
1493
1494 *value = 0;
1495
1496 switch (prop) {
1497 case OCS_HW_N_IO:
1498 *value = hw->config.n_io;
1499 break;
1500 case OCS_HW_N_SGL:
1501 *value = (hw->config.n_sgl - SLI4_SGE_MAX_RESERVED);
1502 break;
1503 case OCS_HW_MAX_IO:
1504 *value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
1505 break;
1506 case OCS_HW_MAX_NODES:
1507 *value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
1508 break;
1509 case OCS_HW_MAX_RQ_ENTRIES:
1510 *value = hw->num_qentries[SLI_QTYPE_RQ];
1511 break;
1512 case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1513 *value = hw->config.rq_default_buffer_size;
1514 break;
1515 case OCS_HW_AUTO_XFER_RDY_CAPABLE:
1516 *value = sli_get_auto_xfer_rdy_capable(&hw->sli);
1517 break;
1518 case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1519 *value = hw->config.auto_xfer_rdy_xri_cnt;
1520 break;
1521 case OCS_HW_AUTO_XFER_RDY_SIZE:
1522 *value = hw->config.auto_xfer_rdy_size;
1523 break;
1524 case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1525 switch (hw->config.auto_xfer_rdy_blk_size_chip) {
1526 case 0:
1527 *value = 512;
1528 break;
1529 case 1:
1530 *value = 1024;
1531 break;
1532 case 2:
1533 *value = 2048;
1534 break;
1535 case 3:
1536 *value = 4096;
1537 break;
1538 case 4:
1539 *value = 520;
1540 break;
1541 default:
1542 *value = 0;
1543 rc = OCS_HW_RTN_ERROR;
1544 break;
1545 }
1546 break;
1547 case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1548 *value = hw->config.auto_xfer_rdy_t10_enable;
1549 break;
1550 case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1551 *value = hw->config.auto_xfer_rdy_p_type;
1552 break;
1553 case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1554 *value = hw->config.auto_xfer_rdy_ref_tag_is_lba;
1555 break;
1556 case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1557 *value = hw->config.auto_xfer_rdy_app_tag_valid;
1558 break;
1559 case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1560 *value = hw->config.auto_xfer_rdy_app_tag_value;
1561 break;
1562 case OCS_HW_MAX_SGE:
1563 *value = sli_get_max_sge(&hw->sli);
1564 break;
1565 case OCS_HW_MAX_SGL:
1566 *value = sli_get_max_sgl(&hw->sli);
1567 break;
1568 case OCS_HW_TOPOLOGY:
1569 /*
1570 * Infer link.status based on link.speed.
1571 * Report OCS_HW_TOPOLOGY_NONE if the link is down.
1572 */
1573 if (hw->link.speed == 0) {
1574 *value = OCS_HW_TOPOLOGY_NONE;
1575 break;
1576 }
1577 switch (hw->link.topology) {
1578 case SLI_LINK_TOPO_NPORT:
1579 *value = OCS_HW_TOPOLOGY_NPORT;
1580 break;
1581 case SLI_LINK_TOPO_LOOP:
1582 *value = OCS_HW_TOPOLOGY_LOOP;
1583 break;
1584 case SLI_LINK_TOPO_NONE:
1585 *value = OCS_HW_TOPOLOGY_NONE;
1586 break;
1587 default:
1588 ocs_log_test(hw->os, "unsupported topology %#x\n", hw->link.topology);
1589 rc = OCS_HW_RTN_ERROR;
1590 break;
1591 }
1592 break;
1593 case OCS_HW_CONFIG_TOPOLOGY:
1594 *value = hw->config.topology;
1595 break;
1596 case OCS_HW_LINK_SPEED:
1597 *value = hw->link.speed;
1598 break;
1599 case OCS_HW_LINK_CONFIG_SPEED:
1600 switch (hw->config.speed) {
1601 case FC_LINK_SPEED_10G:
1602 *value = 10000;
1603 break;
1604 case FC_LINK_SPEED_AUTO_16_8_4:
1605 *value = 0;
1606 break;
1607 case FC_LINK_SPEED_2G:
1608 *value = 2000;
1609 break;
1610 case FC_LINK_SPEED_4G:
1611 *value = 4000;
1612 break;
1613 case FC_LINK_SPEED_8G:
1614 *value = 8000;
1615 break;
1616 case FC_LINK_SPEED_16G:
1617 *value = 16000;
1618 break;
1619 case FC_LINK_SPEED_32G:
1620 *value = 32000;
1621 break;
1622 default:
1623 ocs_log_test(hw->os, "unsupported speed %#x\n", hw->config.speed);
1624 rc = OCS_HW_RTN_ERROR;
1625 break;
1626 }
1627 break;
1628 case OCS_HW_IF_TYPE:
1629 *value = sli_get_if_type(&hw->sli);
1630 break;
1631 case OCS_HW_SLI_REV:
1632 *value = sli_get_sli_rev(&hw->sli);
1633 break;
1634 case OCS_HW_SLI_FAMILY:
1635 *value = sli_get_sli_family(&hw->sli);
1636 break;
1637 case OCS_HW_DIF_CAPABLE:
1638 *value = sli_get_dif_capable(&hw->sli);
1639 break;
1640 case OCS_HW_DIF_SEED:
1641 *value = hw->config.dif_seed;
1642 break;
1643 case OCS_HW_DIF_MODE:
1644 *value = hw->config.dif_mode;
1645 break;
1646 case OCS_HW_DIF_MULTI_SEPARATE:
1647 /* Lancer supports multiple DIF separates */
1648 if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
1649 *value = TRUE;
1650 } else {
1651 *value = FALSE;
1652 }
1653 break;
1654 case OCS_HW_DUMP_MAX_SIZE:
1655 *value = hw->dump_size;
1656 break;
1657 case OCS_HW_DUMP_READY:
1658 *value = sli_dump_is_ready(&hw->sli);
1659 break;
1660 case OCS_HW_DUMP_PRESENT:
1661 *value = sli_dump_is_present(&hw->sli);
1662 break;
1663 case OCS_HW_RESET_REQUIRED:
1664 tmp = sli_reset_required(&hw->sli);
1665 if(tmp < 0) {
1666 rc = OCS_HW_RTN_ERROR;
1667 } else {
1668 *value = tmp;
1669 }
1670 break;
1671 case OCS_HW_FW_ERROR:
1672 *value = sli_fw_error_status(&hw->sli);
1673 break;
1674 case OCS_HW_FW_READY:
1675 *value = sli_fw_ready(&hw->sli);
1676 break;
1677 case OCS_HW_FW_TIMED_OUT:
1678 *value = ocs_hw_get_fw_timed_out(hw);
1679 break;
1680 case OCS_HW_HIGH_LOGIN_MODE:
1681 *value = sli_get_hlm_capable(&hw->sli);
1682 break;
1683 case OCS_HW_PREREGISTER_SGL:
1684 *value = sli_get_sgl_preregister_required(&hw->sli);
1685 break;
1686 case OCS_HW_HW_REV1:
1687 *value = sli_get_hw_revision(&hw->sli, 0);
1688 break;
1689 case OCS_HW_HW_REV2:
1690 *value = sli_get_hw_revision(&hw->sli, 1);
1691 break;
1692 case OCS_HW_HW_REV3:
1693 *value = sli_get_hw_revision(&hw->sli, 2);
1694 break;
1695 case OCS_HW_LINKCFG:
1696 *value = hw->linkcfg;
1697 break;
1698 case OCS_HW_ETH_LICENSE:
1699 *value = hw->eth_license;
1700 break;
1701 case OCS_HW_LINK_MODULE_TYPE:
1702 *value = sli_get_link_module_type(&hw->sli);
1703 break;
1704 case OCS_HW_NUM_CHUTES:
1705 *value = ocs_hw_get_num_chutes(hw);
1706 break;
1707 case OCS_HW_DISABLE_AR_TGT_DIF:
1708 *value = hw->workaround.disable_ar_tgt_dif;
1709 break;
1710 case OCS_HW_EMULATE_I_ONLY_AAB:
1711 *value = hw->config.i_only_aab;
1712 break;
1713 case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
1714 *value = hw->config.emulate_tgt_wqe_timeout;
1715 break;
1716 case OCS_HW_VPD_LEN:
1717 *value = sli_get_vpd_len(&hw->sli);
1718 break;
1719 case OCS_HW_SGL_CHAINING_CAPABLE:
1720 *value = sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported;
1721 break;
1722 case OCS_HW_SGL_CHAINING_ALLOWED:
1723 /*
1724 * SGL Chaining is allowed in the following cases:
1725 * 1. Lancer with host SGL Lists
1726 * 2. Skyhawk with pre-registered SGL Lists
1727 */
1728 *value = FALSE;
1729 if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1730 !sli_get_sgl_preregister(&hw->sli) &&
1731 SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
1732 *value = TRUE;
1733 }
1734
1735 if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1736 sli_get_sgl_preregister(&hw->sli) &&
1737 ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
1738 (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
1739 *value = TRUE;
1740 }
1741 break;
1742 case OCS_HW_SGL_CHAINING_HOST_ALLOCATED:
1743 /* Only lancer supports host allocated SGL Chaining buffers. */
1744 *value = ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
1745 (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)));
1746 break;
1747 case OCS_HW_SEND_FRAME_CAPABLE:
1748 if (hw->workaround.ignore_send_frame) {
1749 *value = 0;
1750 } else {
1751 /* Only lancer is capable */
1752 *value = sli_get_if_type(&hw->sli) == SLI4_IF_TYPE_LANCER_FC_ETH;
1753 }
1754 break;
1755 case OCS_HW_RQ_SELECTION_POLICY:
1756 *value = hw->config.rq_selection_policy;
1757 break;
1758 case OCS_HW_RR_QUANTA:
1759 *value = hw->config.rr_quanta;
1760 break;
1761 case OCS_HW_MAX_VPORTS:
1762 *value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_VPI);
1763 default:
1764 ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1765 rc = OCS_HW_RTN_ERROR;
1766 }
1767
1768 return rc;
1769 }
1770
1771 void *
ocs_hw_get_ptr(ocs_hw_t * hw,ocs_hw_property_e prop)1772 ocs_hw_get_ptr(ocs_hw_t *hw, ocs_hw_property_e prop)
1773 {
1774 void *rc = NULL;
1775
1776 switch (prop) {
1777 case OCS_HW_WWN_NODE:
1778 rc = sli_get_wwn_node(&hw->sli);
1779 break;
1780 case OCS_HW_WWN_PORT:
1781 rc = sli_get_wwn_port(&hw->sli);
1782 break;
1783 case OCS_HW_VPD:
1784 /* make sure VPD length is non-zero */
1785 if (sli_get_vpd_len(&hw->sli)) {
1786 rc = sli_get_vpd(&hw->sli);
1787 }
1788 break;
1789 case OCS_HW_FW_REV:
1790 rc = sli_get_fw_name(&hw->sli, 0);
1791 break;
1792 case OCS_HW_FW_REV2:
1793 rc = sli_get_fw_name(&hw->sli, 1);
1794 break;
1795 case OCS_HW_IPL:
1796 rc = sli_get_ipl_name(&hw->sli);
1797 break;
1798 case OCS_HW_PORTNUM:
1799 rc = sli_get_portnum(&hw->sli);
1800 break;
1801 case OCS_HW_BIOS_VERSION_STRING:
1802 rc = sli_get_bios_version_string(&hw->sli);
1803 break;
1804 default:
1805 ocs_log_test(hw->os, "unsupported property %#x\n", prop);
1806 }
1807
1808 return rc;
1809 }
1810
1811
1812
1813 ocs_hw_rtn_e
ocs_hw_set(ocs_hw_t * hw,ocs_hw_property_e prop,uint32_t value)1814 ocs_hw_set(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t value)
1815 {
1816 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
1817
1818 switch (prop) {
1819 case OCS_HW_N_IO:
1820 if (value > sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI) ||
1821 value == 0) {
1822 ocs_log_test(hw->os, "IO value out of range %d vs %d\n",
1823 value, sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI));
1824 rc = OCS_HW_RTN_ERROR;
1825 } else {
1826 hw->config.n_io = value;
1827 }
1828 break;
1829 case OCS_HW_N_SGL:
1830 value += SLI4_SGE_MAX_RESERVED;
1831 if (value > sli_get_max_sgl(&hw->sli)) {
1832 ocs_log_test(hw->os, "SGL value out of range %d vs %d\n",
1833 value, sli_get_max_sgl(&hw->sli));
1834 rc = OCS_HW_RTN_ERROR;
1835 } else {
1836 hw->config.n_sgl = value;
1837 }
1838 break;
1839 case OCS_HW_TOPOLOGY:
1840 if ((sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) &&
1841 (value != OCS_HW_TOPOLOGY_AUTO)) {
1842 ocs_log_test(hw->os, "unsupported topology=%#x medium=%#x\n",
1843 value, sli_get_medium(&hw->sli));
1844 rc = OCS_HW_RTN_ERROR;
1845 break;
1846 }
1847
1848 switch (value) {
1849 case OCS_HW_TOPOLOGY_AUTO:
1850 if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
1851 sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC);
1852 } else {
1853 sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FCOE);
1854 }
1855 break;
1856 case OCS_HW_TOPOLOGY_NPORT:
1857 sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_DA);
1858 break;
1859 case OCS_HW_TOPOLOGY_LOOP:
1860 sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_AL);
1861 break;
1862 default:
1863 ocs_log_test(hw->os, "unsupported topology %#x\n", value);
1864 rc = OCS_HW_RTN_ERROR;
1865 }
1866 hw->config.topology = value;
1867 break;
1868 case OCS_HW_LINK_SPEED:
1869 if (sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) {
1870 switch (value) {
1871 case 0: /* Auto-speed negotiation */
1872 case 10000: /* FCoE speed */
1873 hw->config.speed = FC_LINK_SPEED_10G;
1874 break;
1875 default:
1876 ocs_log_test(hw->os, "unsupported speed=%#x medium=%#x\n",
1877 value, sli_get_medium(&hw->sli));
1878 rc = OCS_HW_RTN_ERROR;
1879 }
1880 break;
1881 }
1882
1883 switch (value) {
1884 case 0: /* Auto-speed negotiation */
1885 hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
1886 break;
1887 case 2000: /* FC speeds */
1888 hw->config.speed = FC_LINK_SPEED_2G;
1889 break;
1890 case 4000:
1891 hw->config.speed = FC_LINK_SPEED_4G;
1892 break;
1893 case 8000:
1894 hw->config.speed = FC_LINK_SPEED_8G;
1895 break;
1896 case 16000:
1897 hw->config.speed = FC_LINK_SPEED_16G;
1898 break;
1899 case 32000:
1900 hw->config.speed = FC_LINK_SPEED_32G;
1901 break;
1902 default:
1903 ocs_log_test(hw->os, "unsupported speed %d\n", value);
1904 rc = OCS_HW_RTN_ERROR;
1905 }
1906 break;
1907 case OCS_HW_DIF_SEED:
1908 /* Set the DIF seed - only for lancer right now */
1909 if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
1910 ocs_log_test(hw->os, "DIF seed not supported for this device\n");
1911 rc = OCS_HW_RTN_ERROR;
1912 } else {
1913 hw->config.dif_seed = value;
1914 }
1915 break;
1916 case OCS_HW_DIF_MODE:
1917 switch (value) {
1918 case OCS_HW_DIF_MODE_INLINE:
1919 /*
1920 * Make sure we support inline DIF.
1921 *
1922 * Note: Having both bits clear means that we have old
1923 * FW that doesn't set the bits.
1924 */
1925 if (sli_is_dif_inline_capable(&hw->sli)) {
1926 hw->config.dif_mode = value;
1927 } else {
1928 ocs_log_test(hw->os, "chip does not support DIF inline\n");
1929 rc = OCS_HW_RTN_ERROR;
1930 }
1931 break;
1932 case OCS_HW_DIF_MODE_SEPARATE:
1933 /* Make sure we support DIF separates. */
1934 if (sli_is_dif_separate_capable(&hw->sli)) {
1935 hw->config.dif_mode = value;
1936 } else {
1937 ocs_log_test(hw->os, "chip does not support DIF separate\n");
1938 rc = OCS_HW_RTN_ERROR;
1939 }
1940 }
1941 break;
1942 case OCS_HW_RQ_PROCESS_LIMIT: {
1943 hw_rq_t *rq;
1944 uint32_t i;
1945
1946 /* For each hw_rq object, set its parent CQ limit value */
1947 for (i = 0; i < hw->hw_rq_count; i++) {
1948 rq = hw->hw_rq[i];
1949 hw->cq[rq->cq->instance].proc_limit = value;
1950 }
1951 break;
1952 }
1953 case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
1954 hw->config.rq_default_buffer_size = value;
1955 break;
1956 case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
1957 hw->config.auto_xfer_rdy_xri_cnt = value;
1958 break;
1959 case OCS_HW_AUTO_XFER_RDY_SIZE:
1960 hw->config.auto_xfer_rdy_size = value;
1961 break;
1962 case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
1963 switch (value) {
1964 case 512:
1965 hw->config.auto_xfer_rdy_blk_size_chip = 0;
1966 break;
1967 case 1024:
1968 hw->config.auto_xfer_rdy_blk_size_chip = 1;
1969 break;
1970 case 2048:
1971 hw->config.auto_xfer_rdy_blk_size_chip = 2;
1972 break;
1973 case 4096:
1974 hw->config.auto_xfer_rdy_blk_size_chip = 3;
1975 break;
1976 case 520:
1977 hw->config.auto_xfer_rdy_blk_size_chip = 4;
1978 break;
1979 default:
1980 ocs_log_err(hw->os, "Invalid block size %d\n",
1981 value);
1982 rc = OCS_HW_RTN_ERROR;
1983 }
1984 break;
1985 case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
1986 hw->config.auto_xfer_rdy_t10_enable = value;
1987 break;
1988 case OCS_HW_AUTO_XFER_RDY_P_TYPE:
1989 hw->config.auto_xfer_rdy_p_type = value;
1990 break;
1991 case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
1992 hw->config.auto_xfer_rdy_ref_tag_is_lba = value;
1993 break;
1994 case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
1995 hw->config.auto_xfer_rdy_app_tag_valid = value;
1996 break;
1997 case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
1998 hw->config.auto_xfer_rdy_app_tag_value = value;
1999 break;
2000 case OCS_ESOC:
2001 hw->config.esoc = value;
2002 case OCS_HW_HIGH_LOGIN_MODE:
2003 rc = sli_set_hlm(&hw->sli, value);
2004 break;
2005 case OCS_HW_PREREGISTER_SGL:
2006 rc = sli_set_sgl_preregister(&hw->sli, value);
2007 break;
2008 case OCS_HW_ETH_LICENSE:
2009 hw->eth_license = value;
2010 break;
2011 case OCS_HW_EMULATE_I_ONLY_AAB:
2012 hw->config.i_only_aab = value;
2013 break;
2014 case OCS_HW_EMULATE_TARGET_WQE_TIMEOUT:
2015 hw->config.emulate_tgt_wqe_timeout = value;
2016 break;
2017 case OCS_HW_BOUNCE:
2018 hw->config.bounce = value;
2019 break;
2020 case OCS_HW_RQ_SELECTION_POLICY:
2021 hw->config.rq_selection_policy = value;
2022 break;
2023 case OCS_HW_RR_QUANTA:
2024 hw->config.rr_quanta = value;
2025 break;
2026 default:
2027 ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2028 rc = OCS_HW_RTN_ERROR;
2029 }
2030
2031 return rc;
2032 }
2033
2034
2035 ocs_hw_rtn_e
ocs_hw_set_ptr(ocs_hw_t * hw,ocs_hw_property_e prop,void * value)2036 ocs_hw_set_ptr(ocs_hw_t *hw, ocs_hw_property_e prop, void *value)
2037 {
2038 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2039
2040 switch (prop) {
2041 case OCS_HW_WAR_VERSION:
2042 hw->hw_war_version = value;
2043 break;
2044 case OCS_HW_FILTER_DEF: {
2045 char *p = value;
2046 uint32_t idx = 0;
2047
2048 for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) {
2049 hw->config.filter_def[idx] = 0;
2050 }
2051
2052 for (idx = 0; (idx < ARRAY_SIZE(hw->config.filter_def)) && (p != NULL) && *p; ) {
2053 hw->config.filter_def[idx++] = ocs_strtoul(p, 0, 0);
2054 p = ocs_strchr(p, ',');
2055 if (p != NULL) {
2056 p++;
2057 }
2058 }
2059
2060 break;
2061 }
2062 default:
2063 ocs_log_test(hw->os, "unsupported property %#x\n", prop);
2064 rc = OCS_HW_RTN_ERROR;
2065 break;
2066 }
2067 return rc;
2068 }
2069 /**
2070 * @ingroup interrupt
2071 * @brief Check for the events associated with the interrupt vector.
2072 *
2073 * @param hw Hardware context.
2074 * @param vector Zero-based interrupt vector number.
2075 *
2076 * @return Returns 0 on success, or a non-zero value on failure.
2077 */
2078 int32_t
ocs_hw_event_check(ocs_hw_t * hw,uint32_t vector)2079 ocs_hw_event_check(ocs_hw_t *hw, uint32_t vector)
2080 {
2081 int32_t rc = 0;
2082
2083 if (!hw) {
2084 ocs_log_err(NULL, "HW context NULL?!?\n");
2085 return -1;
2086 }
2087
2088 if (vector > hw->eq_count) {
2089 ocs_log_err(hw->os, "vector %d. max %d\n",
2090 vector, hw->eq_count);
2091 return -1;
2092 }
2093
2094 /*
2095 * The caller should disable interrupts if they wish to prevent us
2096 * from processing during a shutdown. The following states are defined:
2097 * OCS_HW_STATE_UNINITIALIZED - No queues allocated
2098 * OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2099 * queues are cleared.
2100 * OCS_HW_STATE_ACTIVE - Chip and queues are operational
2101 * OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2102 * OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2103 * completions.
2104 */
2105 if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
2106 rc = sli_queue_is_empty(&hw->sli, &hw->eq[vector]);
2107
2108 /* Re-arm queue if there are no entries */
2109 if (rc != 0) {
2110 sli_queue_arm(&hw->sli, &hw->eq[vector], TRUE);
2111 }
2112 }
2113 return rc;
2114 }
2115
2116 void
ocs_hw_unsol_process_bounce(void * arg)2117 ocs_hw_unsol_process_bounce(void *arg)
2118 {
2119 ocs_hw_sequence_t *seq = arg;
2120 ocs_hw_t *hw = seq->hw;
2121
2122 ocs_hw_assert(hw != NULL);
2123 ocs_hw_assert(hw->callback.unsolicited != NULL);
2124
2125 hw->callback.unsolicited(hw->args.unsolicited, seq);
2126 }
2127
2128 int32_t
ocs_hw_process(ocs_hw_t * hw,uint32_t vector,uint32_t max_isr_time_msec)2129 ocs_hw_process(ocs_hw_t *hw, uint32_t vector, uint32_t max_isr_time_msec)
2130 {
2131 hw_eq_t *eq;
2132 int32_t rc = 0;
2133
2134 CPUTRACE("");
2135
2136 /*
2137 * The caller should disable interrupts if they wish to prevent us
2138 * from processing during a shutdown. The following states are defined:
2139 * OCS_HW_STATE_UNINITIALIZED - No queues allocated
2140 * OCS_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
2141 * queues are cleared.
2142 * OCS_HW_STATE_ACTIVE - Chip and queues are operational
2143 * OCS_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
2144 * OCS_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
2145 * completions.
2146 */
2147 if (hw->state == OCS_HW_STATE_UNINITIALIZED) {
2148 return 0;
2149 }
2150
2151 /* Get pointer to hw_eq_t */
2152 eq = hw->hw_eq[vector];
2153
2154 OCS_STAT(eq->use_count++);
2155
2156 rc = ocs_hw_eq_process(hw, eq, max_isr_time_msec);
2157
2158 return rc;
2159 }
2160
2161 /**
2162 * @ingroup interrupt
2163 * @brief Process events associated with an EQ.
2164 *
2165 * @par Description
2166 * Loop termination:
2167 * @n @n Without a mechanism to terminate the completion processing loop, it
2168 * is possible under some workload conditions for the loop to never terminate
2169 * (or at least take longer than the OS is happy to have an interrupt handler
2170 * or kernel thread context hold a CPU without yielding).
2171 * @n @n The approach taken here is to periodically check how much time
2172 * we have been in this
2173 * processing loop, and if we exceed a predetermined time (multiple seconds), the
2174 * loop is terminated, and ocs_hw_process() returns.
2175 *
2176 * @param hw Hardware context.
2177 * @param eq Pointer to HW EQ object.
2178 * @param max_isr_time_msec Maximum time in msec to stay in this function.
2179 *
2180 * @return Returns 0 on success, or a non-zero value on failure.
2181 */
2182 int32_t
ocs_hw_eq_process(ocs_hw_t * hw,hw_eq_t * eq,uint32_t max_isr_time_msec)2183 ocs_hw_eq_process(ocs_hw_t *hw, hw_eq_t *eq, uint32_t max_isr_time_msec)
2184 {
2185 uint8_t eqe[sizeof(sli4_eqe_t)] = { 0 };
2186 uint32_t done = FALSE;
2187 uint32_t tcheck_count;
2188 time_t tstart;
2189 time_t telapsed;
2190
2191 tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2192 tstart = ocs_msectime();
2193
2194 CPUTRACE("");
2195
2196 while (!done && !sli_queue_read(&hw->sli, eq->queue, eqe)) {
2197 uint16_t cq_id = 0;
2198 int32_t rc;
2199
2200 rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
2201 if (unlikely(rc)) {
2202 if (rc > 0) {
2203 uint32_t i;
2204
2205 /*
2206 * Received a sentinel EQE indicating the EQ is full.
2207 * Process all CQs
2208 */
2209 for (i = 0; i < hw->cq_count; i++) {
2210 ocs_hw_cq_process(hw, hw->hw_cq[i]);
2211 }
2212 continue;
2213 } else {
2214 return rc;
2215 }
2216 } else {
2217 int32_t index = ocs_hw_queue_hash_find(hw->cq_hash, cq_id);
2218 if (likely(index >= 0)) {
2219 ocs_hw_cq_process(hw, hw->hw_cq[index]);
2220 } else {
2221 ocs_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
2222 }
2223 }
2224
2225
2226 if (eq->queue->n_posted > (eq->queue->posted_limit)) {
2227 sli_queue_arm(&hw->sli, eq->queue, FALSE);
2228 }
2229
2230 if (tcheck_count && (--tcheck_count == 0)) {
2231 tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
2232 telapsed = ocs_msectime() - tstart;
2233 if (telapsed >= max_isr_time_msec) {
2234 done = TRUE;
2235 }
2236 }
2237 }
2238 sli_queue_eq_arm(&hw->sli, eq->queue, TRUE);
2239
2240 return 0;
2241 }
2242
2243 /**
2244 * @brief Submit queued (pending) mbx commands.
2245 *
2246 * @par Description
2247 * Submit queued mailbox commands.
2248 * --- Assumes that hw->cmd_lock is held ---
2249 *
2250 * @param hw Hardware context.
2251 *
2252 * @return Returns 0 on success, or a negative error code value on failure.
2253 */
2254 static int32_t
ocs_hw_cmd_submit_pending(ocs_hw_t * hw)2255 ocs_hw_cmd_submit_pending(ocs_hw_t *hw)
2256 {
2257 ocs_command_ctx_t *ctx;
2258 int32_t rc = 0;
2259
2260 /* Assumes lock held */
2261
2262 /* Only submit MQE if there's room */
2263 while (hw->cmd_head_count < (OCS_HW_MQ_DEPTH - 1)) {
2264 ctx = ocs_list_remove_head(&hw->cmd_pending);
2265 if (ctx == NULL) {
2266 break;
2267 }
2268 ocs_list_add_tail(&hw->cmd_head, ctx);
2269 hw->cmd_head_count++;
2270 if (sli_queue_write(&hw->sli, hw->mq, ctx->buf) < 0) {
2271 ocs_log_test(hw->os, "sli_queue_write failed: %d\n", rc);
2272 rc = -1;
2273 break;
2274 }
2275 }
2276 return rc;
2277 }
2278
2279 /**
2280 * @ingroup io
2281 * @brief Issue a SLI command.
2282 *
2283 * @par Description
2284 * Send a mailbox command to the hardware, and either wait for a completion
2285 * (OCS_CMD_POLL) or get an optional asynchronous completion (OCS_CMD_NOWAIT).
2286 *
2287 * @param hw Hardware context.
2288 * @param cmd Buffer containing a formatted command and results.
2289 * @param opts Command options:
2290 * - OCS_CMD_POLL - Command executes synchronously and busy-waits for the completion.
2291 * - OCS_CMD_NOWAIT - Command executes asynchronously. Uses callback.
2292 * @param cb Function callback used for asynchronous mode. May be NULL.
2293 * @n Prototype is <tt>(*cb)(void *arg, uint8_t *cmd)</tt>.
2294 * @n @n @b Note: If the
2295 * callback function pointer is NULL, the results of the command are silently
2296 * discarded, allowing this pointer to exist solely on the stack.
2297 * @param arg Argument passed to an asynchronous callback.
2298 *
2299 * @return Returns 0 on success, or a non-zero value on failure.
2300 */
2301 ocs_hw_rtn_e
ocs_hw_command(ocs_hw_t * hw,uint8_t * cmd,uint32_t opts,void * cb,void * arg)2302 ocs_hw_command(ocs_hw_t *hw, uint8_t *cmd, uint32_t opts, void *cb, void *arg)
2303 {
2304 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2305
2306 /*
2307 * If the chip is in an error state (UE'd) then reject this mailbox
2308 * command.
2309 */
2310 if (sli_fw_error_status(&hw->sli) > 0) {
2311 uint32_t err1 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1);
2312 uint32_t err2 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2);
2313 if (hw->expiration_logged == 0 && err1 == 0x2 && err2 == 0x10) {
2314 hw->expiration_logged = 1;
2315 ocs_log_crit(hw->os,"Emulex: Heartbeat expired after %d seconds\n",
2316 hw->watchdog_timeout);
2317 }
2318 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2319 ocs_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
2320 sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_STATUS),
2321 err1, err2);
2322
2323 return OCS_HW_RTN_ERROR;
2324 }
2325
2326 if (OCS_CMD_POLL == opts) {
2327
2328 ocs_lock(&hw->cmd_lock);
2329 if (hw->mq->length && !sli_queue_is_empty(&hw->sli, hw->mq)) {
2330 /*
2331 * Can't issue Boot-strap mailbox command with other
2332 * mail-queue commands pending as this interaction is
2333 * undefined
2334 */
2335 rc = OCS_HW_RTN_ERROR;
2336 } else {
2337 void *bmbx = hw->sli.bmbx.virt;
2338
2339 ocs_memset(bmbx, 0, SLI4_BMBX_SIZE);
2340 ocs_memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
2341
2342 if (sli_bmbx_command(&hw->sli) == 0) {
2343 rc = OCS_HW_RTN_SUCCESS;
2344 ocs_memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
2345 }
2346 }
2347 ocs_unlock(&hw->cmd_lock);
2348 } else if (OCS_CMD_NOWAIT == opts) {
2349 ocs_command_ctx_t *ctx = NULL;
2350
2351 ctx = ocs_malloc(hw->os, sizeof(ocs_command_ctx_t), OCS_M_ZERO | OCS_M_NOWAIT);
2352 if (!ctx) {
2353 ocs_log_err(hw->os, "can't allocate command context\n");
2354 return OCS_HW_RTN_NO_RESOURCES;
2355 }
2356
2357 if (hw->state != OCS_HW_STATE_ACTIVE) {
2358 ocs_log_err(hw->os, "Can't send command, HW state=%d\n", hw->state);
2359 ocs_free(hw->os, ctx, sizeof(*ctx));
2360 return OCS_HW_RTN_ERROR;
2361 }
2362
2363 if (cb) {
2364 ctx->cb = cb;
2365 ctx->arg = arg;
2366 }
2367 ctx->buf = cmd;
2368 ctx->ctx = hw;
2369
2370 ocs_lock(&hw->cmd_lock);
2371
2372 /* Add to pending list */
2373 ocs_list_add_tail(&hw->cmd_pending, ctx);
2374
2375 /* Submit as much of the pending list as we can */
2376 if (ocs_hw_cmd_submit_pending(hw) == 0) {
2377 rc = OCS_HW_RTN_SUCCESS;
2378 }
2379
2380 ocs_unlock(&hw->cmd_lock);
2381 }
2382
2383 return rc;
2384 }
2385
2386 /**
2387 * @ingroup devInitShutdown
2388 * @brief Register a callback for the given event.
2389 *
2390 * @param hw Hardware context.
2391 * @param which Event of interest.
2392 * @param func Function to call when the event occurs.
2393 * @param arg Argument passed to the callback function.
2394 *
2395 * @return Returns 0 on success, or a non-zero value on failure.
2396 */
2397 ocs_hw_rtn_e
ocs_hw_callback(ocs_hw_t * hw,ocs_hw_callback_e which,void * func,void * arg)2398 ocs_hw_callback(ocs_hw_t *hw, ocs_hw_callback_e which, void *func, void *arg)
2399 {
2400
2401 if (!hw || !func || (which >= OCS_HW_CB_MAX)) {
2402 ocs_log_err(NULL, "bad parameter hw=%p which=%#x func=%p\n",
2403 hw, which, func);
2404 return OCS_HW_RTN_ERROR;
2405 }
2406
2407 switch (which) {
2408 case OCS_HW_CB_DOMAIN:
2409 hw->callback.domain = func;
2410 hw->args.domain = arg;
2411 break;
2412 case OCS_HW_CB_PORT:
2413 hw->callback.port = func;
2414 hw->args.port = arg;
2415 break;
2416 case OCS_HW_CB_UNSOLICITED:
2417 hw->callback.unsolicited = func;
2418 hw->args.unsolicited = arg;
2419 break;
2420 case OCS_HW_CB_REMOTE_NODE:
2421 hw->callback.rnode = func;
2422 hw->args.rnode = arg;
2423 break;
2424 case OCS_HW_CB_BOUNCE:
2425 hw->callback.bounce = func;
2426 hw->args.bounce = arg;
2427 break;
2428 default:
2429 ocs_log_test(hw->os, "unknown callback %#x\n", which);
2430 return OCS_HW_RTN_ERROR;
2431 }
2432
2433 return OCS_HW_RTN_SUCCESS;
2434 }
2435
2436 /**
2437 * @ingroup port
2438 * @brief Allocate a port object.
2439 *
2440 * @par Description
2441 * This function allocates a VPI object for the port and stores it in the
2442 * indicator field of the port object.
2443 *
2444 * @param hw Hardware context.
2445 * @param sport SLI port object used to connect to the domain.
2446 * @param domain Domain object associated with this port (may be NULL).
2447 * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
2448 *
2449 * @return Returns 0 on success, or a non-zero value on failure.
2450 */
2451 ocs_hw_rtn_e
ocs_hw_port_alloc(ocs_hw_t * hw,ocs_sli_port_t * sport,ocs_domain_t * domain,uint8_t * wwpn)2452 ocs_hw_port_alloc(ocs_hw_t *hw, ocs_sli_port_t *sport, ocs_domain_t *domain,
2453 uint8_t *wwpn)
2454 {
2455 uint8_t *cmd = NULL;
2456 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2457 uint32_t index;
2458
2459 sport->indicator = UINT32_MAX;
2460 sport->hw = hw;
2461 sport->ctx.app = sport;
2462 sport->sm_free_req_pending = 0;
2463
2464 /*
2465 * Check if the chip is in an error state (UE'd) before proceeding.
2466 */
2467 if (sli_fw_error_status(&hw->sli) > 0) {
2468 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2469 return OCS_HW_RTN_ERROR;
2470 }
2471
2472 if (wwpn) {
2473 ocs_memcpy(&sport->sli_wwpn, wwpn, sizeof(sport->sli_wwpn));
2474 }
2475
2476 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VPI, &sport->indicator, &index)) {
2477 ocs_log_err(hw->os, "FCOE_VPI allocation failure\n");
2478 return OCS_HW_RTN_ERROR;
2479 }
2480
2481 if (domain != NULL) {
2482 ocs_sm_function_t next = NULL;
2483
2484 cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2485 if (!cmd) {
2486 ocs_log_err(hw->os, "command memory allocation failed\n");
2487 rc = OCS_HW_RTN_NO_MEMORY;
2488 goto ocs_hw_port_alloc_out;
2489 }
2490
2491 /* If the WWPN is NULL, fetch the default WWPN and WWNN before
2492 * initializing the VPI
2493 */
2494 if (!wwpn) {
2495 next = __ocs_hw_port_alloc_read_sparm64;
2496 } else {
2497 next = __ocs_hw_port_alloc_init_vpi;
2498 }
2499
2500 ocs_sm_transition(&sport->ctx, next, cmd);
2501 } else if (!wwpn) {
2502 /* This is the convention for the HW, not SLI */
2503 ocs_log_test(hw->os, "need WWN for physical port\n");
2504 rc = OCS_HW_RTN_ERROR;
2505 } else {
2506 /* domain NULL and wwpn non-NULL */
2507 ocs_sm_transition(&sport->ctx, __ocs_hw_port_alloc_init, NULL);
2508 }
2509
2510 ocs_hw_port_alloc_out:
2511 if (rc != OCS_HW_RTN_SUCCESS) {
2512 ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2513
2514 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
2515 }
2516
2517 return rc;
2518 }
2519
2520 /**
2521 * @ingroup port
2522 * @brief Attach a physical/virtual SLI port to a domain.
2523 *
2524 * @par Description
2525 * This function registers a previously-allocated VPI with the
2526 * device.
2527 *
2528 * @param hw Hardware context.
2529 * @param sport Pointer to the SLI port object.
2530 * @param fc_id Fibre Channel ID to associate with this port.
2531 *
2532 * @return Returns OCS_HW_RTN_SUCCESS on success, or an error code on failure.
2533 */
2534 ocs_hw_rtn_e
ocs_hw_port_attach(ocs_hw_t * hw,ocs_sli_port_t * sport,uint32_t fc_id)2535 ocs_hw_port_attach(ocs_hw_t *hw, ocs_sli_port_t *sport, uint32_t fc_id)
2536 {
2537 uint8_t *buf = NULL;
2538 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2539
2540 if (!hw || !sport) {
2541 ocs_log_err(hw ? hw->os : NULL,
2542 "bad parameter(s) hw=%p sport=%p\n", hw,
2543 sport);
2544 return OCS_HW_RTN_ERROR;
2545 }
2546
2547 /*
2548 * Check if the chip is in an error state (UE'd) before proceeding.
2549 */
2550 if (sli_fw_error_status(&hw->sli) > 0) {
2551 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2552 return OCS_HW_RTN_ERROR;
2553 }
2554
2555 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2556 if (!buf) {
2557 ocs_log_err(hw->os, "no buffer for command\n");
2558 return OCS_HW_RTN_NO_MEMORY;
2559 }
2560
2561 sport->fc_id = fc_id;
2562 ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_ATTACH, buf);
2563 return rc;
2564 }
2565
2566 /**
2567 * @brief Called when the port control command completes.
2568 *
2569 * @par Description
2570 * We only need to free the mailbox command buffer.
2571 *
2572 * @param hw Hardware context.
2573 * @param status Status field from the mbox completion.
2574 * @param mqe Mailbox response structure.
2575 * @param arg Pointer to a callback function that signals the caller that the command is done.
2576 *
2577 * @return Returns 0.
2578 */
2579 static int32_t
ocs_hw_cb_port_control(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)2580 ocs_hw_cb_port_control(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
2581 {
2582 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
2583 return 0;
2584 }
2585
2586 /**
2587 * @ingroup port
2588 * @brief Control a port (initialize, shutdown, or set link configuration).
2589 *
2590 * @par Description
2591 * This function controls a port depending on the @c ctrl parameter:
2592 * - @b OCS_HW_PORT_INIT -
2593 * Issues the CONFIG_LINK and INIT_LINK commands for the specified port.
2594 * The HW generates an OCS_HW_DOMAIN_FOUND event when the link comes up.
2595 * .
2596 * - @b OCS_HW_PORT_SHUTDOWN -
2597 * Issues the DOWN_LINK command for the specified port.
2598 * The HW generates an OCS_HW_DOMAIN_LOST event when the link is down.
2599 * .
2600 * - @b OCS_HW_PORT_SET_LINK_CONFIG -
2601 * Sets the link configuration.
2602 *
2603 * @param hw Hardware context.
2604 * @param ctrl Specifies the operation:
2605 * - OCS_HW_PORT_INIT
2606 * - OCS_HW_PORT_SHUTDOWN
2607 * - OCS_HW_PORT_SET_LINK_CONFIG
2608 *
2609 * @param value Operation-specific value.
2610 * - OCS_HW_PORT_INIT - Selective reset AL_PA
2611 * - OCS_HW_PORT_SHUTDOWN - N/A
2612 * - OCS_HW_PORT_SET_LINK_CONFIG - An enum #ocs_hw_linkcfg_e value.
2613 *
2614 * @param cb Callback function to invoke the following operation.
2615 * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2616 * are handled by the OCS_HW_CB_DOMAIN callbacks).
2617 * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2618 * completes.
2619 *
2620 * @param arg Callback argument invoked after the command completes.
2621 * - OCS_HW_PORT_INIT/OCS_HW_PORT_SHUTDOWN - NULL (link events
2622 * are handled by the OCS_HW_CB_DOMAIN callbacks).
2623 * - OCS_HW_PORT_SET_LINK_CONFIG - Invoked after linkcfg mailbox command
2624 * completes.
2625 *
2626 * @return Returns 0 on success, or a non-zero value on failure.
2627 */
2628 ocs_hw_rtn_e
ocs_hw_port_control(ocs_hw_t * hw,ocs_hw_port_e ctrl,uintptr_t value,ocs_hw_port_control_cb_t cb,void * arg)2629 ocs_hw_port_control(ocs_hw_t *hw, ocs_hw_port_e ctrl, uintptr_t value, ocs_hw_port_control_cb_t cb, void *arg)
2630 {
2631 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
2632
2633 switch (ctrl) {
2634 case OCS_HW_PORT_INIT:
2635 {
2636 uint8_t *init_link;
2637 uint32_t speed = 0;
2638 uint8_t reset_alpa = 0;
2639
2640 if (SLI_LINK_MEDIUM_FC == sli_get_medium(&hw->sli)) {
2641 uint8_t *cfg_link;
2642
2643 cfg_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2644 if (cfg_link == NULL) {
2645 ocs_log_err(hw->os, "no buffer for command\n");
2646 return OCS_HW_RTN_NO_MEMORY;
2647 }
2648
2649 if (sli_cmd_config_link(&hw->sli, cfg_link, SLI4_BMBX_SIZE)) {
2650 rc = ocs_hw_command(hw, cfg_link, OCS_CMD_NOWAIT,
2651 ocs_hw_cb_port_control, NULL);
2652 }
2653
2654 if (rc != OCS_HW_RTN_SUCCESS) {
2655 ocs_free(hw->os, cfg_link, SLI4_BMBX_SIZE);
2656 ocs_log_err(hw->os, "CONFIG_LINK failed\n");
2657 break;
2658 }
2659 speed = hw->config.speed;
2660 reset_alpa = (uint8_t)(value & 0xff);
2661 } else {
2662 speed = FC_LINK_SPEED_10G;
2663 }
2664
2665 /*
2666 * Bring link up, unless FW version is not supported
2667 */
2668 if (hw->workaround.fw_version_too_low) {
2669 if (SLI4_IF_TYPE_LANCER_FC_ETH == hw->sli.if_type) {
2670 ocs_log_err(hw->os, "Cannot bring up link. Please update firmware to %s or later (current version is %s)\n",
2671 OCS_FW_VER_STR(OCS_MIN_FW_VER_LANCER), (char *) sli_get_fw_name(&hw->sli,0));
2672 } else {
2673 ocs_log_err(hw->os, "Cannot bring up link. Please update firmware to %s or later (current version is %s)\n",
2674 OCS_FW_VER_STR(OCS_MIN_FW_VER_SKYHAWK), (char *) sli_get_fw_name(&hw->sli, 0));
2675 }
2676
2677 return OCS_HW_RTN_ERROR;
2678 }
2679
2680 rc = OCS_HW_RTN_ERROR;
2681
2682 /* Allocate a new buffer for the init_link command */
2683 init_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2684 if (init_link == NULL) {
2685 ocs_log_err(hw->os, "no buffer for command\n");
2686 return OCS_HW_RTN_NO_MEMORY;
2687 }
2688
2689
2690 if (sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE, speed, reset_alpa)) {
2691 rc = ocs_hw_command(hw, init_link, OCS_CMD_NOWAIT,
2692 ocs_hw_cb_port_control, NULL);
2693 }
2694 /* Free buffer on error, since no callback is coming */
2695 if (rc != OCS_HW_RTN_SUCCESS) {
2696 ocs_free(hw->os, init_link, SLI4_BMBX_SIZE);
2697 ocs_log_err(hw->os, "INIT_LINK failed\n");
2698 }
2699 break;
2700 }
2701 case OCS_HW_PORT_SHUTDOWN:
2702 {
2703 uint8_t *down_link;
2704
2705 down_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2706 if (down_link == NULL) {
2707 ocs_log_err(hw->os, "no buffer for command\n");
2708 return OCS_HW_RTN_NO_MEMORY;
2709 }
2710 if (sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE)) {
2711 rc = ocs_hw_command(hw, down_link, OCS_CMD_NOWAIT,
2712 ocs_hw_cb_port_control, NULL);
2713 }
2714 /* Free buffer on error, since no callback is coming */
2715 if (rc != OCS_HW_RTN_SUCCESS) {
2716 ocs_free(hw->os, down_link, SLI4_BMBX_SIZE);
2717 ocs_log_err(hw->os, "DOWN_LINK failed\n");
2718 }
2719 break;
2720 }
2721 case OCS_HW_PORT_SET_LINK_CONFIG:
2722 rc = ocs_hw_set_linkcfg(hw, (ocs_hw_linkcfg_e)value, OCS_CMD_NOWAIT, cb, arg);
2723 break;
2724 default:
2725 ocs_log_test(hw->os, "unhandled control %#x\n", ctrl);
2726 break;
2727 }
2728
2729 return rc;
2730 }
2731
2732
2733 /**
2734 * @ingroup port
2735 * @brief Free port resources.
2736 *
2737 * @par Description
2738 * Issue the UNREG_VPI command to free the assigned VPI context.
2739 *
2740 * @param hw Hardware context.
2741 * @param sport SLI port object used to connect to the domain.
2742 *
2743 * @return Returns 0 on success, or a non-zero value on failure.
2744 */
2745 ocs_hw_rtn_e
ocs_hw_port_free(ocs_hw_t * hw,ocs_sli_port_t * sport)2746 ocs_hw_port_free(ocs_hw_t *hw, ocs_sli_port_t *sport)
2747 {
2748 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2749
2750 if (!hw || !sport) {
2751 ocs_log_err(hw ? hw->os : NULL,
2752 "bad parameter(s) hw=%p sport=%p\n", hw,
2753 sport);
2754 return OCS_HW_RTN_ERROR;
2755 }
2756
2757 /*
2758 * Check if the chip is in an error state (UE'd) before proceeding.
2759 */
2760 if (sli_fw_error_status(&hw->sli) > 0) {
2761 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2762 return OCS_HW_RTN_ERROR;
2763 }
2764
2765 ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_FREE, NULL);
2766 return rc;
2767 }
2768
2769 /**
2770 * @ingroup domain
2771 * @brief Allocate a fabric domain object.
2772 *
2773 * @par Description
2774 * This function starts a series of commands needed to connect to the domain, including
2775 * - REG_FCFI
2776 * - INIT_VFI
2777 * - READ_SPARMS
2778 * .
2779 * @b Note: Not all SLI interface types use all of the above commands.
2780 * @n @n Upon successful allocation, the HW generates a OCS_HW_DOMAIN_ALLOC_OK
2781 * event. On failure, it generates a OCS_HW_DOMAIN_ALLOC_FAIL event.
2782 *
2783 * @param hw Hardware context.
2784 * @param domain Pointer to the domain object.
2785 * @param fcf FCF index.
2786 * @param vlan VLAN ID.
2787 *
2788 * @return Returns 0 on success, or a non-zero value on failure.
2789 */
2790 ocs_hw_rtn_e
ocs_hw_domain_alloc(ocs_hw_t * hw,ocs_domain_t * domain,uint32_t fcf,uint32_t vlan)2791 ocs_hw_domain_alloc(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fcf, uint32_t vlan)
2792 {
2793 uint8_t *cmd = NULL;
2794 uint32_t index;
2795
2796 if (!hw || !domain || !domain->sport) {
2797 ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p sport=%p\n",
2798 hw, domain, domain ? domain->sport : NULL);
2799 return OCS_HW_RTN_ERROR;
2800 }
2801
2802 /*
2803 * Check if the chip is in an error state (UE'd) before proceeding.
2804 */
2805 if (sli_fw_error_status(&hw->sli) > 0) {
2806 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2807 return OCS_HW_RTN_ERROR;
2808 }
2809
2810 cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
2811 if (!cmd) {
2812 ocs_log_err(hw->os, "command memory allocation failed\n");
2813 return OCS_HW_RTN_NO_MEMORY;
2814 }
2815
2816 domain->dma = hw->domain_dmem;
2817
2818 domain->hw = hw;
2819 domain->sm.app = domain;
2820 domain->fcf = fcf;
2821 domain->fcf_indicator = UINT32_MAX;
2822 domain->vlan_id = vlan;
2823 domain->indicator = UINT32_MAX;
2824
2825 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VFI, &domain->indicator, &index)) {
2826 ocs_log_err(hw->os, "FCOE_VFI allocation failure\n");
2827
2828 ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
2829
2830 return OCS_HW_RTN_ERROR;
2831 }
2832
2833 ocs_sm_transition(&domain->sm, __ocs_hw_domain_init, cmd);
2834 return OCS_HW_RTN_SUCCESS;
2835 }
2836
2837 /**
2838 * @ingroup domain
2839 * @brief Attach a SLI port to a domain.
2840 *
2841 * @param hw Hardware context.
2842 * @param domain Pointer to the domain object.
2843 * @param fc_id Fibre Channel ID to associate with this port.
2844 *
2845 * @return Returns 0 on success, or a non-zero value on failure.
2846 */
2847 ocs_hw_rtn_e
ocs_hw_domain_attach(ocs_hw_t * hw,ocs_domain_t * domain,uint32_t fc_id)2848 ocs_hw_domain_attach(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fc_id)
2849 {
2850 uint8_t *buf = NULL;
2851 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2852
2853 if (!hw || !domain) {
2854 ocs_log_err(hw ? hw->os : NULL,
2855 "bad parameter(s) hw=%p domain=%p\n",
2856 hw, domain);
2857 return OCS_HW_RTN_ERROR;
2858 }
2859
2860 /*
2861 * Check if the chip is in an error state (UE'd) before proceeding.
2862 */
2863 if (sli_fw_error_status(&hw->sli) > 0) {
2864 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2865 return OCS_HW_RTN_ERROR;
2866 }
2867
2868 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
2869 if (!buf) {
2870 ocs_log_err(hw->os, "no buffer for command\n");
2871 return OCS_HW_RTN_NO_MEMORY;
2872 }
2873
2874 domain->sport->fc_id = fc_id;
2875 ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_ATTACH, buf);
2876 return rc;
2877 }
2878
2879 /**
2880 * @ingroup domain
2881 * @brief Free a fabric domain object.
2882 *
2883 * @par Description
2884 * Free both the driver and SLI port resources associated with the domain.
2885 *
2886 * @param hw Hardware context.
2887 * @param domain Pointer to the domain object.
2888 *
2889 * @return Returns 0 on success, or a non-zero value on failure.
2890 */
2891 ocs_hw_rtn_e
ocs_hw_domain_free(ocs_hw_t * hw,ocs_domain_t * domain)2892 ocs_hw_domain_free(ocs_hw_t *hw, ocs_domain_t *domain)
2893 {
2894 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
2895
2896 if (!hw || !domain) {
2897 ocs_log_err(hw ? hw->os : NULL,
2898 "bad parameter(s) hw=%p domain=%p\n",
2899 hw, domain);
2900 return OCS_HW_RTN_ERROR;
2901 }
2902
2903 /*
2904 * Check if the chip is in an error state (UE'd) before proceeding.
2905 */
2906 if (sli_fw_error_status(&hw->sli) > 0) {
2907 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2908 return OCS_HW_RTN_ERROR;
2909 }
2910
2911 ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_FREE, NULL);
2912 return rc;
2913 }
2914
2915 /**
2916 * @ingroup domain
2917 * @brief Free a fabric domain object.
2918 *
2919 * @par Description
2920 * Free the driver resources associated with the domain. The difference between
2921 * this call and ocs_hw_domain_free() is that this call assumes resources no longer
2922 * exist on the SLI port, due to a reset or after some error conditions.
2923 *
2924 * @param hw Hardware context.
2925 * @param domain Pointer to the domain object.
2926 *
2927 * @return Returns 0 on success, or a non-zero value on failure.
2928 */
2929 ocs_hw_rtn_e
ocs_hw_domain_force_free(ocs_hw_t * hw,ocs_domain_t * domain)2930 ocs_hw_domain_force_free(ocs_hw_t *hw, ocs_domain_t *domain)
2931 {
2932 if (!hw || !domain) {
2933 ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p\n", hw, domain);
2934 return OCS_HW_RTN_ERROR;
2935 }
2936
2937 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
2938
2939 return OCS_HW_RTN_SUCCESS;
2940 }
2941
2942 /**
2943 * @ingroup node
2944 * @brief Allocate a remote node object.
2945 *
2946 * @param hw Hardware context.
2947 * @param rnode Allocated remote node object to initialize.
2948 * @param fc_addr FC address of the remote node.
2949 * @param sport SLI port used to connect to remote node.
2950 *
2951 * @return Returns 0 on success, or a non-zero value on failure.
2952 */
2953 ocs_hw_rtn_e
ocs_hw_node_alloc(ocs_hw_t * hw,ocs_remote_node_t * rnode,uint32_t fc_addr,ocs_sli_port_t * sport)2954 ocs_hw_node_alloc(ocs_hw_t *hw, ocs_remote_node_t *rnode, uint32_t fc_addr,
2955 ocs_sli_port_t *sport)
2956 {
2957 /* Check for invalid indicator */
2958 if (UINT32_MAX != rnode->indicator) {
2959 ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x rpi=%#x\n",
2960 fc_addr, rnode->indicator);
2961 return OCS_HW_RTN_ERROR;
2962 }
2963
2964 /*
2965 * Check if the chip is in an error state (UE'd) before proceeding.
2966 */
2967 if (sli_fw_error_status(&hw->sli) > 0) {
2968 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
2969 return OCS_HW_RTN_ERROR;
2970 }
2971
2972 /* NULL SLI port indicates an unallocated remote node */
2973 rnode->sport = NULL;
2974
2975 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &rnode->indicator, &rnode->index)) {
2976 ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
2977 fc_addr);
2978 return OCS_HW_RTN_ERROR;
2979 }
2980
2981 rnode->fc_id = fc_addr;
2982 rnode->sport = sport;
2983
2984 return OCS_HW_RTN_SUCCESS;
2985 }
2986
2987 /**
2988 * @ingroup node
2989 * @brief Update a remote node object with the remote port's service parameters.
2990 *
2991 * @param hw Hardware context.
2992 * @param rnode Allocated remote node object to initialize.
2993 * @param sparms DMA buffer containing the remote port's service parameters.
2994 *
2995 * @return Returns 0 on success, or a non-zero value on failure.
2996 */
2997 ocs_hw_rtn_e
ocs_hw_node_attach(ocs_hw_t * hw,ocs_remote_node_t * rnode,ocs_dma_t * sparms)2998 ocs_hw_node_attach(ocs_hw_t *hw, ocs_remote_node_t *rnode, ocs_dma_t *sparms)
2999 {
3000 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
3001 uint8_t *buf = NULL;
3002 uint32_t count = 0;
3003
3004 if (!hw || !rnode || !sparms) {
3005 ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p sparms=%p\n",
3006 hw, rnode, sparms);
3007 return OCS_HW_RTN_ERROR;
3008 }
3009
3010 /*
3011 * Check if the chip is in an error state (UE'd) before proceeding.
3012 */
3013 if (sli_fw_error_status(&hw->sli) > 0) {
3014 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3015 return OCS_HW_RTN_ERROR;
3016 }
3017
3018 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3019 if (!buf) {
3020 ocs_log_err(hw->os, "no buffer for command\n");
3021 return OCS_HW_RTN_NO_MEMORY;
3022 }
3023
3024 /*
3025 * If the attach count is non-zero, this RPI has already been registered.
3026 * Otherwise, register the RPI
3027 */
3028 if (rnode->index == UINT32_MAX) {
3029 ocs_log_err(NULL, "bad parameter rnode->index invalid\n");
3030 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3031 return OCS_HW_RTN_ERROR;
3032 }
3033 count = ocs_atomic_add_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3034 if (count) {
3035 /*
3036 * Can't attach multiple FC_ID's to a node unless High Login
3037 * Mode is enabled
3038 */
3039 if (sli_get_hlm(&hw->sli) == FALSE) {
3040 ocs_log_test(hw->os, "attach to already attached node HLM=%d count=%d\n",
3041 sli_get_hlm(&hw->sli), count);
3042 rc = OCS_HW_RTN_SUCCESS;
3043 } else {
3044 rnode->node_group = TRUE;
3045 rnode->attached = ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_attached);
3046 rc = rnode->attached ? OCS_HW_RTN_SUCCESS_SYNC : OCS_HW_RTN_SUCCESS;
3047 }
3048 } else {
3049 rnode->node_group = FALSE;
3050
3051 ocs_display_sparams("", "reg rpi", 0, NULL, sparms->virt);
3052 if (sli_cmd_reg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->fc_id,
3053 rnode->indicator, rnode->sport->indicator,
3054 sparms, 0, (hw->auto_xfer_rdy_enabled && hw->config.auto_xfer_rdy_t10_enable))) {
3055 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT,
3056 ocs_hw_cb_node_attach, rnode);
3057 }
3058 }
3059
3060 if (count || rc) {
3061 if (rc < OCS_HW_RTN_SUCCESS) {
3062 ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
3063 ocs_log_err(hw->os, "%s error\n", count ? "HLM" : "REG_RPI");
3064 }
3065 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3066 }
3067
3068 return rc;
3069 }
3070
3071 /**
3072 * @ingroup node
3073 * @brief Free a remote node resource.
3074 *
3075 * @param hw Hardware context.
3076 * @param rnode Remote node object to free.
3077 *
3078 * @return Returns 0 on success, or a non-zero value on failure.
3079 */
3080 ocs_hw_rtn_e
ocs_hw_node_free_resources(ocs_hw_t * hw,ocs_remote_node_t * rnode)3081 ocs_hw_node_free_resources(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3082 {
3083 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
3084
3085 if (!hw || !rnode) {
3086 ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3087 hw, rnode);
3088 return OCS_HW_RTN_ERROR;
3089 }
3090
3091 if (rnode->sport) {
3092 if (!rnode->attached) {
3093 if (rnode->indicator != UINT32_MAX) {
3094 if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3095 ocs_log_err(hw->os, "FCOE_RPI free failure RPI %d addr=%#x\n",
3096 rnode->indicator, rnode->fc_id);
3097 rc = OCS_HW_RTN_ERROR;
3098 } else {
3099 rnode->node_group = FALSE;
3100 rnode->indicator = UINT32_MAX;
3101 rnode->index = UINT32_MAX;
3102 rnode->free_group = FALSE;
3103 }
3104 }
3105 } else {
3106 ocs_log_err(hw->os, "Error: rnode is still attached\n");
3107 rc = OCS_HW_RTN_ERROR;
3108 }
3109 }
3110
3111 return rc;
3112 }
3113
3114
3115 /**
3116 * @ingroup node
3117 * @brief Free a remote node object.
3118 *
3119 * @param hw Hardware context.
3120 * @param rnode Remote node object to free.
3121 *
3122 * @return Returns 0 on success, or a non-zero value on failure.
3123 */
3124 ocs_hw_rtn_e
ocs_hw_node_detach(ocs_hw_t * hw,ocs_remote_node_t * rnode)3125 ocs_hw_node_detach(ocs_hw_t *hw, ocs_remote_node_t *rnode)
3126 {
3127 uint8_t *buf = NULL;
3128 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS_SYNC;
3129 uint32_t index = UINT32_MAX;
3130
3131 if (!hw || !rnode) {
3132 ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
3133 hw, rnode);
3134 return OCS_HW_RTN_ERROR;
3135 }
3136
3137 /*
3138 * Check if the chip is in an error state (UE'd) before proceeding.
3139 */
3140 if (sli_fw_error_status(&hw->sli) > 0) {
3141 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3142 return OCS_HW_RTN_ERROR;
3143 }
3144
3145 index = rnode->index;
3146
3147 if (rnode->sport) {
3148 uint32_t count = 0;
3149 uint32_t fc_id;
3150
3151 if (!rnode->attached) {
3152 return OCS_HW_RTN_SUCCESS_SYNC;
3153 }
3154
3155 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3156 if (!buf) {
3157 ocs_log_err(hw->os, "no buffer for command\n");
3158 return OCS_HW_RTN_NO_MEMORY;
3159 }
3160
3161 count = ocs_atomic_sub_return(&hw->rpi_ref[index].rpi_count, 1);
3162
3163 if (count <= 1) {
3164 /* There are no other references to this RPI
3165 * so unregister it and free the resource. */
3166 fc_id = UINT32_MAX;
3167 rnode->node_group = FALSE;
3168 rnode->free_group = TRUE;
3169 } else {
3170 if (sli_get_hlm(&hw->sli) == FALSE) {
3171 ocs_log_test(hw->os, "Invalid count with HLM disabled, count=%d\n",
3172 count);
3173 }
3174 fc_id = rnode->fc_id & 0x00ffffff;
3175 }
3176
3177 rc = OCS_HW_RTN_ERROR;
3178
3179 if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->indicator,
3180 SLI_RSRC_FCOE_RPI, fc_id)) {
3181 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free, rnode);
3182 }
3183
3184 if (rc != OCS_HW_RTN_SUCCESS) {
3185 ocs_log_err(hw->os, "UNREG_RPI failed\n");
3186 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3187 rc = OCS_HW_RTN_ERROR;
3188 }
3189 }
3190
3191 return rc;
3192 }
3193
3194 /**
3195 * @ingroup node
3196 * @brief Free all remote node objects.
3197 *
3198 * @param hw Hardware context.
3199 *
3200 * @return Returns 0 on success, or a non-zero value on failure.
3201 */
3202 ocs_hw_rtn_e
ocs_hw_node_free_all(ocs_hw_t * hw)3203 ocs_hw_node_free_all(ocs_hw_t *hw)
3204 {
3205 uint8_t *buf = NULL;
3206 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
3207
3208 if (!hw) {
3209 ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
3210 return OCS_HW_RTN_ERROR;
3211 }
3212
3213 /*
3214 * Check if the chip is in an error state (UE'd) before proceeding.
3215 */
3216 if (sli_fw_error_status(&hw->sli) > 0) {
3217 ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
3218 return OCS_HW_RTN_ERROR;
3219 }
3220
3221 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
3222 if (!buf) {
3223 ocs_log_err(hw->os, "no buffer for command\n");
3224 return OCS_HW_RTN_NO_MEMORY;
3225 }
3226
3227 if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, 0xffff,
3228 SLI_RSRC_FCOE_FCFI, UINT32_MAX)) {
3229 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free_all,
3230 NULL);
3231 }
3232
3233 if (rc != OCS_HW_RTN_SUCCESS) {
3234 ocs_log_err(hw->os, "UNREG_RPI failed\n");
3235 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
3236 rc = OCS_HW_RTN_ERROR;
3237 }
3238
3239 return rc;
3240 }
3241
3242 ocs_hw_rtn_e
ocs_hw_node_group_alloc(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup)3243 ocs_hw_node_group_alloc(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3244 {
3245
3246 if (!hw || !ngroup) {
3247 ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3248 hw, ngroup);
3249 return OCS_HW_RTN_ERROR;
3250 }
3251
3252 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &ngroup->indicator,
3253 &ngroup->index)) {
3254 ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
3255 ngroup->indicator);
3256 return OCS_HW_RTN_ERROR;
3257 }
3258
3259 return OCS_HW_RTN_SUCCESS;
3260 }
3261
3262 ocs_hw_rtn_e
ocs_hw_node_group_attach(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup,ocs_remote_node_t * rnode)3263 ocs_hw_node_group_attach(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup, ocs_remote_node_t *rnode)
3264 {
3265
3266 if (!hw || !ngroup || !rnode) {
3267 ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p rnode=%p\n",
3268 hw, ngroup, rnode);
3269 return OCS_HW_RTN_ERROR;
3270 }
3271
3272 if (rnode->attached) {
3273 ocs_log_err(hw->os, "node already attached RPI=%#x addr=%#x\n",
3274 rnode->indicator, rnode->fc_id);
3275 return OCS_HW_RTN_ERROR;
3276 }
3277
3278 if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
3279 ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3280 rnode->indicator);
3281 return OCS_HW_RTN_ERROR;
3282 }
3283
3284 rnode->indicator = ngroup->indicator;
3285 rnode->index = ngroup->index;
3286
3287 return OCS_HW_RTN_SUCCESS;
3288 }
3289
3290 ocs_hw_rtn_e
ocs_hw_node_group_free(ocs_hw_t * hw,ocs_remote_node_group_t * ngroup)3291 ocs_hw_node_group_free(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
3292 {
3293 int ref;
3294
3295 if (!hw || !ngroup) {
3296 ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
3297 hw, ngroup);
3298 return OCS_HW_RTN_ERROR;
3299 }
3300
3301 ref = ocs_atomic_read(&hw->rpi_ref[ngroup->index].rpi_count);
3302 if (ref) {
3303 /* Hmmm, the reference count is non-zero */
3304 ocs_log_debug(hw->os, "node group reference=%d (RPI=%#x)\n",
3305 ref, ngroup->indicator);
3306
3307 if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, ngroup->indicator)) {
3308 ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
3309 ngroup->indicator);
3310 return OCS_HW_RTN_ERROR;
3311 }
3312
3313 ocs_atomic_set(&hw->rpi_ref[ngroup->index].rpi_count, 0);
3314 }
3315
3316 ngroup->indicator = UINT32_MAX;
3317 ngroup->index = UINT32_MAX;
3318
3319 return OCS_HW_RTN_SUCCESS;
3320 }
3321
3322 /**
3323 * @brief Initialize IO fields on each free call.
3324 *
3325 * @n @b Note: This is done on each free call (as opposed to each
3326 * alloc call) because port-owned XRIs are not
3327 * allocated with ocs_hw_io_alloc() but are freed with this
3328 * function.
3329 *
3330 * @param io Pointer to HW IO.
3331 */
3332 static inline void
ocs_hw_init_free_io(ocs_hw_io_t * io)3333 ocs_hw_init_free_io(ocs_hw_io_t *io)
3334 {
3335 /*
3336 * Set io->done to NULL, to avoid any callbacks, should
3337 * a completion be received for one of these IOs
3338 */
3339 io->done = NULL;
3340 io->abort_done = NULL;
3341 io->status_saved = 0;
3342 io->abort_in_progress = FALSE;
3343 io->port_owned_abort_count = 0;
3344 io->rnode = NULL;
3345 io->type = 0xFFFF;
3346 io->wq = NULL;
3347 io->ul_io = NULL;
3348 io->tgt_wqe_timeout = 0;
3349 }
3350
3351 /**
3352 * @ingroup io
3353 * @brief Lockless allocate a HW IO object.
3354 *
3355 * @par Description
3356 * Assume that hw->ocs_lock is held. This function is only used if
3357 * use_dif_sec_xri workaround is being used.
3358 *
3359 * @param hw Hardware context.
3360 *
3361 * @return Returns a pointer to an object on success, or NULL on failure.
3362 */
3363 static inline ocs_hw_io_t *
_ocs_hw_io_alloc(ocs_hw_t * hw)3364 _ocs_hw_io_alloc(ocs_hw_t *hw)
3365 {
3366 ocs_hw_io_t *io = NULL;
3367
3368 if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
3369 ocs_list_add_tail(&hw->io_inuse, io);
3370 io->state = OCS_HW_IO_STATE_INUSE;
3371 io->quarantine = FALSE;
3372 io->quarantine_first_phase = TRUE;
3373 io->abort_reqtag = UINT32_MAX;
3374 ocs_ref_init(&io->ref, ocs_hw_io_free_internal, io);
3375 } else {
3376 ocs_atomic_add_return(&hw->io_alloc_failed_count, 1);
3377 }
3378
3379 return io;
3380 }
3381 /**
3382 * @ingroup io
3383 * @brief Allocate a HW IO object.
3384 *
3385 * @par Description
3386 * @n @b Note: This function applies to non-port owned XRIs
3387 * only.
3388 *
3389 * @param hw Hardware context.
3390 *
3391 * @return Returns a pointer to an object on success, or NULL on failure.
3392 */
3393 ocs_hw_io_t *
ocs_hw_io_alloc(ocs_hw_t * hw)3394 ocs_hw_io_alloc(ocs_hw_t *hw)
3395 {
3396 ocs_hw_io_t *io = NULL;
3397
3398 ocs_lock(&hw->io_lock);
3399 io = _ocs_hw_io_alloc(hw);
3400 ocs_unlock(&hw->io_lock);
3401
3402 return io;
3403 }
3404
3405 /**
3406 * @ingroup io
3407 * @brief Allocate/Activate a port owned HW IO object.
3408 *
3409 * @par Description
3410 * This function is called by the transport layer when an XRI is
3411 * allocated by the SLI-Port. This will "activate" the HW IO
3412 * associated with the XRI received from the SLI-Port to mirror
3413 * the state of the XRI.
3414 * @n @n @b Note: This function applies to port owned XRIs only.
3415 *
3416 * @param hw Hardware context.
3417 * @param io Pointer HW IO to activate/allocate.
3418 *
3419 * @return Returns a pointer to an object on success, or NULL on failure.
3420 */
3421 ocs_hw_io_t *
ocs_hw_io_activate_port_owned(ocs_hw_t * hw,ocs_hw_io_t * io)3422 ocs_hw_io_activate_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
3423 {
3424 if (ocs_ref_read_count(&io->ref) > 0) {
3425 ocs_log_err(hw->os, "Bad parameter: refcount > 0\n");
3426 return NULL;
3427 }
3428
3429 if (io->wq != NULL) {
3430 ocs_log_err(hw->os, "XRI %x already in use\n", io->indicator);
3431 return NULL;
3432 }
3433
3434 ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3435 io->xbusy = TRUE;
3436
3437 return io;
3438 }
3439
3440 /**
3441 * @ingroup io
3442 * @brief When an IO is freed, depending on the exchange busy flag, and other
3443 * workarounds, move it to the correct list.
3444 *
3445 * @par Description
3446 * @n @b Note: Assumes that the hw->io_lock is held and the item has been removed
3447 * from the busy or wait_free list.
3448 *
3449 * @param hw Hardware context.
3450 * @param io Pointer to the IO object to move.
3451 */
3452 static void
ocs_hw_io_free_move_correct_list(ocs_hw_t * hw,ocs_hw_io_t * io)3453 ocs_hw_io_free_move_correct_list(ocs_hw_t *hw, ocs_hw_io_t *io)
3454 {
3455 if (io->xbusy) {
3456 /* add to wait_free list and wait for XRI_ABORTED CQEs to clean up */
3457 ocs_list_add_tail(&hw->io_wait_free, io);
3458 io->state = OCS_HW_IO_STATE_WAIT_FREE;
3459 } else {
3460 /* IO not busy, add to free list */
3461 ocs_list_add_tail(&hw->io_free, io);
3462 io->state = OCS_HW_IO_STATE_FREE;
3463 }
3464
3465 /* BZ 161832 workaround */
3466 if (hw->workaround.use_dif_sec_xri) {
3467 ocs_hw_check_sec_hio_list(hw);
3468 }
3469 }
3470
3471 /**
3472 * @ingroup io
3473 * @brief Free a HW IO object. Perform cleanup common to
3474 * port and host-owned IOs.
3475 *
3476 * @param hw Hardware context.
3477 * @param io Pointer to the HW IO object.
3478 */
3479 static inline void
ocs_hw_io_free_common(ocs_hw_t * hw,ocs_hw_io_t * io)3480 ocs_hw_io_free_common(ocs_hw_t *hw, ocs_hw_io_t *io)
3481 {
3482 /* initialize IO fields */
3483 ocs_hw_init_free_io(io);
3484
3485 /* Restore default SGL */
3486 ocs_hw_io_restore_sgl(hw, io);
3487 }
3488
3489 /**
3490 * @ingroup io
3491 * @brief Free a HW IO object associated with a port-owned XRI.
3492 *
3493 * @param arg Pointer to the HW IO object.
3494 */
3495 static void
ocs_hw_io_free_port_owned(void * arg)3496 ocs_hw_io_free_port_owned(void *arg)
3497 {
3498 ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3499 ocs_hw_t *hw = io->hw;
3500
3501 /*
3502 * For auto xfer rdy, if the dnrx bit is set, then add it to the list of XRIs
3503 * waiting for buffers.
3504 */
3505 if (io->auto_xfer_rdy_dnrx) {
3506 ocs_lock(&hw->io_lock);
3507 /* take a reference count because we still own the IO until the buffer is posted */
3508 ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
3509 ocs_list_add_tail(&hw->io_port_dnrx, io);
3510 ocs_unlock(&hw->io_lock);
3511 }
3512
3513 /* perform common cleanup */
3514 ocs_hw_io_free_common(hw, io);
3515 }
3516
3517 /**
3518 * @ingroup io
3519 * @brief Free a previously-allocated HW IO object. Called when
3520 * IO refcount goes to zero (host-owned IOs only).
3521 *
3522 * @param arg Pointer to the HW IO object.
3523 */
3524 static void
ocs_hw_io_free_internal(void * arg)3525 ocs_hw_io_free_internal(void *arg)
3526 {
3527 ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
3528 ocs_hw_t *hw = io->hw;
3529
3530 /* perform common cleanup */
3531 ocs_hw_io_free_common(hw, io);
3532
3533 ocs_lock(&hw->io_lock);
3534 /* remove from in-use list */
3535 ocs_list_remove(&hw->io_inuse, io);
3536 ocs_hw_io_free_move_correct_list(hw, io);
3537 ocs_unlock(&hw->io_lock);
3538 }
3539
3540 /**
3541 * @ingroup io
3542 * @brief Free a previously-allocated HW IO object.
3543 *
3544 * @par Description
3545 * @n @b Note: This function applies to port and host owned XRIs.
3546 *
3547 * @param hw Hardware context.
3548 * @param io Pointer to the HW IO object.
3549 *
3550 * @return Returns a non-zero value if HW IO was freed, 0 if references
3551 * on the IO still exist, or a negative value if an error occurred.
3552 */
3553 int32_t
ocs_hw_io_free(ocs_hw_t * hw,ocs_hw_io_t * io)3554 ocs_hw_io_free(ocs_hw_t *hw, ocs_hw_io_t *io)
3555 {
3556 /* just put refcount */
3557 if (ocs_ref_read_count(&io->ref) <= 0) {
3558 ocs_log_err(hw->os, "Bad parameter: refcount <= 0 xri=%x tag=%x\n",
3559 io->indicator, io->reqtag);
3560 return -1;
3561 }
3562
3563 return ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_hw_io_alloc() */
3564 }
3565
3566 /**
3567 * @ingroup io
3568 * @brief Check if given HW IO is in-use
3569 *
3570 * @par Description
3571 * This function returns TRUE if the given HW IO has been
3572 * allocated and is in-use, and FALSE otherwise. It applies to
3573 * port and host owned XRIs.
3574 *
3575 * @param hw Hardware context.
3576 * @param io Pointer to the HW IO object.
3577 *
3578 * @return TRUE if an IO is in use, or FALSE otherwise.
3579 */
3580 uint8_t
ocs_hw_io_inuse(ocs_hw_t * hw,ocs_hw_io_t * io)3581 ocs_hw_io_inuse(ocs_hw_t *hw, ocs_hw_io_t *io)
3582 {
3583 return (ocs_ref_read_count(&io->ref) > 0);
3584 }
3585
3586 /**
3587 * @brief Write a HW IO to a work queue.
3588 *
3589 * @par Description
3590 * A HW IO is written to a work queue.
3591 *
3592 * @param wq Pointer to work queue.
3593 * @param wqe Pointer to WQ entry.
3594 *
3595 * @n @b Note: Assumes the SLI-4 queue lock is held.
3596 *
3597 * @return Returns 0 on success, or a negative error code value on failure.
3598 */
3599 static int32_t
_hw_wq_write(hw_wq_t * wq,ocs_hw_wqe_t * wqe)3600 _hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3601 {
3602 int32_t rc;
3603 int32_t queue_rc;
3604
3605 /* Every so often, set the wqec bit to generate comsummed completions */
3606 if (wq->wqec_count) {
3607 wq->wqec_count--;
3608 }
3609 if (wq->wqec_count == 0) {
3610 sli4_generic_wqe_t *genwqe = (void*)wqe->wqebuf;
3611 genwqe->wqec = 1;
3612 wq->wqec_count = wq->wqec_set_count;
3613 }
3614
3615 /* Decrement WQ free count */
3616 wq->free_count--;
3617
3618 queue_rc = _sli_queue_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
3619
3620 if (queue_rc < 0) {
3621 rc = -1;
3622 } else {
3623 rc = 0;
3624 ocs_queue_history_wq(&wq->hw->q_hist, (void *) wqe->wqebuf, wq->queue->id, queue_rc);
3625 }
3626
3627 return rc;
3628 }
3629
3630 /**
3631 * @brief Write a HW IO to a work queue.
3632 *
3633 * @par Description
3634 * A HW IO is written to a work queue.
3635 *
3636 * @param wq Pointer to work queue.
3637 * @param wqe Pointer to WQE entry.
3638 *
3639 * @n @b Note: Takes the SLI-4 queue lock.
3640 *
3641 * @return Returns 0 on success, or a negative error code value on failure.
3642 */
3643 int32_t
hw_wq_write(hw_wq_t * wq,ocs_hw_wqe_t * wqe)3644 hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
3645 {
3646 int32_t rc = 0;
3647
3648 sli_queue_lock(wq->queue);
3649 if ( ! ocs_list_empty(&wq->pending_list)) {
3650 ocs_list_add_tail(&wq->pending_list, wqe);
3651 OCS_STAT(wq->wq_pending_count++;)
3652 while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3653 rc = _hw_wq_write(wq, wqe);
3654 if (rc < 0) {
3655 break;
3656 }
3657 if (wqe->abort_wqe_submit_needed) {
3658 wqe->abort_wqe_submit_needed = 0;
3659 sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3660 wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT );
3661 ocs_list_add_tail(&wq->pending_list, wqe);
3662 OCS_STAT(wq->wq_pending_count++;)
3663 }
3664 }
3665 } else {
3666 if (wq->free_count > 0) {
3667 rc = _hw_wq_write(wq, wqe);
3668 } else {
3669 ocs_list_add_tail(&wq->pending_list, wqe);
3670 OCS_STAT(wq->wq_pending_count++;)
3671 }
3672 }
3673
3674 sli_queue_unlock(wq->queue);
3675
3676 return rc;
3677
3678 }
3679
3680 /**
3681 * @brief Update free count and submit any pending HW IOs
3682 *
3683 * @par Description
3684 * The WQ free count is updated, and any pending HW IOs are submitted that
3685 * will fit in the queue.
3686 *
3687 * @param wq Pointer to work queue.
3688 * @param update_free_count Value added to WQs free count.
3689 *
3690 * @return None.
3691 */
3692 static void
hw_wq_submit_pending(hw_wq_t * wq,uint32_t update_free_count)3693 hw_wq_submit_pending(hw_wq_t *wq, uint32_t update_free_count)
3694 {
3695 ocs_hw_wqe_t *wqe;
3696
3697 sli_queue_lock(wq->queue);
3698
3699 /* Update free count with value passed in */
3700 wq->free_count += update_free_count;
3701
3702 while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
3703 _hw_wq_write(wq, wqe);
3704
3705 if (wqe->abort_wqe_submit_needed) {
3706 wqe->abort_wqe_submit_needed = 0;
3707 sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
3708 wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT);
3709 ocs_list_add_tail(&wq->pending_list, wqe);
3710 OCS_STAT(wq->wq_pending_count++;)
3711 }
3712 }
3713
3714 sli_queue_unlock(wq->queue);
3715 }
3716
3717 /**
3718 * @brief Check to see if there are any BZ 161832 workaround waiting IOs
3719 *
3720 * @par Description
3721 * Checks hw->sec_hio_wait_list, if an IO is waiting for a HW IO, then try
3722 * to allocate a secondary HW io, and dispatch it.
3723 *
3724 * @n @b Note: hw->io_lock MUST be taken when called.
3725 *
3726 * @param hw pointer to HW object
3727 *
3728 * @return none
3729 */
3730 static void
ocs_hw_check_sec_hio_list(ocs_hw_t * hw)3731 ocs_hw_check_sec_hio_list(ocs_hw_t *hw)
3732 {
3733 ocs_hw_io_t *io;
3734 ocs_hw_io_t *sec_io;
3735 int rc = 0;
3736
3737 while (!ocs_list_empty(&hw->sec_hio_wait_list)) {
3738 uint16_t flags;
3739
3740 sec_io = _ocs_hw_io_alloc(hw);
3741 if (sec_io == NULL) {
3742 break;
3743 }
3744
3745 io = ocs_list_remove_head(&hw->sec_hio_wait_list);
3746 ocs_list_add_tail(&hw->io_inuse, io);
3747 io->state = OCS_HW_IO_STATE_INUSE;
3748 io->sec_hio = sec_io;
3749
3750 /* mark secondary XRI for second and subsequent data phase as quarantine */
3751 if (io->xbusy) {
3752 sec_io->quarantine = TRUE;
3753 }
3754
3755 flags = io->sec_iparam.fcp_tgt.flags;
3756 if (io->xbusy) {
3757 flags |= SLI4_IO_CONTINUATION;
3758 } else {
3759 flags &= ~SLI4_IO_CONTINUATION;
3760 }
3761
3762 io->tgt_wqe_timeout = io->sec_iparam.fcp_tgt.timeout;
3763
3764 /* Complete (continue) TRECV IO */
3765 if (io->xbusy) {
3766 if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3767 io->first_data_sge,
3768 io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator, io->sec_hio->indicator,
3769 io->reqtag, SLI4_CQ_DEFAULT,
3770 io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3771 flags,
3772 io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size, io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3773 ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3774 break;
3775 }
3776 } else {
3777 if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
3778 io->first_data_sge,
3779 io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator,
3780 io->reqtag, SLI4_CQ_DEFAULT,
3781 io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
3782 flags,
3783 io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size,
3784 io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
3785 ocs_log_test(hw->os, "TRECEIVE WQE error\n");
3786 break;
3787 }
3788 }
3789
3790 if (io->wq == NULL) {
3791 io->wq = ocs_hw_queue_next_wq(hw, io);
3792 ocs_hw_assert(io->wq != NULL);
3793 }
3794 io->xbusy = TRUE;
3795
3796 /*
3797 * Add IO to active io wqe list before submitting, in case the
3798 * wcqe processing preempts this thread.
3799 */
3800 ocs_hw_add_io_timed_wqe(hw, io);
3801 rc = hw_wq_write(io->wq, &io->wqe);
3802 if (rc >= 0) {
3803 /* non-negative return is success */
3804 rc = 0;
3805 } else {
3806 /* failed to write wqe, remove from active wqe list */
3807 ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
3808 io->xbusy = FALSE;
3809 ocs_hw_remove_io_timed_wqe(hw, io);
3810 }
3811 }
3812 }
3813
3814 /**
3815 * @ingroup io
3816 * @brief Send a Single Request/Response Sequence (SRRS).
3817 *
3818 * @par Description
3819 * This routine supports communication sequences consisting of a single
3820 * request and single response between two endpoints. Examples include:
3821 * - Sending an ELS request.
3822 * - Sending an ELS response - To send an ELS reponse, the caller must provide
3823 * the OX_ID from the received request.
3824 * - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
3825 * the caller must provide the R_CTL, TYPE, and DF_CTL
3826 * values to place in the FC frame header.
3827 * .
3828 * @n @b Note: The caller is expected to provide both send and receive
3829 * buffers for requests. In the case of sending a response, no receive buffer
3830 * is necessary and the caller may pass in a NULL pointer.
3831 *
3832 * @param hw Hardware context.
3833 * @param type Type of sequence (ELS request/response, FC-CT).
3834 * @param io Previously-allocated HW IO object.
3835 * @param send DMA memory holding data to send (for example, ELS request, BLS response).
3836 * @param len Length, in bytes, of data to send.
3837 * @param receive Optional DMA memory to hold a response.
3838 * @param rnode Destination of data (that is, a remote node).
3839 * @param iparam IO parameters (ELS response and FC-CT).
3840 * @param cb Function call upon completion of sending the data (may be NULL).
3841 * @param arg Argument to pass to IO completion function.
3842 *
3843 * @return Returns 0 on success, or a non-zero on failure.
3844 */
3845 ocs_hw_rtn_e
ocs_hw_srrs_send(ocs_hw_t * hw,ocs_hw_io_type_e type,ocs_hw_io_t * io,ocs_dma_t * send,uint32_t len,ocs_dma_t * receive,ocs_remote_node_t * rnode,ocs_hw_io_param_t * iparam,ocs_hw_srrs_cb_t cb,void * arg)3846 ocs_hw_srrs_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
3847 ocs_dma_t *send, uint32_t len, ocs_dma_t *receive,
3848 ocs_remote_node_t *rnode, ocs_hw_io_param_t *iparam,
3849 ocs_hw_srrs_cb_t cb, void *arg)
3850 {
3851 sli4_sge_t *sge = NULL;
3852 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
3853 uint16_t local_flags = 0;
3854
3855 if (!hw || !io || !rnode || !iparam) {
3856 ocs_log_err(NULL, "bad parm hw=%p io=%p send=%p receive=%p rnode=%p iparam=%p\n",
3857 hw, io, send, receive, rnode, iparam);
3858 return OCS_HW_RTN_ERROR;
3859 }
3860
3861 if (hw->state != OCS_HW_STATE_ACTIVE) {
3862 ocs_log_test(hw->os, "cannot send SRRS, HW state=%d\n", hw->state);
3863 return OCS_HW_RTN_ERROR;
3864 }
3865
3866 if (ocs_hw_is_xri_port_owned(hw, io->indicator)) {
3867 /* We must set the XC bit for port owned XRIs */
3868 local_flags |= SLI4_IO_CONTINUATION;
3869 }
3870 io->rnode = rnode;
3871 io->type = type;
3872 io->done = cb;
3873 io->arg = arg;
3874
3875 sge = io->sgl->virt;
3876
3877 /* clear both SGE */
3878 ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
3879
3880 if (send) {
3881 sge[0].buffer_address_high = ocs_addr32_hi(send->phys);
3882 sge[0].buffer_address_low = ocs_addr32_lo(send->phys);
3883 sge[0].sge_type = SLI4_SGE_TYPE_DATA;
3884 sge[0].buffer_length = len;
3885 }
3886
3887 if ((OCS_HW_ELS_REQ == type) || (OCS_HW_FC_CT == type)) {
3888 sge[1].buffer_address_high = ocs_addr32_hi(receive->phys);
3889 sge[1].buffer_address_low = ocs_addr32_lo(receive->phys);
3890 sge[1].sge_type = SLI4_SGE_TYPE_DATA;
3891 sge[1].buffer_length = receive->size;
3892 sge[1].last = TRUE;
3893 } else {
3894 sge[0].last = TRUE;
3895 }
3896
3897 switch (type) {
3898 case OCS_HW_ELS_REQ:
3899 if ( (!send) || sli_els_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl,
3900 *((uint8_t *)(send->virt)), /* req_type */
3901 len, receive->size,
3902 iparam->els.timeout, io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rnode)) {
3903 ocs_log_err(hw->os, "REQ WQE error\n");
3904 rc = OCS_HW_RTN_ERROR;
3905 }
3906 break;
3907 case OCS_HW_ELS_RSP:
3908 if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3909 io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3910 iparam->els.ox_id,
3911 rnode, local_flags, UINT32_MAX)) {
3912 ocs_log_err(hw->os, "RSP WQE error\n");
3913 rc = OCS_HW_RTN_ERROR;
3914 }
3915 break;
3916 case OCS_HW_ELS_RSP_SID:
3917 if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3918 io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
3919 iparam->els_sid.ox_id,
3920 rnode, local_flags, iparam->els_sid.s_id)) {
3921 ocs_log_err(hw->os, "RSP (SID) WQE error\n");
3922 rc = OCS_HW_RTN_ERROR;
3923 }
3924 break;
3925 case OCS_HW_FC_CT:
3926 if ( (!send) || sli_gen_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3927 receive->size, iparam->fc_ct.timeout, io->indicator,
3928 io->reqtag, SLI4_CQ_DEFAULT, rnode, iparam->fc_ct.r_ctl,
3929 iparam->fc_ct.type, iparam->fc_ct.df_ctl)) {
3930 ocs_log_err(hw->os, "GEN WQE error\n");
3931 rc = OCS_HW_RTN_ERROR;
3932 }
3933 break;
3934 case OCS_HW_FC_CT_RSP:
3935 if ( (!send) || sli_xmit_sequence64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
3936 iparam->fc_ct_rsp.timeout, iparam->fc_ct_rsp.ox_id, io->indicator,
3937 io->reqtag, rnode, iparam->fc_ct_rsp.r_ctl,
3938 iparam->fc_ct_rsp.type, iparam->fc_ct_rsp.df_ctl)) {
3939 ocs_log_err(hw->os, "XMIT SEQ WQE error\n");
3940 rc = OCS_HW_RTN_ERROR;
3941 }
3942 break;
3943 case OCS_HW_BLS_ACC:
3944 case OCS_HW_BLS_RJT:
3945 {
3946 sli_bls_payload_t bls;
3947
3948 if (OCS_HW_BLS_ACC == type) {
3949 bls.type = SLI_BLS_ACC;
3950 ocs_memcpy(&bls.u.acc, iparam->bls.payload, sizeof(bls.u.acc));
3951 } else {
3952 bls.type = SLI_BLS_RJT;
3953 ocs_memcpy(&bls.u.rjt, iparam->bls.payload, sizeof(bls.u.rjt));
3954 }
3955
3956 bls.ox_id = iparam->bls.ox_id;
3957 bls.rx_id = iparam->bls.rx_id;
3958
3959 if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3960 io->indicator, io->reqtag,
3961 SLI4_CQ_DEFAULT,
3962 rnode, UINT32_MAX)) {
3963 ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
3964 rc = OCS_HW_RTN_ERROR;
3965 }
3966 break;
3967 }
3968 case OCS_HW_BLS_ACC_SID:
3969 {
3970 sli_bls_payload_t bls;
3971
3972 bls.type = SLI_BLS_ACC;
3973 ocs_memcpy(&bls.u.acc, iparam->bls_sid.payload, sizeof(bls.u.acc));
3974
3975 bls.ox_id = iparam->bls_sid.ox_id;
3976 bls.rx_id = iparam->bls_sid.rx_id;
3977
3978 if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
3979 io->indicator, io->reqtag,
3980 SLI4_CQ_DEFAULT,
3981 rnode, iparam->bls_sid.s_id)) {
3982 ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE SID error\n");
3983 rc = OCS_HW_RTN_ERROR;
3984 }
3985 break;
3986 }
3987 case OCS_HW_BCAST:
3988 if ( (!send) || sli_xmit_bcast64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
3989 iparam->bcast.timeout, io->indicator, io->reqtag,
3990 SLI4_CQ_DEFAULT, rnode,
3991 iparam->bcast.r_ctl, iparam->bcast.type, iparam->bcast.df_ctl)) {
3992 ocs_log_err(hw->os, "XMIT_BCAST64 WQE error\n");
3993 rc = OCS_HW_RTN_ERROR;
3994 }
3995 break;
3996 default:
3997 ocs_log_err(hw->os, "bad SRRS type %#x\n", type);
3998 rc = OCS_HW_RTN_ERROR;
3999 }
4000
4001 if (OCS_HW_RTN_SUCCESS == rc) {
4002 if (io->wq == NULL) {
4003 io->wq = ocs_hw_queue_next_wq(hw, io);
4004 ocs_hw_assert(io->wq != NULL);
4005 }
4006 io->xbusy = TRUE;
4007
4008 /*
4009 * Add IO to active io wqe list before submitting, in case the
4010 * wcqe processing preempts this thread.
4011 */
4012 OCS_STAT(io->wq->use_count++);
4013 ocs_hw_add_io_timed_wqe(hw, io);
4014 rc = hw_wq_write(io->wq, &io->wqe);
4015 if (rc >= 0) {
4016 /* non-negative return is success */
4017 rc = 0;
4018 } else {
4019 /* failed to write wqe, remove from active wqe list */
4020 ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4021 io->xbusy = FALSE;
4022 ocs_hw_remove_io_timed_wqe(hw, io);
4023 }
4024 }
4025
4026 return rc;
4027 }
4028
4029 /**
4030 * @ingroup io
4031 * @brief Send a read, write, or response IO.
4032 *
4033 * @par Description
4034 * This routine supports sending a higher-level IO (for example, FCP) between two endpoints
4035 * as a target or initiator. Examples include:
4036 * - Sending read data and good response (target).
4037 * - Sending a response (target with no data or after receiving write data).
4038 * .
4039 * This routine assumes all IOs use the SGL associated with the HW IO. Prior to
4040 * calling this routine, the data should be loaded using ocs_hw_io_add_sge().
4041 *
4042 * @param hw Hardware context.
4043 * @param type Type of IO (target read, target response, and so on).
4044 * @param io Previously-allocated HW IO object.
4045 * @param len Length, in bytes, of data to send.
4046 * @param iparam IO parameters.
4047 * @param rnode Destination of data (that is, a remote node).
4048 * @param cb Function call upon completion of sending data (may be NULL).
4049 * @param arg Argument to pass to IO completion function.
4050 *
4051 * @return Returns 0 on success, or a non-zero value on failure.
4052 *
4053 * @todo
4054 * - Support specifiying relative offset.
4055 * - Use a WQ other than 0.
4056 */
4057 ocs_hw_rtn_e
ocs_hw_io_send(ocs_hw_t * hw,ocs_hw_io_type_e type,ocs_hw_io_t * io,uint32_t len,ocs_hw_io_param_t * iparam,ocs_remote_node_t * rnode,void * cb,void * arg)4058 ocs_hw_io_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
4059 uint32_t len, ocs_hw_io_param_t *iparam, ocs_remote_node_t *rnode,
4060 void *cb, void *arg)
4061 {
4062 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
4063 uint32_t rpi;
4064 uint8_t send_wqe = TRUE;
4065
4066 CPUTRACE("");
4067
4068 if (!hw || !io || !rnode || !iparam) {
4069 ocs_log_err(NULL, "bad parm hw=%p io=%p iparam=%p rnode=%p\n",
4070 hw, io, iparam, rnode);
4071 return OCS_HW_RTN_ERROR;
4072 }
4073
4074 if (hw->state != OCS_HW_STATE_ACTIVE) {
4075 ocs_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
4076 return OCS_HW_RTN_ERROR;
4077 }
4078
4079 rpi = rnode->indicator;
4080
4081 if (hw->workaround.use_unregistered_rpi && (rpi == UINT32_MAX)) {
4082 rpi = hw->workaround.unregistered_rid;
4083 ocs_log_test(hw->os, "using unregistered RPI: %d\n", rpi);
4084 }
4085
4086 /*
4087 * Save state needed during later stages
4088 */
4089 io->rnode = rnode;
4090 io->type = type;
4091 io->done = cb;
4092 io->arg = arg;
4093
4094 /*
4095 * Format the work queue entry used to send the IO
4096 */
4097 switch (type) {
4098 case OCS_HW_IO_INITIATOR_READ:
4099 /*
4100 * If use_dif_quarantine workaround is in effect, and dif_separates then mark the
4101 * initiator read IO for quarantine
4102 */
4103 if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4104 (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4105 io->quarantine = TRUE;
4106 }
4107
4108 ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4109 iparam->fcp_ini.rsp);
4110
4111 if (sli_fcp_iread64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge, len,
4112 io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rpi, rnode,
4113 iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4114 iparam->fcp_ini.timeout)) {
4115 ocs_log_err(hw->os, "IREAD WQE error\n");
4116 rc = OCS_HW_RTN_ERROR;
4117 }
4118 break;
4119 case OCS_HW_IO_INITIATOR_WRITE:
4120 ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4121 iparam->fcp_ini.rsp);
4122
4123 if (sli_fcp_iwrite64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4124 len, iparam->fcp_ini.first_burst,
4125 io->indicator, io->reqtag,
4126 SLI4_CQ_DEFAULT, rpi, rnode,
4127 iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
4128 iparam->fcp_ini.timeout)) {
4129 ocs_log_err(hw->os, "IWRITE WQE error\n");
4130 rc = OCS_HW_RTN_ERROR;
4131 }
4132 break;
4133 case OCS_HW_IO_INITIATOR_NODATA:
4134 ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
4135 iparam->fcp_ini.rsp);
4136
4137 if (sli_fcp_icmnd64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
4138 io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
4139 rpi, rnode, iparam->fcp_ini.timeout)) {
4140 ocs_log_err(hw->os, "ICMND WQE error\n");
4141 rc = OCS_HW_RTN_ERROR;
4142 }
4143 break;
4144 case OCS_HW_IO_TARGET_WRITE: {
4145 uint16_t flags = iparam->fcp_tgt.flags;
4146 fcp_xfer_rdy_iu_t *xfer = io->xfer_rdy.virt;
4147
4148 /*
4149 * Fill in the XFER_RDY for IF_TYPE 0 devices
4150 */
4151 *((uint32_t *)xfer->fcp_data_ro) = ocs_htobe32(iparam->fcp_tgt.offset);
4152 *((uint32_t *)xfer->fcp_burst_len) = ocs_htobe32(len);
4153 *((uint32_t *)xfer->rsvd) = 0;
4154
4155 if (io->xbusy) {
4156 flags |= SLI4_IO_CONTINUATION;
4157 } else {
4158 flags &= ~SLI4_IO_CONTINUATION;
4159 }
4160
4161 io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4162
4163 /*
4164 * If use_dif_quarantine workaround is in effect, and this is a DIF enabled IO
4165 * then mark the target write IO for quarantine
4166 */
4167 if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
4168 (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4169 io->quarantine = TRUE;
4170 }
4171
4172 /*
4173 * BZ 161832 Workaround:
4174 * Check for use_dif_sec_xri workaround. Note, even though the first dataphase
4175 * doesn't really need a secondary XRI, we allocate one anyway, as this avoids the
4176 * potential for deadlock where all XRI's are allocated as primaries to IOs that
4177 * are on hw->sec_hio_wait_list. If this secondary XRI is not for the first
4178 * data phase, it is marked for quarantine.
4179 */
4180 if (hw->workaround.use_dif_sec_xri && (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
4181
4182 /*
4183 * If we have allocated a chained SGL for skyhawk, then
4184 * we can re-use this for the sec_hio.
4185 */
4186 if (io->ovfl_io != NULL) {
4187 io->sec_hio = io->ovfl_io;
4188 io->sec_hio->quarantine = TRUE;
4189 } else {
4190 io->sec_hio = ocs_hw_io_alloc(hw);
4191 }
4192 if (io->sec_hio == NULL) {
4193 /* Failed to allocate, so save full request context and put
4194 * this IO on the wait list
4195 */
4196 io->sec_iparam = *iparam;
4197 io->sec_len = len;
4198 ocs_lock(&hw->io_lock);
4199 ocs_list_remove(&hw->io_inuse, io);
4200 ocs_list_add_tail(&hw->sec_hio_wait_list, io);
4201 io->state = OCS_HW_IO_STATE_WAIT_SEC_HIO;
4202 hw->sec_hio_wait_count++;
4203 ocs_unlock(&hw->io_lock);
4204 send_wqe = FALSE;
4205 /* Done */
4206 break;
4207 }
4208 /* We quarantine the secondary IO if this is the second or subsequent data phase */
4209 if (io->xbusy) {
4210 io->sec_hio->quarantine = TRUE;
4211 }
4212 }
4213
4214 /*
4215 * If not the first data phase, and io->sec_hio has been allocated, then issue
4216 * FCP_CONT_TRECEIVE64 WQE, otherwise use the usual FCP_TRECEIVE64 WQE
4217 */
4218 if (io->xbusy && (io->sec_hio != NULL)) {
4219 if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4220 iparam->fcp_tgt.offset, len, io->indicator, io->sec_hio->indicator,
4221 io->reqtag, SLI4_CQ_DEFAULT,
4222 iparam->fcp_tgt.ox_id, rpi, rnode,
4223 flags,
4224 iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4225 iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4226 ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4227 rc = OCS_HW_RTN_ERROR;
4228 }
4229 } else {
4230 if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4231 iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4232 SLI4_CQ_DEFAULT,
4233 iparam->fcp_tgt.ox_id, rpi, rnode,
4234 flags,
4235 iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
4236 iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
4237 ocs_log_err(hw->os, "TRECEIVE WQE error\n");
4238 rc = OCS_HW_RTN_ERROR;
4239 }
4240 }
4241 break;
4242 }
4243 case OCS_HW_IO_TARGET_READ: {
4244 uint16_t flags = iparam->fcp_tgt.flags;
4245
4246 if (io->xbusy) {
4247 flags |= SLI4_IO_CONTINUATION;
4248 } else {
4249 flags &= ~SLI4_IO_CONTINUATION;
4250 }
4251
4252 io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4253 if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
4254 iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
4255 SLI4_CQ_DEFAULT,
4256 iparam->fcp_tgt.ox_id, rpi, rnode,
4257 flags,
4258 iparam->fcp_tgt.dif_oper,
4259 iparam->fcp_tgt.blk_size,
4260 iparam->fcp_tgt.cs_ctl,
4261 iparam->fcp_tgt.app_id)) {
4262 ocs_log_err(hw->os, "TSEND WQE error\n");
4263 rc = OCS_HW_RTN_ERROR;
4264 } else if (hw->workaround.retain_tsend_io_length) {
4265 io->length = len;
4266 }
4267 break;
4268 }
4269 case OCS_HW_IO_TARGET_RSP: {
4270 uint16_t flags = iparam->fcp_tgt.flags;
4271
4272 if (io->xbusy) {
4273 flags |= SLI4_IO_CONTINUATION;
4274 } else {
4275 flags &= ~SLI4_IO_CONTINUATION;
4276 }
4277
4278 /* post a new auto xfer ready buffer */
4279 if (hw->auto_xfer_rdy_enabled && io->is_port_owned) {
4280 if ((io->auto_xfer_rdy_dnrx = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1))) {
4281 flags |= SLI4_IO_DNRX;
4282 }
4283 }
4284
4285 io->tgt_wqe_timeout = iparam->fcp_tgt.timeout;
4286 if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size,
4287 &io->def_sgl,
4288 len,
4289 io->indicator, io->reqtag,
4290 SLI4_CQ_DEFAULT,
4291 iparam->fcp_tgt.ox_id,
4292 rpi, rnode,
4293 flags, iparam->fcp_tgt.cs_ctl,
4294 io->is_port_owned,
4295 iparam->fcp_tgt.app_id)) {
4296 ocs_log_err(hw->os, "TRSP WQE error\n");
4297 rc = OCS_HW_RTN_ERROR;
4298 }
4299
4300 break;
4301 }
4302 default:
4303 ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4304 rc = OCS_HW_RTN_ERROR;
4305 }
4306
4307 if (send_wqe && (OCS_HW_RTN_SUCCESS == rc)) {
4308 if (io->wq == NULL) {
4309 io->wq = ocs_hw_queue_next_wq(hw, io);
4310 ocs_hw_assert(io->wq != NULL);
4311 }
4312
4313 io->xbusy = TRUE;
4314
4315 /*
4316 * Add IO to active io wqe list before submitting, in case the
4317 * wcqe processing preempts this thread.
4318 */
4319 OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
4320 OCS_STAT(io->wq->use_count++);
4321 ocs_hw_add_io_timed_wqe(hw, io);
4322 rc = hw_wq_write(io->wq, &io->wqe);
4323 if (rc >= 0) {
4324 /* non-negative return is success */
4325 rc = 0;
4326 } else {
4327 /* failed to write wqe, remove from active wqe list */
4328 ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
4329 io->xbusy = FALSE;
4330 ocs_hw_remove_io_timed_wqe(hw, io);
4331 }
4332 }
4333
4334 return rc;
4335 }
4336
4337 /**
4338 * @brief Send a raw frame
4339 *
4340 * @par Description
4341 * Using the SEND_FRAME_WQE, a frame consisting of header and payload is sent.
4342 *
4343 * @param hw Pointer to HW object.
4344 * @param hdr Pointer to a little endian formatted FC header.
4345 * @param sof Value to use as the frame SOF.
4346 * @param eof Value to use as the frame EOF.
4347 * @param payload Pointer to payload DMA buffer.
4348 * @param ctx Pointer to caller provided send frame context.
4349 * @param callback Callback function.
4350 * @param arg Callback function argument.
4351 *
4352 * @return Returns 0 on success, or a negative error code value on failure.
4353 */
4354 ocs_hw_rtn_e
ocs_hw_send_frame(ocs_hw_t * hw,fc_header_le_t * hdr,uint8_t sof,uint8_t eof,ocs_dma_t * payload,ocs_hw_send_frame_context_t * ctx,void (* callback)(void * arg,uint8_t * cqe,int32_t status),void * arg)4355 ocs_hw_send_frame(ocs_hw_t *hw, fc_header_le_t *hdr, uint8_t sof, uint8_t eof, ocs_dma_t *payload,
4356 ocs_hw_send_frame_context_t *ctx, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
4357 {
4358 int32_t rc;
4359 ocs_hw_wqe_t *wqe;
4360 uint32_t xri;
4361 hw_wq_t *wq;
4362
4363 wqe = &ctx->wqe;
4364
4365 /* populate the callback object */
4366 ctx->hw = hw;
4367
4368 /* Fetch and populate request tag */
4369 ctx->wqcb = ocs_hw_reqtag_alloc(hw, callback, arg);
4370 if (ctx->wqcb == NULL) {
4371 ocs_log_err(hw->os, "can't allocate request tag\n");
4372 return OCS_HW_RTN_NO_RESOURCES;
4373 }
4374
4375 /* Choose a work queue, first look for a class[1] wq, otherwise just use wq[0] */
4376 wq = ocs_varray_iter_next(hw->wq_class_array[1]);
4377 if (wq == NULL) {
4378 wq = hw->hw_wq[0];
4379 }
4380
4381 /* Set XRI and RX_ID in the header based on which WQ, and which send_frame_io we are using */
4382 xri = wq->send_frame_io->indicator;
4383
4384 /* Build the send frame WQE */
4385 rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, hw->sli.config.wqe_size, sof, eof, (uint32_t*) hdr, payload,
4386 payload->len, OCS_HW_SEND_FRAME_TIMEOUT, xri, ctx->wqcb->instance_index);
4387 if (rc) {
4388 ocs_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
4389 return OCS_HW_RTN_ERROR;
4390 }
4391
4392 /* Write to WQ */
4393 rc = hw_wq_write(wq, wqe);
4394 if (rc) {
4395 ocs_log_err(hw->os, "hw_wq_write failed: %d\n", rc);
4396 return OCS_HW_RTN_ERROR;
4397 }
4398
4399 OCS_STAT(wq->use_count++);
4400
4401 return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
4402 }
4403
4404 ocs_hw_rtn_e
ocs_hw_io_register_sgl(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_dma_t * sgl,uint32_t sgl_count)4405 ocs_hw_io_register_sgl(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *sgl, uint32_t sgl_count)
4406 {
4407 if (sli_get_sgl_preregister(&hw->sli)) {
4408 ocs_log_err(hw->os, "can't use temporary SGL with pre-registered SGLs\n");
4409 return OCS_HW_RTN_ERROR;
4410 }
4411 io->ovfl_sgl = sgl;
4412 io->ovfl_sgl_count = sgl_count;
4413 io->ovfl_io = NULL;
4414
4415 return OCS_HW_RTN_SUCCESS;
4416 }
4417
4418 static void
ocs_hw_io_restore_sgl(ocs_hw_t * hw,ocs_hw_io_t * io)4419 ocs_hw_io_restore_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4420 {
4421 /* Restore the default */
4422 io->sgl = &io->def_sgl;
4423 io->sgl_count = io->def_sgl_count;
4424
4425 /*
4426 * For skyhawk, we need to free the IO allocated for the chained
4427 * SGL. For all devices, clear the overflow fields on the IO.
4428 *
4429 * Note: For DIF IOs, we may be using the same XRI for the sec_hio and
4430 * the chained SGLs. If so, then we clear the ovfl_io field
4431 * when the sec_hio is freed.
4432 */
4433 if (io->ovfl_io != NULL) {
4434 ocs_hw_io_free(hw, io->ovfl_io);
4435 io->ovfl_io = NULL;
4436 }
4437
4438 /* Clear the overflow SGL */
4439 io->ovfl_sgl = NULL;
4440 io->ovfl_sgl_count = 0;
4441 io->ovfl_lsp = NULL;
4442 }
4443
4444 /**
4445 * @ingroup io
4446 * @brief Initialize the scatter gather list entries of an IO.
4447 *
4448 * @param hw Hardware context.
4449 * @param io Previously-allocated HW IO object.
4450 * @param type Type of IO (target read, target response, and so on).
4451 *
4452 * @return Returns 0 on success, or a non-zero value on failure.
4453 */
4454 ocs_hw_rtn_e
ocs_hw_io_init_sges(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_hw_io_type_e type)4455 ocs_hw_io_init_sges(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_io_type_e type)
4456 {
4457 sli4_sge_t *data = NULL;
4458 uint32_t i = 0;
4459 uint32_t skips = 0;
4460
4461 if (!hw || !io) {
4462 ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p\n",
4463 hw, io);
4464 return OCS_HW_RTN_ERROR;
4465 }
4466
4467 /* Clear / reset the scatter-gather list */
4468 io->sgl = &io->def_sgl;
4469 io->sgl_count = io->def_sgl_count;
4470 io->first_data_sge = 0;
4471
4472 ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
4473 io->n_sge = 0;
4474 io->sge_offset = 0;
4475
4476 io->type = type;
4477
4478 data = io->sgl->virt;
4479
4480 /*
4481 * Some IO types have underlying hardware requirements on the order
4482 * of SGEs. Process all special entries here.
4483 */
4484 switch (type) {
4485 case OCS_HW_IO_INITIATOR_READ:
4486 case OCS_HW_IO_INITIATOR_WRITE:
4487 case OCS_HW_IO_INITIATOR_NODATA:
4488 /*
4489 * No skips, 2 special for initiator I/Os
4490 * The addresses and length are written later
4491 */
4492 /* setup command pointer */
4493 data->sge_type = SLI4_SGE_TYPE_DATA;
4494 data++;
4495
4496 /* setup response pointer */
4497 data->sge_type = SLI4_SGE_TYPE_DATA;
4498
4499 if (OCS_HW_IO_INITIATOR_NODATA == type) {
4500 data->last = TRUE;
4501 }
4502 data++;
4503
4504 io->n_sge = 2;
4505 break;
4506 case OCS_HW_IO_TARGET_WRITE:
4507 #define OCS_TARGET_WRITE_SKIPS 2
4508 skips = OCS_TARGET_WRITE_SKIPS;
4509
4510 /* populate host resident XFER_RDY buffer */
4511 data->sge_type = SLI4_SGE_TYPE_DATA;
4512 data->buffer_address_high = ocs_addr32_hi(io->xfer_rdy.phys);
4513 data->buffer_address_low = ocs_addr32_lo(io->xfer_rdy.phys);
4514 data->buffer_length = io->xfer_rdy.size;
4515 data++;
4516
4517 skips--;
4518
4519 io->n_sge = 1;
4520 break;
4521 case OCS_HW_IO_TARGET_READ:
4522 /*
4523 * For FCP_TSEND64, the first 2 entries are SKIP SGE's
4524 */
4525 #define OCS_TARGET_READ_SKIPS 2
4526 skips = OCS_TARGET_READ_SKIPS;
4527 break;
4528 case OCS_HW_IO_TARGET_RSP:
4529 /*
4530 * No skips, etc. for FCP_TRSP64
4531 */
4532 break;
4533 default:
4534 ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
4535 return OCS_HW_RTN_ERROR;
4536 }
4537
4538 /*
4539 * Write skip entries
4540 */
4541 for (i = 0; i < skips; i++) {
4542 data->sge_type = SLI4_SGE_TYPE_SKIP;
4543 data++;
4544 }
4545
4546 io->n_sge += skips;
4547
4548 /*
4549 * Set last
4550 */
4551 data->last = TRUE;
4552
4553 return OCS_HW_RTN_SUCCESS;
4554 }
4555
4556 /**
4557 * @ingroup io
4558 * @brief Add a T10 PI seed scatter gather list entry.
4559 *
4560 * @param hw Hardware context.
4561 * @param io Previously-allocated HW IO object.
4562 * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
4563 *
4564 * @return Returns 0 on success, or a non-zero value on failure.
4565 */
4566 ocs_hw_rtn_e
ocs_hw_io_add_seed_sge(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_hw_dif_info_t * dif_info)4567 ocs_hw_io_add_seed_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_dif_info_t *dif_info)
4568 {
4569 sli4_sge_t *data = NULL;
4570 sli4_diseed_sge_t *dif_seed;
4571
4572 /* If no dif_info, or dif_oper is disabled, then just return success */
4573 if ((dif_info == NULL) || (dif_info->dif_oper == OCS_HW_DIF_OPER_DISABLED)) {
4574 return OCS_HW_RTN_SUCCESS;
4575 }
4576
4577 if (!hw || !io) {
4578 ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p dif_info=%p\n",
4579 hw, io, dif_info);
4580 return OCS_HW_RTN_ERROR;
4581 }
4582
4583 data = io->sgl->virt;
4584 data += io->n_sge;
4585
4586 /* If we are doing T10 DIF add the DIF Seed SGE */
4587 ocs_memset(data, 0, sizeof(sli4_diseed_sge_t));
4588 dif_seed = (sli4_diseed_sge_t *)data;
4589 dif_seed->ref_tag_cmp = dif_info->ref_tag_cmp;
4590 dif_seed->ref_tag_repl = dif_info->ref_tag_repl;
4591 dif_seed->app_tag_repl = dif_info->app_tag_repl;
4592 dif_seed->repl_app_tag = dif_info->repl_app_tag;
4593 if (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) {
4594 dif_seed->atrt = dif_info->disable_app_ref_ffff;
4595 dif_seed->at = dif_info->disable_app_ffff;
4596 }
4597 dif_seed->sge_type = SLI4_SGE_TYPE_DISEED;
4598 /* Workaround for SKH (BZ157233) */
4599 if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4600 (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) && dif_info->dif_separate) {
4601 dif_seed->sge_type = SLI4_SGE_TYPE_SKIP;
4602 }
4603
4604 dif_seed->app_tag_cmp = dif_info->app_tag_cmp;
4605 dif_seed->dif_blk_size = dif_info->blk_size;
4606 dif_seed->auto_incr_ref_tag = dif_info->auto_incr_ref_tag;
4607 dif_seed->check_app_tag = dif_info->check_app_tag;
4608 dif_seed->check_ref_tag = dif_info->check_ref_tag;
4609 dif_seed->check_crc = dif_info->check_guard;
4610 dif_seed->new_ref_tag = dif_info->repl_ref_tag;
4611
4612 switch(dif_info->dif_oper) {
4613 case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
4614 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4615 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
4616 break;
4617 case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
4618 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4619 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
4620 break;
4621 case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM:
4622 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4623 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
4624 break;
4625 case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF:
4626 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4627 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
4628 break;
4629 case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
4630 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4631 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
4632 break;
4633 case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM:
4634 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4635 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
4636 break;
4637 case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
4638 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4639 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
4640 break;
4641 case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
4642 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4643 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
4644 break;
4645 case OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW:
4646 dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4647 dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
4648 break;
4649 default:
4650 ocs_log_err(hw->os, "unsupported DIF operation %#x\n",
4651 dif_info->dif_oper);
4652 return OCS_HW_RTN_ERROR;
4653 }
4654
4655 /*
4656 * Set last, clear previous last
4657 */
4658 data->last = TRUE;
4659 if (io->n_sge) {
4660 data[-1].last = FALSE;
4661 }
4662
4663 io->n_sge++;
4664
4665 return OCS_HW_RTN_SUCCESS;
4666 }
4667
4668 static ocs_hw_rtn_e
ocs_hw_io_overflow_sgl(ocs_hw_t * hw,ocs_hw_io_t * io)4669 ocs_hw_io_overflow_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
4670 {
4671 sli4_lsp_sge_t *lsp;
4672
4673 /* fail if we're already pointing to the overflow SGL */
4674 if (io->sgl == io->ovfl_sgl) {
4675 return OCS_HW_RTN_ERROR;
4676 }
4677
4678 /*
4679 * For skyhawk, we can use another SGL to extend the SGL list. The
4680 * Chained entry must not be in the first 4 entries.
4681 *
4682 * Note: For DIF enabled IOs, we will use the ovfl_io for the sec_hio.
4683 */
4684 if (sli_get_sgl_preregister(&hw->sli) &&
4685 io->def_sgl_count > 4 &&
4686 io->ovfl_io == NULL &&
4687 ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4688 (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
4689 io->ovfl_io = ocs_hw_io_alloc(hw);
4690 if (io->ovfl_io != NULL) {
4691 /*
4692 * Note: We can't call ocs_hw_io_register_sgl() here
4693 * because it checks that SGLs are not pre-registered
4694 * and for shyhawk, preregistered SGLs are required.
4695 */
4696 io->ovfl_sgl = &io->ovfl_io->def_sgl;
4697 io->ovfl_sgl_count = io->ovfl_io->def_sgl_count;
4698 }
4699 }
4700
4701 /* fail if we don't have an overflow SGL registered */
4702 if (io->ovfl_sgl == NULL) {
4703 return OCS_HW_RTN_ERROR;
4704 }
4705
4706 /*
4707 * Overflow, we need to put a link SGE in the last location of the current SGL, after
4708 * copying the the last SGE to the overflow SGL
4709 */
4710
4711 ((sli4_sge_t*)io->ovfl_sgl->virt)[0] = ((sli4_sge_t*)io->sgl->virt)[io->n_sge - 1];
4712
4713 lsp = &((sli4_lsp_sge_t*)io->sgl->virt)[io->n_sge - 1];
4714 ocs_memset(lsp, 0, sizeof(*lsp));
4715
4716 if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
4717 (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
4718 sli_skh_chain_sge_build(&hw->sli,
4719 (sli4_sge_t*)lsp,
4720 io->ovfl_io->indicator,
4721 0, /* frag_num */
4722 0); /* offset */
4723 } else {
4724 lsp->buffer_address_high = ocs_addr32_hi(io->ovfl_sgl->phys);
4725 lsp->buffer_address_low = ocs_addr32_lo(io->ovfl_sgl->phys);
4726 lsp->sge_type = SLI4_SGE_TYPE_LSP;
4727 lsp->last = 0;
4728 io->ovfl_lsp = lsp;
4729 io->ovfl_lsp->segment_length = sizeof(sli4_sge_t);
4730 }
4731
4732 /* Update the current SGL pointer, and n_sgl */
4733 io->sgl = io->ovfl_sgl;
4734 io->sgl_count = io->ovfl_sgl_count;
4735 io->n_sge = 1;
4736
4737 return OCS_HW_RTN_SUCCESS;
4738 }
4739
4740 /**
4741 * @ingroup io
4742 * @brief Add a scatter gather list entry to an IO.
4743 *
4744 * @param hw Hardware context.
4745 * @param io Previously-allocated HW IO object.
4746 * @param addr Physical address.
4747 * @param length Length of memory pointed to by @c addr.
4748 *
4749 * @return Returns 0 on success, or a non-zero value on failure.
4750 */
4751 ocs_hw_rtn_e
ocs_hw_io_add_sge(ocs_hw_t * hw,ocs_hw_io_t * io,uintptr_t addr,uint32_t length)4752 ocs_hw_io_add_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr, uint32_t length)
4753 {
4754 sli4_sge_t *data = NULL;
4755
4756 if (!hw || !io || !addr || !length) {
4757 ocs_log_err(hw ? hw->os : NULL,
4758 "bad parameter hw=%p io=%p addr=%lx length=%u\n",
4759 hw, io, addr, length);
4760 return OCS_HW_RTN_ERROR;
4761 }
4762
4763 if ((length != 0) && (io->n_sge + 1) > io->sgl_count) {
4764 if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_SUCCESS) {
4765 ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4766 return OCS_HW_RTN_ERROR;
4767 }
4768 }
4769
4770 if (length > sli_get_max_sge(&hw->sli)) {
4771 ocs_log_err(hw->os, "length of SGE %d bigger than allowed %d\n",
4772 length, sli_get_max_sge(&hw->sli));
4773 return OCS_HW_RTN_ERROR;
4774 }
4775
4776 data = io->sgl->virt;
4777 data += io->n_sge;
4778
4779 data->sge_type = SLI4_SGE_TYPE_DATA;
4780 data->buffer_address_high = ocs_addr32_hi(addr);
4781 data->buffer_address_low = ocs_addr32_lo(addr);
4782 data->buffer_length = length;
4783 data->data_offset = io->sge_offset;
4784 /*
4785 * Always assume this is the last entry and mark as such.
4786 * If this is not the first entry unset the "last SGE"
4787 * indication for the previous entry
4788 */
4789 data->last = TRUE;
4790 if (io->n_sge) {
4791 data[-1].last = FALSE;
4792 }
4793
4794 /* Set first_data_bde if not previously set */
4795 if (io->first_data_sge == 0) {
4796 io->first_data_sge = io->n_sge;
4797 }
4798
4799 io->sge_offset += length;
4800 io->n_sge++;
4801
4802 /* Update the linked segment length (only executed after overflow has begun) */
4803 if (io->ovfl_lsp != NULL) {
4804 io->ovfl_lsp->segment_length = io->n_sge * sizeof(sli4_sge_t);
4805 }
4806
4807 return OCS_HW_RTN_SUCCESS;
4808 }
4809
4810 /**
4811 * @ingroup io
4812 * @brief Add a T10 DIF scatter gather list entry to an IO.
4813 *
4814 * @param hw Hardware context.
4815 * @param io Previously-allocated HW IO object.
4816 * @param addr DIF physical address.
4817 *
4818 * @return Returns 0 on success, or a non-zero value on failure.
4819 */
4820 ocs_hw_rtn_e
ocs_hw_io_add_dif_sge(ocs_hw_t * hw,ocs_hw_io_t * io,uintptr_t addr)4821 ocs_hw_io_add_dif_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr)
4822 {
4823 sli4_dif_sge_t *data = NULL;
4824
4825 if (!hw || !io || !addr) {
4826 ocs_log_err(hw ? hw->os : NULL,
4827 "bad parameter hw=%p io=%p addr=%lx\n",
4828 hw, io, addr);
4829 return OCS_HW_RTN_ERROR;
4830 }
4831
4832 if ((io->n_sge + 1) > hw->config.n_sgl) {
4833 if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_ERROR) {
4834 ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
4835 return OCS_HW_RTN_ERROR;
4836 }
4837 }
4838
4839 data = io->sgl->virt;
4840 data += io->n_sge;
4841
4842 data->sge_type = SLI4_SGE_TYPE_DIF;
4843 /* Workaround for SKH (BZ157233) */
4844 if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
4845 (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type)) {
4846 data->sge_type = SLI4_SGE_TYPE_SKIP;
4847 }
4848
4849 data->buffer_address_high = ocs_addr32_hi(addr);
4850 data->buffer_address_low = ocs_addr32_lo(addr);
4851
4852 /*
4853 * Always assume this is the last entry and mark as such.
4854 * If this is not the first entry unset the "last SGE"
4855 * indication for the previous entry
4856 */
4857 data->last = TRUE;
4858 if (io->n_sge) {
4859 data[-1].last = FALSE;
4860 }
4861
4862 io->n_sge++;
4863
4864 return OCS_HW_RTN_SUCCESS;
4865 }
4866
4867 /**
4868 * @ingroup io
4869 * @brief Abort a previously-started IO.
4870 *
4871 * @param hw Hardware context.
4872 * @param io_to_abort The IO to abort.
4873 * @param send_abts Boolean to have the hardware automatically
4874 * generate an ABTS.
4875 * @param cb Function call upon completion of the abort (may be NULL).
4876 * @param arg Argument to pass to abort completion function.
4877 *
4878 * @return Returns 0 on success, or a non-zero value on failure.
4879 */
4880 ocs_hw_rtn_e
ocs_hw_io_abort(ocs_hw_t * hw,ocs_hw_io_t * io_to_abort,uint32_t send_abts,void * cb,void * arg)4881 ocs_hw_io_abort(ocs_hw_t *hw, ocs_hw_io_t *io_to_abort, uint32_t send_abts, void *cb, void *arg)
4882 {
4883 sli4_abort_type_e atype = SLI_ABORT_MAX;
4884 uint32_t id = 0, mask = 0;
4885 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
4886 hw_wq_callback_t *wqcb;
4887
4888 if (!hw || !io_to_abort) {
4889 ocs_log_err(hw ? hw->os : NULL,
4890 "bad parameter hw=%p io=%p\n",
4891 hw, io_to_abort);
4892 return OCS_HW_RTN_ERROR;
4893 }
4894
4895 if (hw->state != OCS_HW_STATE_ACTIVE) {
4896 ocs_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
4897 hw->state);
4898 return OCS_HW_RTN_ERROR;
4899 }
4900
4901 /* take a reference on IO being aborted */
4902 if (ocs_ref_get_unless_zero(&io_to_abort->ref) == 0) {
4903 /* command no longer active */
4904 ocs_log_test(hw ? hw->os : NULL,
4905 "io not active xri=0x%x tag=0x%x\n",
4906 io_to_abort->indicator, io_to_abort->reqtag);
4907 return OCS_HW_RTN_IO_NOT_ACTIVE;
4908 }
4909
4910 /* non-port owned XRI checks */
4911 /* Must have a valid WQ reference */
4912 if (io_to_abort->wq == NULL) {
4913 ocs_log_test(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
4914 io_to_abort->indicator);
4915 ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4916 return OCS_HW_RTN_IO_NOT_ACTIVE;
4917 }
4918
4919 /* Validation checks complete; now check to see if already being aborted */
4920 ocs_lock(&hw->io_abort_lock);
4921 if (io_to_abort->abort_in_progress) {
4922 ocs_unlock(&hw->io_abort_lock);
4923 ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
4924 ocs_log_debug(hw ? hw->os : NULL,
4925 "io already being aborted xri=0x%x tag=0x%x\n",
4926 io_to_abort->indicator, io_to_abort->reqtag);
4927 return OCS_HW_RTN_IO_ABORT_IN_PROGRESS;
4928 }
4929
4930 /*
4931 * This IO is not already being aborted. Set flag so we won't try to
4932 * abort it again. After all, we only have one abort_done callback.
4933 */
4934 io_to_abort->abort_in_progress = 1;
4935 ocs_unlock(&hw->io_abort_lock);
4936
4937 /*
4938 * If we got here, the possibilities are:
4939 * - host owned xri
4940 * - io_to_abort->wq_index != UINT32_MAX
4941 * - submit ABORT_WQE to same WQ
4942 * - port owned xri:
4943 * - rxri: io_to_abort->wq_index == UINT32_MAX
4944 * - submit ABORT_WQE to any WQ
4945 * - non-rxri
4946 * - io_to_abort->index != UINT32_MAX
4947 * - submit ABORT_WQE to same WQ
4948 * - io_to_abort->index == UINT32_MAX
4949 * - submit ABORT_WQE to any WQ
4950 */
4951 io_to_abort->abort_done = cb;
4952 io_to_abort->abort_arg = arg;
4953
4954 atype = SLI_ABORT_XRI;
4955 id = io_to_abort->indicator;
4956
4957 /* Allocate a request tag for the abort portion of this IO */
4958 wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_abort, io_to_abort);
4959 if (wqcb == NULL) {
4960 ocs_log_err(hw->os, "can't allocate request tag\n");
4961 return OCS_HW_RTN_NO_RESOURCES;
4962 }
4963 io_to_abort->abort_reqtag = wqcb->instance_index;
4964
4965 /*
4966 * If the wqe is on the pending list, then set this wqe to be
4967 * aborted when the IO's wqe is removed from the list.
4968 */
4969 if (io_to_abort->wq != NULL) {
4970 sli_queue_lock(io_to_abort->wq->queue);
4971 if (ocs_list_on_list(&io_to_abort->wqe.link)) {
4972 io_to_abort->wqe.abort_wqe_submit_needed = 1;
4973 io_to_abort->wqe.send_abts = send_abts;
4974 io_to_abort->wqe.id = id;
4975 io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
4976 sli_queue_unlock(io_to_abort->wq->queue);
4977 return 0;
4978 }
4979 sli_queue_unlock(io_to_abort->wq->queue);
4980 }
4981
4982 if (sli_abort_wqe(&hw->sli, io_to_abort->wqe.wqebuf, hw->sli.config.wqe_size, atype, send_abts, id, mask,
4983 io_to_abort->abort_reqtag, SLI4_CQ_DEFAULT)) {
4984 ocs_log_err(hw->os, "ABORT WQE error\n");
4985 io_to_abort->abort_reqtag = UINT32_MAX;
4986 ocs_hw_reqtag_free(hw, wqcb);
4987 rc = OCS_HW_RTN_ERROR;
4988 }
4989
4990 if (OCS_HW_RTN_SUCCESS == rc) {
4991 if (io_to_abort->wq == NULL) {
4992 io_to_abort->wq = ocs_hw_queue_next_wq(hw, io_to_abort);
4993 ocs_hw_assert(io_to_abort->wq != NULL);
4994 }
4995 /* ABORT_WQE does not actually utilize an XRI on the Port,
4996 * therefore, keep xbusy as-is to track the exchange's state,
4997 * not the ABORT_WQE's state
4998 */
4999 rc = hw_wq_write(io_to_abort->wq, &io_to_abort->wqe);
5000 if (rc > 0) {
5001 /* non-negative return is success */
5002 rc = 0;
5003 /* can't abort an abort so skip adding to timed wqe list */
5004 }
5005 }
5006
5007 if (OCS_HW_RTN_SUCCESS != rc) {
5008 ocs_lock(&hw->io_abort_lock);
5009 io_to_abort->abort_in_progress = 0;
5010 ocs_unlock(&hw->io_abort_lock);
5011 ocs_ref_put(&io_to_abort->ref); /* ocs_ref_get(): same function */
5012 }
5013 return rc;
5014 }
5015
5016 /**
5017 * @ingroup io
5018 * @brief Return the OX_ID/RX_ID of the IO.
5019 *
5020 * @param hw Hardware context.
5021 * @param io HW IO object.
5022 *
5023 * @return Returns X_ID on success, or -1 on failure.
5024 */
5025 int32_t
ocs_hw_io_get_xid(ocs_hw_t * hw,ocs_hw_io_t * io)5026 ocs_hw_io_get_xid(ocs_hw_t *hw, ocs_hw_io_t *io)
5027 {
5028 if (!hw || !io) {
5029 ocs_log_err(hw ? hw->os : NULL,
5030 "bad parameter hw=%p io=%p\n", hw, io);
5031 return -1;
5032 }
5033
5034 return io->indicator;
5035 }
5036
5037
5038 typedef struct ocs_hw_fw_write_cb_arg {
5039 ocs_hw_fw_cb_t cb;
5040 void *arg;
5041 } ocs_hw_fw_write_cb_arg_t;
5042
5043 typedef struct ocs_hw_sfp_cb_arg {
5044 ocs_hw_sfp_cb_t cb;
5045 void *arg;
5046 ocs_dma_t payload;
5047 } ocs_hw_sfp_cb_arg_t;
5048
5049 typedef struct ocs_hw_temp_cb_arg {
5050 ocs_hw_temp_cb_t cb;
5051 void *arg;
5052 } ocs_hw_temp_cb_arg_t;
5053
5054 typedef struct ocs_hw_link_stat_cb_arg {
5055 ocs_hw_link_stat_cb_t cb;
5056 void *arg;
5057 } ocs_hw_link_stat_cb_arg_t;
5058
5059 typedef struct ocs_hw_host_stat_cb_arg {
5060 ocs_hw_host_stat_cb_t cb;
5061 void *arg;
5062 } ocs_hw_host_stat_cb_arg_t;
5063
5064 typedef struct ocs_hw_dump_get_cb_arg {
5065 ocs_hw_dump_get_cb_t cb;
5066 void *arg;
5067 void *mbox_cmd;
5068 } ocs_hw_dump_get_cb_arg_t;
5069
5070 typedef struct ocs_hw_dump_clear_cb_arg {
5071 ocs_hw_dump_clear_cb_t cb;
5072 void *arg;
5073 void *mbox_cmd;
5074 } ocs_hw_dump_clear_cb_arg_t;
5075
5076 /**
5077 * @brief Write a portion of a firmware image to the device.
5078 *
5079 * @par Description
5080 * Calls the correct firmware write function based on the device type.
5081 *
5082 * @param hw Hardware context.
5083 * @param dma DMA structure containing the firmware image chunk.
5084 * @param size Size of the firmware image chunk.
5085 * @param offset Offset, in bytes, from the beginning of the firmware image.
5086 * @param last True if this is the last chunk of the image.
5087 * Causes the image to be committed to flash.
5088 * @param cb Pointer to a callback function that is called when the command completes.
5089 * The callback function prototype is
5090 * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5091 * @param arg Pointer to be passed to the callback function.
5092 *
5093 * @return Returns 0 on success, or a non-zero value on failure.
5094 */
5095 ocs_hw_rtn_e
ocs_hw_firmware_write(ocs_hw_t * hw,ocs_dma_t * dma,uint32_t size,uint32_t offset,int last,ocs_hw_fw_cb_t cb,void * arg)5096 ocs_hw_firmware_write(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
5097 {
5098 if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
5099 return ocs_hw_firmware_write_lancer(hw, dma, size, offset, last, cb, arg);
5100 } else {
5101 /* Write firmware_write for BE3/Skyhawk not supported */
5102 return -1;
5103 }
5104 }
5105
5106 /**
5107 * @brief Write a portion of a firmware image to the Emulex XE201 ASIC (Lancer).
5108 *
5109 * @par Description
5110 * Creates a SLI_CONFIG mailbox command, fills it with the correct values to write a
5111 * firmware image chunk, and then sends the command with ocs_hw_command(). On completion,
5112 * the callback function ocs_hw_fw_write_cb() gets called to free the mailbox
5113 * and to signal the caller that the write has completed.
5114 *
5115 * @param hw Hardware context.
5116 * @param dma DMA structure containing the firmware image chunk.
5117 * @param size Size of the firmware image chunk.
5118 * @param offset Offset, in bytes, from the beginning of the firmware image.
5119 * @param last True if this is the last chunk of the image. Causes the image to be committed to flash.
5120 * @param cb Pointer to a callback function that is called when the command completes.
5121 * The callback function prototype is
5122 * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
5123 * @param arg Pointer to be passed to the callback function.
5124 *
5125 * @return Returns 0 on success, or a non-zero value on failure.
5126 */
5127 ocs_hw_rtn_e
ocs_hw_firmware_write_lancer(ocs_hw_t * hw,ocs_dma_t * dma,uint32_t size,uint32_t offset,int last,ocs_hw_fw_cb_t cb,void * arg)5128 ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
5129 {
5130 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5131 uint8_t *mbxdata;
5132 ocs_hw_fw_write_cb_arg_t *cb_arg;
5133 int noc=0; /* No Commit bit - set to 1 for testing */
5134
5135 if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
5136 ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
5137 return OCS_HW_RTN_ERROR;
5138 }
5139
5140 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5141 if (mbxdata == NULL) {
5142 ocs_log_err(hw->os, "failed to malloc mbox\n");
5143 return OCS_HW_RTN_NO_MEMORY;
5144 }
5145
5146 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_fw_write_cb_arg_t), OCS_M_NOWAIT);
5147 if (cb_arg == NULL) {
5148 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5149 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5150 return OCS_HW_RTN_NO_MEMORY;
5151 }
5152
5153 cb_arg->cb = cb;
5154 cb_arg->arg = arg;
5155
5156 if (sli_cmd_common_write_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE, noc, last,
5157 size, offset, "/prg/", dma)) {
5158 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_fw_write, cb_arg);
5159 }
5160
5161 if (rc != OCS_HW_RTN_SUCCESS) {
5162 ocs_log_test(hw->os, "COMMON_WRITE_OBJECT failed\n");
5163 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5164 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5165 }
5166
5167 return rc;
5168
5169 }
5170
5171 /**
5172 * @brief Called when the WRITE OBJECT command completes.
5173 *
5174 * @par Description
5175 * Get the number of bytes actually written out of the response, free the mailbox
5176 * that was malloc'd by ocs_hw_firmware_write(),
5177 * then call the callback and pass the status and bytes written.
5178 *
5179 * @param hw Hardware context.
5180 * @param status Status field from the mbox completion.
5181 * @param mqe Mailbox response structure.
5182 * @param arg Pointer to a callback function that signals the caller that the command is done.
5183 * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_written)</tt>.
5184 *
5185 * @return Returns 0.
5186 */
5187 static int32_t
ocs_hw_cb_fw_write(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5188 ocs_hw_cb_fw_write(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5189 {
5190
5191 sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
5192 sli4_res_common_write_object_t* wr_obj_rsp = (sli4_res_common_write_object_t*) &(mbox_rsp->payload.embed);
5193 ocs_hw_fw_write_cb_arg_t *cb_arg = arg;
5194 uint32_t bytes_written;
5195 uint16_t mbox_status;
5196 uint32_t change_status;
5197
5198 bytes_written = wr_obj_rsp->actual_write_length;
5199 mbox_status = mbox_rsp->hdr.status;
5200 change_status = wr_obj_rsp->change_status;
5201
5202 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5203
5204 if (cb_arg) {
5205 if (cb_arg->cb) {
5206 if ((status == 0) && mbox_status) {
5207 status = mbox_status;
5208 }
5209 cb_arg->cb(status, bytes_written, change_status, cb_arg->arg);
5210 }
5211
5212 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
5213 }
5214
5215 return 0;
5216
5217 }
5218
5219 /**
5220 * @brief Called when the READ_TRANSCEIVER_DATA command completes.
5221 *
5222 * @par Description
5223 * Get the number of bytes read out of the response, free the mailbox that was malloc'd
5224 * by ocs_hw_get_sfp(), then call the callback and pass the status and bytes written.
5225 *
5226 * @param hw Hardware context.
5227 * @param status Status field from the mbox completion.
5228 * @param mqe Mailbox response structure.
5229 * @param arg Pointer to a callback function that signals the caller that the command is done.
5230 * The callback function prototype is
5231 * <tt>void cb(int32_t status, uint32_t bytes_written, uint32_t *data, void *arg)</tt>.
5232 *
5233 * @return Returns 0.
5234 */
5235 static int32_t
ocs_hw_cb_sfp(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5236 ocs_hw_cb_sfp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5237 {
5238
5239 ocs_hw_sfp_cb_arg_t *cb_arg = arg;
5240 ocs_dma_t *payload = NULL;
5241 sli4_res_common_read_transceiver_data_t* mbox_rsp = NULL;
5242 uint32_t bytes_written;
5243
5244 if (cb_arg) {
5245 payload = &(cb_arg->payload);
5246 if (cb_arg->cb) {
5247 mbox_rsp = (sli4_res_common_read_transceiver_data_t*) payload->virt;
5248 bytes_written = mbox_rsp->hdr.response_length;
5249 if ((status == 0) && mbox_rsp->hdr.status) {
5250 status = mbox_rsp->hdr.status;
5251 }
5252 cb_arg->cb(hw->os, status, bytes_written, mbox_rsp->page_data, cb_arg->arg);
5253 }
5254
5255 ocs_dma_free(hw->os, &cb_arg->payload);
5256 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5257 }
5258
5259 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5260 return 0;
5261 }
5262
5263 /**
5264 * @ingroup io
5265 * @brief Function to retrieve the SFP information.
5266 *
5267 * @param hw Hardware context.
5268 * @param page The page of SFP data to retrieve (0xa0 or 0xa2).
5269 * @param cb Function call upon completion of sending the data (may be NULL).
5270 * @param arg Argument to pass to IO completion function.
5271 *
5272 * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5273 */
5274 ocs_hw_rtn_e
ocs_hw_get_sfp(ocs_hw_t * hw,uint16_t page,ocs_hw_sfp_cb_t cb,void * arg)5275 ocs_hw_get_sfp(ocs_hw_t *hw, uint16_t page, ocs_hw_sfp_cb_t cb, void *arg)
5276 {
5277 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5278 ocs_hw_sfp_cb_arg_t *cb_arg;
5279 uint8_t *mbxdata;
5280
5281 /* mbxdata holds the header of the command */
5282 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5283 if (mbxdata == NULL) {
5284 ocs_log_err(hw->os, "failed to malloc mbox\n");
5285 return OCS_HW_RTN_NO_MEMORY;
5286 }
5287
5288 /* cb_arg holds the data that will be passed to the callback on completion */
5289 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_sfp_cb_arg_t), OCS_M_NOWAIT);
5290 if (cb_arg == NULL) {
5291 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5292 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5293 return OCS_HW_RTN_NO_MEMORY;
5294 }
5295
5296 cb_arg->cb = cb;
5297 cb_arg->arg = arg;
5298
5299 /* payload holds the non-embedded portion */
5300 if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_read_transceiver_data_t),
5301 OCS_MIN_DMA_ALIGNMENT)) {
5302 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
5303 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5304 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5305 return OCS_HW_RTN_NO_MEMORY;
5306 }
5307
5308 /* Send the HW command */
5309 if (sli_cmd_common_read_transceiver_data(&hw->sli, mbxdata, SLI4_BMBX_SIZE, page,
5310 &cb_arg->payload)) {
5311 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_sfp, cb_arg);
5312 }
5313
5314 if (rc != OCS_HW_RTN_SUCCESS) {
5315 ocs_log_test(hw->os, "READ_TRANSCEIVER_DATA failed with status %d\n",
5316 rc);
5317 ocs_dma_free(hw->os, &cb_arg->payload);
5318 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
5319 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5320 }
5321
5322 return rc;
5323 }
5324
5325 /**
5326 * @brief Function to retrieve the temperature information.
5327 *
5328 * @param hw Hardware context.
5329 * @param cb Function call upon completion of sending the data (may be NULL).
5330 * @param arg Argument to pass to IO completion function.
5331 *
5332 * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5333 */
5334 ocs_hw_rtn_e
ocs_hw_get_temperature(ocs_hw_t * hw,ocs_hw_temp_cb_t cb,void * arg)5335 ocs_hw_get_temperature(ocs_hw_t *hw, ocs_hw_temp_cb_t cb, void *arg)
5336 {
5337 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5338 ocs_hw_temp_cb_arg_t *cb_arg;
5339 uint8_t *mbxdata;
5340
5341 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5342 if (mbxdata == NULL) {
5343 ocs_log_err(hw->os, "failed to malloc mbox");
5344 return OCS_HW_RTN_NO_MEMORY;
5345 }
5346
5347 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_temp_cb_arg_t), OCS_M_NOWAIT);
5348 if (cb_arg == NULL) {
5349 ocs_log_err(hw->os, "failed to malloc cb_arg");
5350 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5351 return OCS_HW_RTN_NO_MEMORY;
5352 }
5353
5354 cb_arg->cb = cb;
5355 cb_arg->arg = arg;
5356
5357 if (sli_cmd_dump_type4(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5358 SLI4_WKI_TAG_SAT_TEM)) {
5359 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_temp, cb_arg);
5360 }
5361
5362 if (rc != OCS_HW_RTN_SUCCESS) {
5363 ocs_log_test(hw->os, "DUMP_TYPE4 failed\n");
5364 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5365 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5366 }
5367
5368 return rc;
5369 }
5370
5371 /**
5372 * @brief Called when the DUMP command completes.
5373 *
5374 * @par Description
5375 * Get the temperature data out of the response, free the mailbox that was malloc'd
5376 * by ocs_hw_get_temperature(), then call the callback and pass the status and data.
5377 *
5378 * @param hw Hardware context.
5379 * @param status Status field from the mbox completion.
5380 * @param mqe Mailbox response structure.
5381 * @param arg Pointer to a callback function that signals the caller that the command is done.
5382 * The callback function prototype is defined by ocs_hw_temp_cb_t.
5383 *
5384 * @return Returns 0.
5385 */
5386 static int32_t
ocs_hw_cb_temp(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5387 ocs_hw_cb_temp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5388 {
5389
5390 sli4_cmd_dump4_t* mbox_rsp = (sli4_cmd_dump4_t*) mqe;
5391 ocs_hw_temp_cb_arg_t *cb_arg = arg;
5392 uint32_t curr_temp = mbox_rsp->resp_data[0]; /* word 5 */
5393 uint32_t crit_temp_thrshld = mbox_rsp->resp_data[1]; /* word 6*/
5394 uint32_t warn_temp_thrshld = mbox_rsp->resp_data[2]; /* word 7 */
5395 uint32_t norm_temp_thrshld = mbox_rsp->resp_data[3]; /* word 8 */
5396 uint32_t fan_off_thrshld = mbox_rsp->resp_data[4]; /* word 9 */
5397 uint32_t fan_on_thrshld = mbox_rsp->resp_data[5]; /* word 10 */
5398
5399 if (cb_arg) {
5400 if (cb_arg->cb) {
5401 if ((status == 0) && mbox_rsp->hdr.status) {
5402 status = mbox_rsp->hdr.status;
5403 }
5404 cb_arg->cb(status,
5405 curr_temp,
5406 crit_temp_thrshld,
5407 warn_temp_thrshld,
5408 norm_temp_thrshld,
5409 fan_off_thrshld,
5410 fan_on_thrshld,
5411 cb_arg->arg);
5412 }
5413
5414 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
5415 }
5416 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5417
5418 return 0;
5419 }
5420
5421 /**
5422 * @brief Function to retrieve the link statistics.
5423 *
5424 * @param hw Hardware context.
5425 * @param req_ext_counters If TRUE, then the extended counters will be requested.
5426 * @param clear_overflow_flags If TRUE, then overflow flags will be cleared.
5427 * @param clear_all_counters If TRUE, the counters will be cleared.
5428 * @param cb Function call upon completion of sending the data (may be NULL).
5429 * @param arg Argument to pass to IO completion function.
5430 *
5431 * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5432 */
5433 ocs_hw_rtn_e
ocs_hw_get_link_stats(ocs_hw_t * hw,uint8_t req_ext_counters,uint8_t clear_overflow_flags,uint8_t clear_all_counters,ocs_hw_link_stat_cb_t cb,void * arg)5434 ocs_hw_get_link_stats(ocs_hw_t *hw,
5435 uint8_t req_ext_counters,
5436 uint8_t clear_overflow_flags,
5437 uint8_t clear_all_counters,
5438 ocs_hw_link_stat_cb_t cb,
5439 void *arg)
5440 {
5441 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5442 ocs_hw_link_stat_cb_arg_t *cb_arg;
5443 uint8_t *mbxdata;
5444
5445 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5446 if (mbxdata == NULL) {
5447 ocs_log_err(hw->os, "failed to malloc mbox");
5448 return OCS_HW_RTN_NO_MEMORY;
5449 }
5450
5451 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_link_stat_cb_arg_t), OCS_M_NOWAIT);
5452 if (cb_arg == NULL) {
5453 ocs_log_err(hw->os, "failed to malloc cb_arg");
5454 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5455 return OCS_HW_RTN_NO_MEMORY;
5456 }
5457
5458 cb_arg->cb = cb;
5459 cb_arg->arg = arg;
5460
5461 if (sli_cmd_read_link_stats(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
5462 req_ext_counters,
5463 clear_overflow_flags,
5464 clear_all_counters)) {
5465 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_link_stat, cb_arg);
5466 }
5467
5468 if (rc != OCS_HW_RTN_SUCCESS) {
5469 ocs_log_test(hw->os, "READ_LINK_STATS failed\n");
5470 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5471 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5472 }
5473
5474 return rc;
5475 }
5476
5477 /**
5478 * @brief Called when the READ_LINK_STAT command completes.
5479 *
5480 * @par Description
5481 * Get the counters out of the response, free the mailbox that was malloc'd
5482 * by ocs_hw_get_link_stats(), then call the callback and pass the status and data.
5483 *
5484 * @param hw Hardware context.
5485 * @param status Status field from the mbox completion.
5486 * @param mqe Mailbox response structure.
5487 * @param arg Pointer to a callback function that signals the caller that the command is done.
5488 * The callback function prototype is defined by ocs_hw_link_stat_cb_t.
5489 *
5490 * @return Returns 0.
5491 */
5492 static int32_t
ocs_hw_cb_link_stat(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5493 ocs_hw_cb_link_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5494 {
5495
5496 sli4_cmd_read_link_stats_t* mbox_rsp = (sli4_cmd_read_link_stats_t*) mqe;
5497 ocs_hw_link_stat_cb_arg_t *cb_arg = arg;
5498 ocs_hw_link_stat_counts_t counts[OCS_HW_LINK_STAT_MAX];
5499 uint32_t num_counters = (mbox_rsp->gec ? 20 : 13);
5500
5501 ocs_memset(counts, 0, sizeof(ocs_hw_link_stat_counts_t) *
5502 OCS_HW_LINK_STAT_MAX);
5503
5504 counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].overflow = mbox_rsp->w02of;
5505 counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].overflow = mbox_rsp->w03of;
5506 counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].overflow = mbox_rsp->w04of;
5507 counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].overflow = mbox_rsp->w05of;
5508 counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].overflow = mbox_rsp->w06of;
5509 counts[OCS_HW_LINK_STAT_CRC_COUNT].overflow = mbox_rsp->w07of;
5510 counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].overflow = mbox_rsp->w08of;
5511 counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].overflow = mbox_rsp->w09of;
5512 counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].overflow = mbox_rsp->w10of;
5513 counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].overflow = mbox_rsp->w11of;
5514 counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].overflow = mbox_rsp->w12of;
5515 counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].overflow = mbox_rsp->w13of;
5516 counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].overflow = mbox_rsp->w14of;
5517 counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].overflow = mbox_rsp->w15of;
5518 counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].overflow = mbox_rsp->w16of;
5519 counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].overflow = mbox_rsp->w17of;
5520 counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].overflow = mbox_rsp->w18of;
5521 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].overflow = mbox_rsp->w19of;
5522 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].overflow = mbox_rsp->w20of;
5523 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].overflow = mbox_rsp->w21of;
5524
5525 counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = mbox_rsp->link_failure_error_count;
5526 counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = mbox_rsp->loss_of_sync_error_count;
5527 counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = mbox_rsp->loss_of_signal_error_count;
5528 counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = mbox_rsp->primitive_sequence_error_count;
5529 counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = mbox_rsp->invalid_transmission_word_error_count;
5530 counts[OCS_HW_LINK_STAT_CRC_COUNT].counter = mbox_rsp->crc_error_count;
5531 counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = mbox_rsp->primitive_sequence_event_timeout_count;
5532 counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = mbox_rsp->elastic_buffer_overrun_error_count;
5533 counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = mbox_rsp->arbitration_fc_al_timout_count;
5534 counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = mbox_rsp->advertised_receive_bufftor_to_buffer_credit;
5535 counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = mbox_rsp->current_receive_buffer_to_buffer_credit;
5536 counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = mbox_rsp->advertised_transmit_buffer_to_buffer_credit;
5537 counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = mbox_rsp->current_transmit_buffer_to_buffer_credit;
5538 counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].counter = mbox_rsp->received_eofa_count;
5539 counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = mbox_rsp->received_eofdti_count;
5540 counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = mbox_rsp->received_eofni_count;
5541 counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].counter = mbox_rsp->received_soff_count;
5542 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = mbox_rsp->received_dropped_no_aer_count;
5543 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = mbox_rsp->received_dropped_no_available_rpi_resources_count;
5544 counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = mbox_rsp->received_dropped_no_available_xri_resources_count;
5545
5546 if (cb_arg) {
5547 if (cb_arg->cb) {
5548 if ((status == 0) && mbox_rsp->hdr.status) {
5549 status = mbox_rsp->hdr.status;
5550 }
5551 cb_arg->cb(status,
5552 num_counters,
5553 counts,
5554 cb_arg->arg);
5555 }
5556
5557 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
5558 }
5559 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5560
5561 return 0;
5562 }
5563
5564 /**
5565 * @brief Function to retrieve the link and host statistics.
5566 *
5567 * @param hw Hardware context.
5568 * @param cc clear counters, if TRUE all counters will be cleared.
5569 * @param cb Function call upon completion of receiving the data.
5570 * @param arg Argument to pass to pointer fc hosts statistics structure.
5571 *
5572 * @return Returns OCS_HW_RTN_SUCCESS, OCS_HW_RTN_ERROR, or OCS_HW_RTN_NO_MEMORY.
5573 */
5574 ocs_hw_rtn_e
ocs_hw_get_host_stats(ocs_hw_t * hw,uint8_t cc,ocs_hw_host_stat_cb_t cb,void * arg)5575 ocs_hw_get_host_stats(ocs_hw_t *hw, uint8_t cc, ocs_hw_host_stat_cb_t cb, void *arg)
5576 {
5577 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
5578 ocs_hw_host_stat_cb_arg_t *cb_arg;
5579 uint8_t *mbxdata;
5580
5581 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO);
5582 if (mbxdata == NULL) {
5583 ocs_log_err(hw->os, "failed to malloc mbox");
5584 return OCS_HW_RTN_NO_MEMORY;
5585 }
5586
5587 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_host_stat_cb_arg_t), 0);
5588 if (cb_arg == NULL) {
5589 ocs_log_err(hw->os, "failed to malloc cb_arg");
5590 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5591 return OCS_HW_RTN_NO_MEMORY;
5592 }
5593
5594 cb_arg->cb = cb;
5595 cb_arg->arg = arg;
5596
5597 /* Send the HW command to get the host stats */
5598 if (sli_cmd_read_status(&hw->sli, mbxdata, SLI4_BMBX_SIZE, cc)) {
5599 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_host_stat, cb_arg);
5600 }
5601
5602 if (rc != OCS_HW_RTN_SUCCESS) {
5603 ocs_log_test(hw->os, "READ_HOST_STATS failed\n");
5604 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5605 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5606 }
5607
5608 return rc;
5609 }
5610
5611
5612 /**
5613 * @brief Called when the READ_STATUS command completes.
5614 *
5615 * @par Description
5616 * Get the counters out of the response, free the mailbox that was malloc'd
5617 * by ocs_hw_get_host_stats(), then call the callback and pass
5618 * the status and data.
5619 *
5620 * @param hw Hardware context.
5621 * @param status Status field from the mbox completion.
5622 * @param mqe Mailbox response structure.
5623 * @param arg Pointer to a callback function that signals the caller that the command is done.
5624 * The callback function prototype is defined by
5625 * ocs_hw_host_stat_cb_t.
5626 *
5627 * @return Returns 0.
5628 */
5629 static int32_t
ocs_hw_cb_host_stat(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5630 ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5631 {
5632
5633 sli4_cmd_read_status_t* mbox_rsp = (sli4_cmd_read_status_t*) mqe;
5634 ocs_hw_host_stat_cb_arg_t *cb_arg = arg;
5635 ocs_hw_host_stat_counts_t counts[OCS_HW_HOST_STAT_MAX];
5636 uint32_t num_counters = OCS_HW_HOST_STAT_MAX;
5637
5638 ocs_memset(counts, 0, sizeof(ocs_hw_host_stat_counts_t) *
5639 OCS_HW_HOST_STAT_MAX);
5640
5641 counts[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter = mbox_rsp->transmit_kbyte_count;
5642 counts[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter = mbox_rsp->receive_kbyte_count;
5643 counts[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter = mbox_rsp->transmit_frame_count;
5644 counts[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter = mbox_rsp->receive_frame_count;
5645 counts[OCS_HW_HOST_STAT_TX_SEQ_COUNT].counter = mbox_rsp->transmit_sequence_count;
5646 counts[OCS_HW_HOST_STAT_RX_SEQ_COUNT].counter = mbox_rsp->receive_sequence_count;
5647 counts[OCS_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = mbox_rsp->total_exchanges_originator;
5648 counts[OCS_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = mbox_rsp->total_exchanges_responder;
5649 counts[OCS_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = mbox_rsp->receive_p_bsy_count;
5650 counts[OCS_HW_HOST_STAT_RX_F_BSY_COUNT].counter = mbox_rsp->receive_f_bsy_count;
5651 counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_rq_buffer_count;
5652 counts[OCS_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = mbox_rsp->empty_rq_timeout_count;
5653 counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_xri_count;
5654 counts[OCS_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = mbox_rsp->empty_xri_pool_count;
5655
5656
5657 if (cb_arg) {
5658 if (cb_arg->cb) {
5659 if ((status == 0) && mbox_rsp->hdr.status) {
5660 status = mbox_rsp->hdr.status;
5661 }
5662 cb_arg->cb(status,
5663 num_counters,
5664 counts,
5665 cb_arg->arg);
5666 }
5667
5668 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
5669 }
5670 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
5671
5672 return 0;
5673 }
5674
5675 /**
5676 * @brief HW link configuration enum to the CLP string value mapping.
5677 *
5678 * This structure provides a mapping from the ocs_hw_linkcfg_e
5679 * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5680 * control) to the CLP string that is used
5681 * in the DMTF_CLP_CMD mailbox command.
5682 */
5683 typedef struct ocs_hw_linkcfg_map_s {
5684 ocs_hw_linkcfg_e linkcfg;
5685 const char *clp_str;
5686 } ocs_hw_linkcfg_map_t;
5687
5688 /**
5689 * @brief Mapping from the HW linkcfg enum to the CLP command value
5690 * string.
5691 */
5692 static ocs_hw_linkcfg_map_t linkcfg_map[] = {
5693 {OCS_HW_LINKCFG_4X10G, "ELX_4x10G"},
5694 {OCS_HW_LINKCFG_1X40G, "ELX_1x40G"},
5695 {OCS_HW_LINKCFG_2X16G, "ELX_2x16G"},
5696 {OCS_HW_LINKCFG_4X8G, "ELX_4x8G"},
5697 {OCS_HW_LINKCFG_4X1G, "ELX_4x1G"},
5698 {OCS_HW_LINKCFG_2X10G, "ELX_2x10G"},
5699 {OCS_HW_LINKCFG_2X10G_2X8G, "ELX_2x10G_2x8G"}};
5700
5701 /**
5702 * @brief HW link configuration enum to Skyhawk link config ID mapping.
5703 *
5704 * This structure provides a mapping from the ocs_hw_linkcfg_e
5705 * enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
5706 * control) to the link config ID numbers used by Skyhawk
5707 */
5708 typedef struct ocs_hw_skyhawk_linkcfg_map_s {
5709 ocs_hw_linkcfg_e linkcfg;
5710 uint32_t config_id;
5711 } ocs_hw_skyhawk_linkcfg_map_t;
5712
5713 /**
5714 * @brief Mapping from the HW linkcfg enum to the Skyhawk link config IDs
5715 */
5716 static ocs_hw_skyhawk_linkcfg_map_t skyhawk_linkcfg_map[] = {
5717 {OCS_HW_LINKCFG_4X10G, 0x0a},
5718 {OCS_HW_LINKCFG_1X40G, 0x09},
5719 };
5720
5721 /**
5722 * @brief Helper function for getting the HW linkcfg enum from the CLP
5723 * string value
5724 *
5725 * @param clp_str CLP string value from OEMELX_LinkConfig.
5726 *
5727 * @return Returns the HW linkcfg enum corresponding to clp_str.
5728 */
5729 static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_clp(const char * clp_str)5730 ocs_hw_linkcfg_from_clp(const char *clp_str)
5731 {
5732 uint32_t i;
5733 for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5734 if (ocs_strncmp(linkcfg_map[i].clp_str, clp_str, ocs_strlen(clp_str)) == 0) {
5735 return linkcfg_map[i].linkcfg;
5736 }
5737 }
5738 return OCS_HW_LINKCFG_NA;
5739 }
5740
5741 /**
5742 * @brief Helper function for getting the CLP string value from the HW
5743 * linkcfg enum.
5744 *
5745 * @param linkcfg HW linkcfg enum.
5746 *
5747 * @return Returns the OEMELX_LinkConfig CLP string value corresponding to
5748 * given linkcfg.
5749 */
5750 static const char *
ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)5751 ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5752 {
5753 uint32_t i;
5754 for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
5755 if (linkcfg_map[i].linkcfg == linkcfg) {
5756 return linkcfg_map[i].clp_str;
5757 }
5758 }
5759 return NULL;
5760 }
5761
5762 /**
5763 * @brief Helper function for getting a Skyhawk link config ID from the HW
5764 * linkcfg enum.
5765 *
5766 * @param linkcfg HW linkcfg enum.
5767 *
5768 * @return Returns the Skyhawk link config ID corresponding to
5769 * given linkcfg.
5770 */
5771 static uint32_t
ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)5772 ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
5773 {
5774 uint32_t i;
5775 for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5776 if (skyhawk_linkcfg_map[i].linkcfg == linkcfg) {
5777 return skyhawk_linkcfg_map[i].config_id;
5778 }
5779 }
5780 return 0;
5781 }
5782
5783 /**
5784 * @brief Helper function for getting the HW linkcfg enum from a
5785 * Skyhawk config ID.
5786 *
5787 * @param config_id Skyhawk link config ID.
5788 *
5789 * @return Returns the HW linkcfg enum corresponding to config_id.
5790 */
5791 static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_config_id(const uint32_t config_id)5792 ocs_hw_linkcfg_from_config_id(const uint32_t config_id)
5793 {
5794 uint32_t i;
5795 for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
5796 if (skyhawk_linkcfg_map[i].config_id == config_id) {
5797 return skyhawk_linkcfg_map[i].linkcfg;
5798 }
5799 }
5800 return OCS_HW_LINKCFG_NA;
5801 }
5802
5803 /**
5804 * @brief Link configuration callback argument.
5805 */
5806 typedef struct ocs_hw_linkcfg_cb_arg_s {
5807 ocs_hw_port_control_cb_t cb;
5808 void *arg;
5809 uint32_t opts;
5810 int32_t status;
5811 ocs_dma_t dma_cmd;
5812 ocs_dma_t dma_resp;
5813 uint32_t result_len;
5814 } ocs_hw_linkcfg_cb_arg_t;
5815
5816 /**
5817 * @brief Set link configuration.
5818 *
5819 * @param hw Hardware context.
5820 * @param value Link configuration enum to which the link configuration is
5821 * set.
5822 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5823 * @param cb Callback function to invoke following mbx command.
5824 * @param arg Callback argument.
5825 *
5826 * @return Returns OCS_HW_RTN_SUCCESS on success.
5827 */
5828 static ocs_hw_rtn_e
ocs_hw_set_linkcfg(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5829 ocs_hw_set_linkcfg(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5830 {
5831 if (!sli_link_is_configurable(&hw->sli)) {
5832 ocs_log_debug(hw->os, "Function not supported\n");
5833 return OCS_HW_RTN_ERROR;
5834 }
5835
5836 if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
5837 return ocs_hw_set_linkcfg_lancer(hw, value, opts, cb, arg);
5838 } else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
5839 (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
5840 return ocs_hw_set_linkcfg_skyhawk(hw, value, opts, cb, arg);
5841 } else {
5842 ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
5843 return OCS_HW_RTN_ERROR;
5844 }
5845 }
5846
5847 /**
5848 * @brief Set link configuration for Lancer
5849 *
5850 * @param hw Hardware context.
5851 * @param value Link configuration enum to which the link configuration is
5852 * set.
5853 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5854 * @param cb Callback function to invoke following mbx command.
5855 * @param arg Callback argument.
5856 *
5857 * @return Returns OCS_HW_RTN_SUCCESS on success.
5858 */
5859 static ocs_hw_rtn_e
ocs_hw_set_linkcfg_lancer(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5860 ocs_hw_set_linkcfg_lancer(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5861 {
5862 char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
5863 ocs_hw_linkcfg_cb_arg_t *cb_arg;
5864 const char *value_str = NULL;
5865 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5866
5867 /* translate ocs_hw_linkcfg_e to CLP string */
5868 value_str = ocs_hw_clp_from_linkcfg(value);
5869
5870 /* allocate memory for callback argument */
5871 cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
5872 if (cb_arg == NULL) {
5873 ocs_log_err(hw->os, "failed to malloc cb_arg");
5874 return OCS_HW_RTN_NO_MEMORY;
5875 }
5876
5877 ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_LinkConfig=%s", value_str);
5878 /* allocate DMA for command */
5879 if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
5880 ocs_log_err(hw->os, "malloc failed\n");
5881 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5882 return OCS_HW_RTN_NO_MEMORY;
5883 }
5884 ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
5885 ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
5886
5887 /* allocate DMA for response */
5888 if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
5889 ocs_log_err(hw->os, "malloc failed\n");
5890 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5891 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5892 return OCS_HW_RTN_NO_MEMORY;
5893 }
5894 cb_arg->cb = cb;
5895 cb_arg->arg = arg;
5896 cb_arg->opts = opts;
5897
5898 rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
5899 opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
5900
5901 if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
5902 /* if failed, or polling, free memory here; if success and not
5903 * polling, will free in callback function
5904 */
5905 if (rc) {
5906 ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
5907 (char *)cb_arg->dma_cmd.virt);
5908 }
5909 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
5910 ocs_dma_free(hw->os, &cb_arg->dma_resp);
5911 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5912 }
5913 return rc;
5914 }
5915
5916 /**
5917 * @brief Callback for ocs_hw_set_linkcfg_skyhawk
5918 *
5919 * @param hw Hardware context.
5920 * @param status Status from the RECONFIG_GET_LINK_INFO command.
5921 * @param mqe Mailbox response structure.
5922 * @param arg Pointer to a callback argument.
5923 *
5924 * @return none
5925 */
5926 static void
ocs_hw_set_active_link_config_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)5927 ocs_hw_set_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
5928 {
5929 ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
5930
5931 if (status) {
5932 ocs_log_test(hw->os, "SET_RECONFIG_LINK_ID failed, status=%d\n", status);
5933 }
5934
5935 /* invoke callback */
5936 if (cb_arg->cb) {
5937 cb_arg->cb(status, 0, cb_arg->arg);
5938 }
5939
5940 /* if polling, will free memory in calling function */
5941 if (cb_arg->opts != OCS_CMD_POLL) {
5942 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
5943 }
5944 }
5945
5946 /**
5947 * @brief Set link configuration for a Skyhawk
5948 *
5949 * @param hw Hardware context.
5950 * @param value Link configuration enum to which the link configuration is
5951 * set.
5952 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
5953 * @param cb Callback function to invoke following mbx command.
5954 * @param arg Callback argument.
5955 *
5956 * @return Returns OCS_HW_RTN_SUCCESS on success.
5957 */
5958 static ocs_hw_rtn_e
ocs_hw_set_linkcfg_skyhawk(ocs_hw_t * hw,ocs_hw_linkcfg_e value,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)5959 ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
5960 {
5961 uint8_t *mbxdata;
5962 ocs_hw_linkcfg_cb_arg_t *cb_arg;
5963 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
5964 uint32_t config_id;
5965
5966 config_id = ocs_hw_config_id_from_linkcfg(value);
5967
5968 if (config_id == 0) {
5969 ocs_log_test(hw->os, "Link config %d not supported by Skyhawk\n", value);
5970 return OCS_HW_RTN_ERROR;
5971 }
5972
5973 /* mbxdata holds the header of the command */
5974 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
5975 if (mbxdata == NULL) {
5976 ocs_log_err(hw->os, "failed to malloc mbox\n");
5977 return OCS_HW_RTN_NO_MEMORY;
5978 }
5979
5980 /* cb_arg holds the data that will be passed to the callback on completion */
5981 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
5982 if (cb_arg == NULL) {
5983 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
5984 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5985 return OCS_HW_RTN_NO_MEMORY;
5986 }
5987
5988 cb_arg->cb = cb;
5989 cb_arg->arg = arg;
5990
5991 if (sli_cmd_common_set_reconfig_link_id(&hw->sli, mbxdata, SLI4_BMBX_SIZE, NULL, 0, config_id)) {
5992 rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_set_active_link_config_cb, cb_arg);
5993 }
5994
5995 if (rc != OCS_HW_RTN_SUCCESS) {
5996 ocs_log_err(hw->os, "SET_RECONFIG_LINK_ID failed\n");
5997 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
5998 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
5999 } else if (opts == OCS_CMD_POLL) {
6000 /* if we're polling we have to call the callback here. */
6001 ocs_hw_set_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6002 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6003 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6004 } else {
6005 /* We weren't poling, so the callback got called */
6006 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6007 }
6008
6009 return rc;
6010 }
6011
6012 /**
6013 * @brief Get link configuration.
6014 *
6015 * @param hw Hardware context.
6016 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6017 * @param cb Callback function to invoke following mbx command.
6018 * @param arg Callback argument.
6019 *
6020 * @return Returns OCS_HW_RTN_SUCCESS on success.
6021 */
6022 static ocs_hw_rtn_e
ocs_hw_get_linkcfg(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6023 ocs_hw_get_linkcfg(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6024 {
6025 if (!sli_link_is_configurable(&hw->sli)) {
6026 ocs_log_debug(hw->os, "Function not supported\n");
6027 return OCS_HW_RTN_ERROR;
6028 }
6029
6030 if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
6031 (SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))){
6032 return ocs_hw_get_linkcfg_lancer(hw, opts, cb, arg);
6033 } else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
6034 (SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
6035 return ocs_hw_get_linkcfg_skyhawk(hw, opts, cb, arg);
6036 } else {
6037 ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
6038 return OCS_HW_RTN_ERROR;
6039 }
6040 }
6041
6042 /**
6043 * @brief Get link configuration for a Lancer
6044 *
6045 * @param hw Hardware context.
6046 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6047 * @param cb Callback function to invoke following mbx command.
6048 * @param arg Callback argument.
6049 *
6050 * @return Returns OCS_HW_RTN_SUCCESS on success.
6051 */
6052 static ocs_hw_rtn_e
ocs_hw_get_linkcfg_lancer(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6053 ocs_hw_get_linkcfg_lancer(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6054 {
6055 char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6056 ocs_hw_linkcfg_cb_arg_t *cb_arg;
6057 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6058
6059 /* allocate memory for callback argument */
6060 cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6061 if (cb_arg == NULL) {
6062 ocs_log_err(hw->os, "failed to malloc cb_arg");
6063 return OCS_HW_RTN_NO_MEMORY;
6064 }
6065
6066 ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "show / OEMELX_LinkConfig");
6067
6068 /* allocate DMA for command */
6069 if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6070 ocs_log_err(hw->os, "malloc failed\n");
6071 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6072 return OCS_HW_RTN_NO_MEMORY;
6073 }
6074
6075 /* copy CLP command to DMA command */
6076 ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6077 ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
6078
6079 /* allocate DMA for response */
6080 if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6081 ocs_log_err(hw->os, "malloc failed\n");
6082 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6083 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6084 return OCS_HW_RTN_NO_MEMORY;
6085 }
6086 cb_arg->cb = cb;
6087 cb_arg->arg = arg;
6088 cb_arg->opts = opts;
6089
6090 rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
6091 opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
6092
6093 if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6094 /* if failed or polling, free memory here; if not polling and success,
6095 * will free in callback function
6096 */
6097 if (rc) {
6098 ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
6099 (char *)cb_arg->dma_cmd.virt);
6100 }
6101 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6102 ocs_dma_free(hw->os, &cb_arg->dma_resp);
6103 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6104 }
6105 return rc;
6106 }
6107
6108
6109 /**
6110 * @brief Get the link configuration callback.
6111 *
6112 * @param hw Hardware context.
6113 * @param status Status from the RECONFIG_GET_LINK_INFO command.
6114 * @param mqe Mailbox response structure.
6115 * @param arg Pointer to a callback argument.
6116 *
6117 * @return none
6118 */
6119 static void
ocs_hw_get_active_link_config_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6120 ocs_hw_get_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
6121 {
6122 ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6123 sli4_res_common_get_reconfig_link_info_t *rsp = cb_arg->dma_cmd.virt;
6124 ocs_hw_linkcfg_e value = OCS_HW_LINKCFG_NA;
6125
6126 if (status) {
6127 ocs_log_test(hw->os, "GET_RECONFIG_LINK_INFO failed, status=%d\n", status);
6128 } else {
6129 /* Call was successful */
6130 value = ocs_hw_linkcfg_from_config_id(rsp->active_link_config_id);
6131 }
6132
6133 /* invoke callback */
6134 if (cb_arg->cb) {
6135 cb_arg->cb(status, value, cb_arg->arg);
6136 }
6137
6138 /* if polling, will free memory in calling function */
6139 if (cb_arg->opts != OCS_CMD_POLL) {
6140 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6141 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6142 }
6143 }
6144
6145 /**
6146 * @brief Get link configuration for a Skyhawk.
6147 *
6148 * @param hw Hardware context.
6149 * @param opts Mailbox command options (OCS_CMD_NOWAIT/POLL).
6150 * @param cb Callback function to invoke following mbx command.
6151 * @param arg Callback argument.
6152 *
6153 * @return Returns OCS_HW_RTN_SUCCESS on success.
6154 */
6155 static ocs_hw_rtn_e
ocs_hw_get_linkcfg_skyhawk(ocs_hw_t * hw,uint32_t opts,ocs_hw_port_control_cb_t cb,void * arg)6156 ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
6157 {
6158 uint8_t *mbxdata;
6159 ocs_hw_linkcfg_cb_arg_t *cb_arg;
6160 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6161
6162 /* mbxdata holds the header of the command */
6163 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6164 if (mbxdata == NULL) {
6165 ocs_log_err(hw->os, "failed to malloc mbox\n");
6166 return OCS_HW_RTN_NO_MEMORY;
6167 }
6168
6169 /* cb_arg holds the data that will be passed to the callback on completion */
6170 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
6171 if (cb_arg == NULL) {
6172 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
6173 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6174 return OCS_HW_RTN_NO_MEMORY;
6175 }
6176
6177 cb_arg->cb = cb;
6178 cb_arg->arg = arg;
6179 cb_arg->opts = opts;
6180
6181 /* dma_mem holds the non-embedded portion */
6182 if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, sizeof(sli4_res_common_get_reconfig_link_info_t), 4)) {
6183 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
6184 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6185 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6186 return OCS_HW_RTN_NO_MEMORY;
6187 }
6188
6189 if (sli_cmd_common_get_reconfig_link_info(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->dma_cmd)) {
6190 rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_get_active_link_config_cb, cb_arg);
6191 }
6192
6193 if (rc != OCS_HW_RTN_SUCCESS) {
6194 ocs_log_err(hw->os, "GET_RECONFIG_LINK_INFO failed\n");
6195 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6196 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6197 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6198 } else if (opts == OCS_CMD_POLL) {
6199 /* if we're polling we have to call the callback here. */
6200 ocs_hw_get_active_link_config_cb(hw, 0, mbxdata, cb_arg);
6201 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6202 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6203 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
6204 } else {
6205 /* We weren't poling, so the callback got called */
6206 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6207 }
6208
6209 return rc;
6210 }
6211
6212 /**
6213 * @brief Sets the DIF seed value.
6214 *
6215 * @param hw Hardware context.
6216 *
6217 * @return Returns OCS_HW_RTN_SUCCESS on success.
6218 */
6219 static ocs_hw_rtn_e
ocs_hw_set_dif_seed(ocs_hw_t * hw)6220 ocs_hw_set_dif_seed(ocs_hw_t *hw)
6221 {
6222 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6223 uint8_t buf[SLI4_BMBX_SIZE];
6224 sli4_req_common_set_features_dif_seed_t seed_param;
6225
6226 ocs_memset(&seed_param, 0, sizeof(seed_param));
6227 seed_param.seed = hw->config.dif_seed;
6228
6229 /* send set_features command */
6230 if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6231 SLI4_SET_FEATURES_DIF_SEED,
6232 4,
6233 (uint32_t*)&seed_param)) {
6234 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6235 if (rc) {
6236 ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6237 } else {
6238 ocs_log_debug(hw->os, "DIF seed set to 0x%x\n",
6239 hw->config.dif_seed);
6240 }
6241 } else {
6242 ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6243 rc = OCS_HW_RTN_ERROR;
6244 }
6245 return rc;
6246 }
6247
6248
6249 /**
6250 * @brief Sets the DIF mode value.
6251 *
6252 * @param hw Hardware context.
6253 *
6254 * @return Returns OCS_HW_RTN_SUCCESS on success.
6255 */
6256 static ocs_hw_rtn_e
ocs_hw_set_dif_mode(ocs_hw_t * hw)6257 ocs_hw_set_dif_mode(ocs_hw_t *hw)
6258 {
6259 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6260 uint8_t buf[SLI4_BMBX_SIZE];
6261 sli4_req_common_set_features_t10_pi_mem_model_t mode_param;
6262
6263 ocs_memset(&mode_param, 0, sizeof(mode_param));
6264 mode_param.tmm = (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? 0 : 1);
6265
6266 /* send set_features command */
6267 if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6268 SLI4_SET_FEATURES_DIF_MEMORY_MODE,
6269 sizeof(mode_param),
6270 (uint32_t*)&mode_param)) {
6271 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6272 if (rc) {
6273 ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6274 } else {
6275 ocs_log_test(hw->os, "DIF mode set to %s\n",
6276 (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? "inline" : "separate"));
6277 }
6278 } else {
6279 ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
6280 rc = OCS_HW_RTN_ERROR;
6281 }
6282 return rc;
6283 }
6284
6285 static void
ocs_hw_watchdog_timer_cb(void * arg)6286 ocs_hw_watchdog_timer_cb(void *arg)
6287 {
6288 ocs_hw_t *hw = (ocs_hw_t *)arg;
6289
6290 ocs_hw_config_watchdog_timer(hw);
6291 return;
6292 }
6293
6294 static void
ocs_hw_cb_cfg_watchdog(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6295 ocs_hw_cb_cfg_watchdog(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
6296 {
6297 uint16_t timeout = hw->watchdog_timeout;
6298
6299 if (status != 0) {
6300 ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", status);
6301 } else {
6302 if(timeout != 0) {
6303 /* keeping callback 500ms before timeout to keep heartbeat alive */
6304 ocs_setup_timer(hw->os, &hw->watchdog_timer, ocs_hw_watchdog_timer_cb, hw, (timeout*1000 - 500) );
6305 }else {
6306 ocs_del_timer(&hw->watchdog_timer);
6307 }
6308 }
6309
6310 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6311 return;
6312 }
6313
6314 /**
6315 * @brief Set configuration parameters for watchdog timer feature.
6316 *
6317 * @param hw Hardware context.
6318 * @param timeout Timeout for watchdog timer in seconds
6319 *
6320 * @return Returns OCS_HW_RTN_SUCCESS on success.
6321 */
6322 static ocs_hw_rtn_e
ocs_hw_config_watchdog_timer(ocs_hw_t * hw)6323 ocs_hw_config_watchdog_timer(ocs_hw_t *hw)
6324 {
6325 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6326 uint8_t *buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
6327
6328 sli4_cmd_lowlevel_set_watchdog(&hw->sli, buf, SLI4_BMBX_SIZE, hw->watchdog_timeout);
6329 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_cfg_watchdog, NULL);
6330 if (rc) {
6331 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
6332 ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", rc);
6333 }
6334 return rc;
6335 }
6336
6337 /**
6338 * @brief Set configuration parameters for auto-generate xfer_rdy T10 PI feature.
6339 *
6340 * @param hw Hardware context.
6341 * @param buf Pointer to a mailbox buffer area.
6342 *
6343 * @return Returns OCS_HW_RTN_SUCCESS on success.
6344 */
6345 static ocs_hw_rtn_e
ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t * hw,uint8_t * buf)6346 ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf)
6347 {
6348 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6349 sli4_req_common_set_features_xfer_rdy_t10pi_t param;
6350
6351 ocs_memset(¶m, 0, sizeof(param));
6352 param.rtc = (hw->config.auto_xfer_rdy_ref_tag_is_lba ? 0 : 1);
6353 param.atv = (hw->config.auto_xfer_rdy_app_tag_valid ? 1 : 0);
6354 param.tmm = ((hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE) ? 0 : 1);
6355 param.app_tag = hw->config.auto_xfer_rdy_app_tag_value;
6356 param.blk_size = hw->config.auto_xfer_rdy_blk_size_chip;
6357
6358 switch (hw->config.auto_xfer_rdy_p_type) {
6359 case 1:
6360 param.p_type = 0;
6361 break;
6362 case 3:
6363 param.p_type = 2;
6364 break;
6365 default:
6366 ocs_log_err(hw->os, "unsupported p_type %d\n",
6367 hw->config.auto_xfer_rdy_p_type);
6368 return OCS_HW_RTN_ERROR;
6369 }
6370
6371 /* build the set_features command */
6372 sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6373 SLI4_SET_FEATURES_SET_CONFIG_AUTO_XFER_RDY_T10PI,
6374 sizeof(param),
6375 ¶m);
6376
6377
6378 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6379 if (rc) {
6380 ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6381 } else {
6382 ocs_log_test(hw->os, "Auto XFER RDY T10 PI configured rtc:%d atv:%d p_type:%d app_tag:%x blk_size:%d\n",
6383 param.rtc, param.atv, param.p_type,
6384 param.app_tag, param.blk_size);
6385 }
6386
6387 return rc;
6388 }
6389
6390
6391 /**
6392 * @brief enable sli port health check
6393 *
6394 * @param hw Hardware context.
6395 * @param buf Pointer to a mailbox buffer area.
6396 * @param query current status of the health check feature enabled/disabled
6397 * @param enable if 1: enable 0: disable
6398 * @param buf Pointer to a mailbox buffer area.
6399 *
6400 * @return Returns OCS_HW_RTN_SUCCESS on success.
6401 */
6402 static ocs_hw_rtn_e
ocs_hw_config_sli_port_health_check(ocs_hw_t * hw,uint8_t query,uint8_t enable)6403 ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable)
6404 {
6405 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6406 uint8_t buf[SLI4_BMBX_SIZE];
6407 sli4_req_common_set_features_health_check_t param;
6408
6409 ocs_memset(¶m, 0, sizeof(param));
6410 param.hck = enable;
6411 param.qry = query;
6412
6413 /* build the set_features command */
6414 sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6415 SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK,
6416 sizeof(param),
6417 ¶m);
6418
6419 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6420 if (rc) {
6421 ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
6422 } else {
6423 ocs_log_test(hw->os, "SLI Port Health Check is enabled \n");
6424 }
6425
6426 return rc;
6427 }
6428
6429 /**
6430 * @brief Set FTD transfer hint feature
6431 *
6432 * @param hw Hardware context.
6433 * @param fdt_xfer_hint size in bytes where read requests are segmented.
6434 *
6435 * @return Returns OCS_HW_RTN_SUCCESS on success.
6436 */
6437 static ocs_hw_rtn_e
ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t * hw,uint32_t fdt_xfer_hint)6438 ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint)
6439 {
6440 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6441 uint8_t buf[SLI4_BMBX_SIZE];
6442 sli4_req_common_set_features_set_fdt_xfer_hint_t param;
6443
6444 ocs_memset(¶m, 0, sizeof(param));
6445 param.fdt_xfer_hint = fdt_xfer_hint;
6446 /* build the set_features command */
6447 sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
6448 SLI4_SET_FEATURES_SET_FTD_XFER_HINT,
6449 sizeof(param),
6450 ¶m);
6451
6452
6453 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
6454 if (rc) {
6455 ocs_log_warn(hw->os, "set FDT hint %d failed: %d\n", fdt_xfer_hint, rc);
6456 } else {
6457 ocs_log_debug(hw->os, "Set FTD transfer hint to %d\n", param.fdt_xfer_hint);
6458 }
6459
6460 return rc;
6461 }
6462
6463 /**
6464 * @brief Get the link configuration callback.
6465 *
6466 * @param hw Hardware context.
6467 * @param status Status from the DMTF CLP command.
6468 * @param result_len Length, in bytes, of the DMTF CLP result.
6469 * @param arg Pointer to a callback argument.
6470 *
6471 * @return Returns OCS_HW_RTN_SUCCESS on success.
6472 */
6473 static void
ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t * hw,int32_t status,uint32_t result_len,void * arg)6474 ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg)
6475 {
6476 int32_t rval;
6477 char retdata_str[64];
6478 ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
6479 ocs_hw_linkcfg_e linkcfg = OCS_HW_LINKCFG_NA;
6480
6481 if (status) {
6482 ocs_log_test(hw->os, "CLP cmd failed, status=%d\n", status);
6483 } else {
6484 /* parse CLP response to get return data */
6485 rval = ocs_hw_clp_resp_get_value(hw, "retdata", retdata_str,
6486 sizeof(retdata_str),
6487 cb_arg->dma_resp.virt,
6488 result_len);
6489
6490 if (rval <= 0) {
6491 ocs_log_err(hw->os, "failed to get retdata %d\n", result_len);
6492 } else {
6493 /* translate string into hw enum */
6494 linkcfg = ocs_hw_linkcfg_from_clp(retdata_str);
6495 }
6496 }
6497
6498 /* invoke callback */
6499 if (cb_arg->cb) {
6500 cb_arg->cb(status, linkcfg, cb_arg->arg);
6501 }
6502
6503 /* if polling, will free memory in calling function */
6504 if (cb_arg->opts != OCS_CMD_POLL) {
6505 ocs_dma_free(hw->os, &cb_arg->dma_cmd);
6506 ocs_dma_free(hw->os, &cb_arg->dma_resp);
6507 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6508 }
6509 }
6510
6511 /**
6512 * @brief Set the Lancer dump location
6513 * @par Description
6514 * This function tells a Lancer chip to use a specific DMA
6515 * buffer as a dump location rather than the internal flash.
6516 *
6517 * @param hw Hardware context.
6518 * @param num_buffers The number of DMA buffers to hold the dump (1..n).
6519 * @param dump_buffers DMA buffers to hold the dump.
6520 *
6521 * @return Returns OCS_HW_RTN_SUCCESS on success.
6522 */
6523 ocs_hw_rtn_e
ocs_hw_set_dump_location(ocs_hw_t * hw,uint32_t num_buffers,ocs_dma_t * dump_buffers,uint8_t fdb)6524 ocs_hw_set_dump_location(ocs_hw_t *hw, uint32_t num_buffers, ocs_dma_t *dump_buffers, uint8_t fdb)
6525 {
6526 uint8_t bus, dev, func;
6527 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6528 uint8_t buf[SLI4_BMBX_SIZE];
6529
6530 /*
6531 * Make sure the FW is new enough to support this command. If the FW
6532 * is too old, the FW will UE.
6533 */
6534 if (hw->workaround.disable_dump_loc) {
6535 ocs_log_test(hw->os, "FW version is too old for this feature\n");
6536 return OCS_HW_RTN_ERROR;
6537 }
6538
6539 /* This command is only valid for physical port 0 */
6540 ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
6541 if (fdb == 0 && func != 0) {
6542 ocs_log_test(hw->os, "function only valid for pci function 0, %d passed\n",
6543 func);
6544 return OCS_HW_RTN_ERROR;
6545 }
6546
6547 /*
6548 * If a single buffer is used, then it may be passed as is to the chip. For multiple buffers,
6549 * We must allocate a SGL list and then pass the address of the list to the chip.
6550 */
6551 if (num_buffers > 1) {
6552 uint32_t sge_size = num_buffers * sizeof(sli4_sge_t);
6553 sli4_sge_t *sge;
6554 uint32_t i;
6555
6556 if (hw->dump_sges.size < sge_size) {
6557 ocs_dma_free(hw->os, &hw->dump_sges);
6558 if (ocs_dma_alloc(hw->os, &hw->dump_sges, sge_size, OCS_MIN_DMA_ALIGNMENT)) {
6559 ocs_log_err(hw->os, "SGE DMA allocation failed\n");
6560 return OCS_HW_RTN_NO_MEMORY;
6561 }
6562 }
6563 /* build the SGE list */
6564 ocs_memset(hw->dump_sges.virt, 0, hw->dump_sges.size);
6565 hw->dump_sges.len = sge_size;
6566 sge = hw->dump_sges.virt;
6567 for (i = 0; i < num_buffers; i++) {
6568 sge[i].buffer_address_high = ocs_addr32_hi(dump_buffers[i].phys);
6569 sge[i].buffer_address_low = ocs_addr32_lo(dump_buffers[i].phys);
6570 sge[i].last = (i == num_buffers - 1 ? 1 : 0);
6571 sge[i].buffer_length = dump_buffers[i].size;
6572 }
6573 rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6574 SLI4_BMBX_SIZE, FALSE, TRUE,
6575 &hw->dump_sges, fdb);
6576 } else {
6577 dump_buffers->len = dump_buffers->size;
6578 rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
6579 SLI4_BMBX_SIZE, FALSE, FALSE,
6580 dump_buffers, fdb);
6581 }
6582
6583 if (rc) {
6584 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL,
6585 NULL, NULL);
6586 if (rc) {
6587 ocs_log_err(hw->os, "ocs_hw_command returns %d\n",
6588 rc);
6589 }
6590 } else {
6591 ocs_log_err(hw->os,
6592 "sli_cmd_common_set_dump_location failed\n");
6593 rc = OCS_HW_RTN_ERROR;
6594 }
6595
6596 return rc;
6597 }
6598
6599
6600 /**
6601 * @brief Set the Ethernet license.
6602 *
6603 * @par Description
6604 * This function sends the appropriate mailbox command (DMTF
6605 * CLP) to set the Ethernet license to the given license value.
6606 * Since it is used during the time of ocs_hw_init(), the mailbox
6607 * command is sent via polling (the BMBX route).
6608 *
6609 * @param hw Hardware context.
6610 * @param license 32-bit license value.
6611 *
6612 * @return Returns OCS_HW_RTN_SUCCESS on success.
6613 */
6614 static ocs_hw_rtn_e
ocs_hw_set_eth_license(ocs_hw_t * hw,uint32_t license)6615 ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license)
6616 {
6617 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6618 char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
6619 ocs_dma_t dma_cmd;
6620 ocs_dma_t dma_resp;
6621
6622 /* only for lancer right now */
6623 if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6624 ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6625 return OCS_HW_RTN_ERROR;
6626 }
6627
6628 ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_Ethernet_License=%X", license);
6629 /* allocate DMA for command */
6630 if (ocs_dma_alloc(hw->os, &dma_cmd, ocs_strlen(cmd)+1, 4096)) {
6631 ocs_log_err(hw->os, "malloc failed\n");
6632 return OCS_HW_RTN_NO_MEMORY;
6633 }
6634 ocs_memset(dma_cmd.virt, 0, ocs_strlen(cmd)+1);
6635 ocs_memcpy(dma_cmd.virt, cmd, ocs_strlen(cmd));
6636
6637 /* allocate DMA for response */
6638 if (ocs_dma_alloc(hw->os, &dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
6639 ocs_log_err(hw->os, "malloc failed\n");
6640 ocs_dma_free(hw->os, &dma_cmd);
6641 return OCS_HW_RTN_NO_MEMORY;
6642 }
6643
6644 /* send DMTF CLP command mbx and poll */
6645 if (ocs_hw_exec_dmtf_clp_cmd(hw, &dma_cmd, &dma_resp, OCS_CMD_POLL, NULL, NULL)) {
6646 ocs_log_err(hw->os, "CLP cmd=\"%s\" failed\n", (char *)dma_cmd.virt);
6647 rc = OCS_HW_RTN_ERROR;
6648 }
6649
6650 ocs_dma_free(hw->os, &dma_cmd);
6651 ocs_dma_free(hw->os, &dma_resp);
6652 return rc;
6653 }
6654
6655 /**
6656 * @brief Callback argument structure for the DMTF CLP commands.
6657 */
6658 typedef struct ocs_hw_clp_cb_arg_s {
6659 ocs_hw_dmtf_clp_cb_t cb;
6660 ocs_dma_t *dma_resp;
6661 int32_t status;
6662 uint32_t opts;
6663 void *arg;
6664 } ocs_hw_clp_cb_arg_t;
6665
6666 /**
6667 * @brief Execute the DMTF CLP command.
6668 *
6669 * @param hw Hardware context.
6670 * @param dma_cmd DMA buffer containing the CLP command.
6671 * @param dma_resp DMA buffer that will contain the response (if successful).
6672 * @param opts Mailbox command options (such as OCS_CMD_NOWAIT and POLL).
6673 * @param cb Callback function.
6674 * @param arg Callback argument.
6675 *
6676 * @return Returns the number of bytes written to the response
6677 * buffer on success, or a negative value if failed.
6678 */
6679 static ocs_hw_rtn_e
ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t * hw,ocs_dma_t * dma_cmd,ocs_dma_t * dma_resp,uint32_t opts,ocs_hw_dmtf_clp_cb_t cb,void * arg)6680 ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg)
6681 {
6682 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6683 ocs_hw_clp_cb_arg_t *cb_arg;
6684 uint8_t *mbxdata;
6685
6686 /* allocate DMA for mailbox */
6687 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
6688 if (mbxdata == NULL) {
6689 ocs_log_err(hw->os, "failed to malloc mbox\n");
6690 return OCS_HW_RTN_NO_MEMORY;
6691 }
6692
6693 /* allocate memory for callback argument */
6694 cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
6695 if (cb_arg == NULL) {
6696 ocs_log_err(hw->os, "failed to malloc cb_arg");
6697 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6698 return OCS_HW_RTN_NO_MEMORY;
6699 }
6700
6701 cb_arg->cb = cb;
6702 cb_arg->arg = arg;
6703 cb_arg->dma_resp = dma_resp;
6704 cb_arg->opts = opts;
6705
6706 /* Send the HW command */
6707 if (sli_cmd_dmtf_exec_clp_cmd(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
6708 dma_cmd, dma_resp)) {
6709 rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_dmtf_clp_cb, cb_arg);
6710
6711 if (opts == OCS_CMD_POLL && rc == OCS_HW_RTN_SUCCESS) {
6712 /* if we're polling, copy response and invoke callback to
6713 * parse result */
6714 ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
6715 ocs_hw_dmtf_clp_cb(hw, 0, mbxdata, cb_arg);
6716
6717 /* set rc to resulting or "parsed" status */
6718 rc = cb_arg->status;
6719 }
6720
6721 /* if failed, or polling, free memory here */
6722 if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
6723 if (rc != OCS_HW_RTN_SUCCESS) {
6724 ocs_log_test(hw->os, "ocs_hw_command failed\n");
6725 }
6726 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6727 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6728 }
6729 } else {
6730 ocs_log_test(hw->os, "sli_cmd_dmtf_exec_clp_cmd failed\n");
6731 rc = OCS_HW_RTN_ERROR;
6732 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
6733 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6734 }
6735
6736 return rc;
6737 }
6738
6739
6740 /**
6741 * @brief Called when the DMTF CLP command completes.
6742 *
6743 * @param hw Hardware context.
6744 * @param status Status field from the mbox completion.
6745 * @param mqe Mailbox response structure.
6746 * @param arg Pointer to a callback argument.
6747 *
6748 * @return None.
6749 *
6750 */
6751 static void
ocs_hw_dmtf_clp_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6752 ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
6753 {
6754 int32_t cb_status = 0;
6755 sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6756 sli4_res_dmtf_exec_clp_cmd_t *clp_rsp = (sli4_res_dmtf_exec_clp_cmd_t *) mbox_rsp->payload.embed;
6757 ocs_hw_clp_cb_arg_t *cb_arg = arg;
6758 uint32_t result_len = 0;
6759 int32_t stat_len;
6760 char stat_str[8];
6761
6762 /* there are several status codes here, check them all and condense
6763 * into a single callback status
6764 */
6765 if (status || mbox_rsp->hdr.status || clp_rsp->clp_status) {
6766 ocs_log_debug(hw->os, "status=x%x/x%x/x%x addl=x%x clp=x%x detail=x%x\n",
6767 status,
6768 mbox_rsp->hdr.status,
6769 clp_rsp->hdr.status,
6770 clp_rsp->hdr.additional_status,
6771 clp_rsp->clp_status,
6772 clp_rsp->clp_detailed_status);
6773 if (status) {
6774 cb_status = status;
6775 } else if (mbox_rsp->hdr.status) {
6776 cb_status = mbox_rsp->hdr.status;
6777 } else {
6778 cb_status = clp_rsp->clp_status;
6779 }
6780 } else {
6781 result_len = clp_rsp->resp_length;
6782 }
6783
6784 if (cb_status) {
6785 goto ocs_hw_cb_dmtf_clp_done;
6786 }
6787
6788 if ((result_len == 0) || (cb_arg->dma_resp->size < result_len)) {
6789 ocs_log_test(hw->os, "Invalid response length: resp_len=%zu result len=%d\n",
6790 cb_arg->dma_resp->size, result_len);
6791 cb_status = -1;
6792 goto ocs_hw_cb_dmtf_clp_done;
6793 }
6794
6795 /* parse CLP response to get status */
6796 stat_len = ocs_hw_clp_resp_get_value(hw, "status", stat_str,
6797 sizeof(stat_str),
6798 cb_arg->dma_resp->virt,
6799 result_len);
6800
6801 if (stat_len <= 0) {
6802 ocs_log_test(hw->os, "failed to get status %d\n", stat_len);
6803 cb_status = -1;
6804 goto ocs_hw_cb_dmtf_clp_done;
6805 }
6806
6807 if (ocs_strcmp(stat_str, "0") != 0) {
6808 ocs_log_test(hw->os, "CLP status indicates failure=%s\n", stat_str);
6809 cb_status = -1;
6810 goto ocs_hw_cb_dmtf_clp_done;
6811 }
6812
6813 ocs_hw_cb_dmtf_clp_done:
6814
6815 /* save status in cb_arg for callers with NULL cb's + polling */
6816 cb_arg->status = cb_status;
6817 if (cb_arg->cb) {
6818 cb_arg->cb(hw, cb_status, result_len, cb_arg->arg);
6819 }
6820 /* if polling, caller will free memory */
6821 if (cb_arg->opts != OCS_CMD_POLL) {
6822 ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
6823 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
6824 }
6825 }
6826
6827 /**
6828 * @brief Parse the CLP result and get the value corresponding to the given
6829 * keyword.
6830 *
6831 * @param hw Hardware context.
6832 * @param keyword CLP keyword for which the value is returned.
6833 * @param value Location to which the resulting value is copied.
6834 * @param value_len Length of the value parameter.
6835 * @param resp Pointer to the response buffer that is searched
6836 * for the keyword and value.
6837 * @param resp_len Length of response buffer passed in.
6838 *
6839 * @return Returns the number of bytes written to the value
6840 * buffer on success, or a negative vaue on failure.
6841 */
6842 static int32_t
ocs_hw_clp_resp_get_value(ocs_hw_t * hw,const char * keyword,char * value,uint32_t value_len,const char * resp,uint32_t resp_len)6843 ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len)
6844 {
6845 char *start = NULL;
6846 char *end = NULL;
6847
6848 /* look for specified keyword in string */
6849 start = ocs_strstr(resp, keyword);
6850 if (start == NULL) {
6851 ocs_log_test(hw->os, "could not find keyword=%s in CLP response\n",
6852 keyword);
6853 return -1;
6854 }
6855
6856 /* now look for '=' and go one past */
6857 start = ocs_strchr(start, '=');
6858 if (start == NULL) {
6859 ocs_log_test(hw->os, "could not find \'=\' in CLP response for keyword=%s\n",
6860 keyword);
6861 return -1;
6862 }
6863 start++;
6864
6865 /* \r\n terminates value */
6866 end = ocs_strstr(start, "\r\n");
6867 if (end == NULL) {
6868 ocs_log_test(hw->os, "could not find \\r\\n for keyword=%s in CLP response\n",
6869 keyword);
6870 return -1;
6871 }
6872
6873 /* make sure given result array is big enough */
6874 if ((end - start + 1) > value_len) {
6875 ocs_log_test(hw->os, "value len=%d not large enough for actual=%ld\n",
6876 value_len, (end-start));
6877 return -1;
6878 }
6879
6880 ocs_strncpy(value, start, (end - start));
6881 value[end-start] = '\0';
6882 return (end-start+1);
6883 }
6884
6885 /**
6886 * @brief Cause chip to enter an unrecoverable error state.
6887 *
6888 * @par Description
6889 * Cause chip to enter an unrecoverable error state. This is
6890 * used when detecting unexpected FW behavior so that the FW can be
6891 * hwted from the driver as soon as the error is detected.
6892 *
6893 * @param hw Hardware context.
6894 * @param dump Generate dump as part of reset.
6895 *
6896 * @return Returns 0 on success, or a non-zero value on failure.
6897 *
6898 */
6899 ocs_hw_rtn_e
ocs_hw_raise_ue(ocs_hw_t * hw,uint8_t dump)6900 ocs_hw_raise_ue(ocs_hw_t *hw, uint8_t dump)
6901 {
6902 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
6903
6904 if (sli_raise_ue(&hw->sli, dump) != 0) {
6905 rc = OCS_HW_RTN_ERROR;
6906 } else {
6907 if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
6908 hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
6909 }
6910 }
6911
6912 return rc;
6913 }
6914
6915 /**
6916 * @brief Called when the OBJECT_GET command completes.
6917 *
6918 * @par Description
6919 * Get the number of bytes actually written out of the response, free the mailbox
6920 * that was malloc'd by ocs_hw_dump_get(), then call the callback
6921 * and pass the status and bytes read.
6922 *
6923 * @param hw Hardware context.
6924 * @param status Status field from the mbox completion.
6925 * @param mqe Mailbox response structure.
6926 * @param arg Pointer to a callback function that signals the caller that the command is done.
6927 * The callback function prototype is <tt>void cb(int32_t status, uint32_t bytes_read)</tt>.
6928 *
6929 * @return Returns 0.
6930 */
6931 static int32_t
ocs_hw_cb_dump_get(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)6932 ocs_hw_cb_dump_get(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
6933 {
6934 sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
6935 sli4_res_common_read_object_t* rd_obj_rsp = (sli4_res_common_read_object_t*) mbox_rsp->payload.embed;
6936 ocs_hw_dump_get_cb_arg_t *cb_arg = arg;
6937 uint32_t bytes_read;
6938 uint8_t eof;
6939
6940 bytes_read = rd_obj_rsp->actual_read_length;
6941 eof = rd_obj_rsp->eof;
6942
6943 if (cb_arg) {
6944 if (cb_arg->cb) {
6945 if ((status == 0) && mbox_rsp->hdr.status) {
6946 status = mbox_rsp->hdr.status;
6947 }
6948 cb_arg->cb(status, bytes_read, eof, cb_arg->arg);
6949 }
6950
6951 ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
6952 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
6953 }
6954
6955 return 0;
6956 }
6957
6958
6959 /**
6960 * @brief Read a dump image to the host.
6961 *
6962 * @par Description
6963 * Creates a SLI_CONFIG mailbox command, fills in the correct values to read a
6964 * dump image chunk, then sends the command with the ocs_hw_command(). On completion,
6965 * the callback function ocs_hw_cb_dump_get() gets called to free the mailbox
6966 * and signal the caller that the read has completed.
6967 *
6968 * @param hw Hardware context.
6969 * @param dma DMA structure to transfer the dump chunk into.
6970 * @param size Size of the dump chunk.
6971 * @param offset Offset, in bytes, from the beginning of the dump.
6972 * @param cb Pointer to a callback function that is called when the command completes.
6973 * The callback function prototype is
6974 * <tt>void cb(int32_t status, uint32_t bytes_read, uint8_t eof, void *arg)</tt>.
6975 * @param arg Pointer to be passed to the callback function.
6976 *
6977 * @return Returns 0 on success, or a non-zero value on failure.
6978 */
6979 ocs_hw_rtn_e
ocs_hw_dump_get(ocs_hw_t * hw,ocs_dma_t * dma,uint32_t size,uint32_t offset,ocs_hw_dump_get_cb_t cb,void * arg)6980 ocs_hw_dump_get(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, ocs_hw_dump_get_cb_t cb, void *arg)
6981 {
6982 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
6983 uint8_t *mbxdata;
6984 ocs_hw_dump_get_cb_arg_t *cb_arg;
6985 uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
6986
6987 if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
6988 ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
6989 return OCS_HW_RTN_ERROR;
6990 }
6991
6992 if (1 != sli_dump_is_present(&hw->sli)) {
6993 ocs_log_test(hw->os, "No dump is present\n");
6994 return OCS_HW_RTN_ERROR;
6995 }
6996
6997 if (1 == sli_reset_required(&hw->sli)) {
6998 ocs_log_test(hw->os, "device reset required\n");
6999 return OCS_HW_RTN_ERROR;
7000 }
7001
7002 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7003 if (mbxdata == NULL) {
7004 ocs_log_err(hw->os, "failed to malloc mbox\n");
7005 return OCS_HW_RTN_NO_MEMORY;
7006 }
7007
7008 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_get_cb_arg_t), OCS_M_NOWAIT);
7009 if (cb_arg == NULL) {
7010 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7011 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7012 return OCS_HW_RTN_NO_MEMORY;
7013 }
7014
7015 cb_arg->cb = cb;
7016 cb_arg->arg = arg;
7017 cb_arg->mbox_cmd = mbxdata;
7018
7019 if (sli_cmd_common_read_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7020 size, offset, "/dbg/dump.bin", dma)) {
7021 rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_get, cb_arg);
7022 if (rc == 0 && opts == OCS_CMD_POLL) {
7023 ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7024 rc = ocs_hw_cb_dump_get(hw, 0, mbxdata, cb_arg);
7025 }
7026 }
7027
7028 if (rc != OCS_HW_RTN_SUCCESS) {
7029 ocs_log_test(hw->os, "COMMON_READ_OBJECT failed\n");
7030 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7031 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
7032 }
7033
7034 return rc;
7035 }
7036
7037 /**
7038 * @brief Called when the OBJECT_DELETE command completes.
7039 *
7040 * @par Description
7041 * Free the mailbox that was malloc'd
7042 * by ocs_hw_dump_clear(), then call the callback and pass the status.
7043 *
7044 * @param hw Hardware context.
7045 * @param status Status field from the mbox completion.
7046 * @param mqe Mailbox response structure.
7047 * @param arg Pointer to a callback function that signals the caller that the command is done.
7048 * The callback function prototype is <tt>void cb(int32_t status, void *arg)</tt>.
7049 *
7050 * @return Returns 0.
7051 */
7052 static int32_t
ocs_hw_cb_dump_clear(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7053 ocs_hw_cb_dump_clear(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7054 {
7055 ocs_hw_dump_clear_cb_arg_t *cb_arg = arg;
7056 sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7057
7058 if (cb_arg) {
7059 if (cb_arg->cb) {
7060 if ((status == 0) && mbox_rsp->hdr.status) {
7061 status = mbox_rsp->hdr.status;
7062 }
7063 cb_arg->cb(status, cb_arg->arg);
7064 }
7065
7066 ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
7067 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7068 }
7069
7070 return 0;
7071 }
7072
7073 /**
7074 * @brief Clear a dump image from the device.
7075 *
7076 * @par Description
7077 * Creates a SLI_CONFIG mailbox command, fills it with the correct values to clear
7078 * the dump, then sends the command with ocs_hw_command(). On completion,
7079 * the callback function ocs_hw_cb_dump_clear() gets called to free the mailbox
7080 * and to signal the caller that the write has completed.
7081 *
7082 * @param hw Hardware context.
7083 * @param cb Pointer to a callback function that is called when the command completes.
7084 * The callback function prototype is
7085 * <tt>void cb(int32_t status, uint32_t bytes_written, void *arg)</tt>.
7086 * @param arg Pointer to be passed to the callback function.
7087 *
7088 * @return Returns 0 on success, or a non-zero value on failure.
7089 */
7090 ocs_hw_rtn_e
ocs_hw_dump_clear(ocs_hw_t * hw,ocs_hw_dump_clear_cb_t cb,void * arg)7091 ocs_hw_dump_clear(ocs_hw_t *hw, ocs_hw_dump_clear_cb_t cb, void *arg)
7092 {
7093 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7094 uint8_t *mbxdata;
7095 ocs_hw_dump_clear_cb_arg_t *cb_arg;
7096 uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
7097
7098 if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
7099 ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
7100 return OCS_HW_RTN_ERROR;
7101 }
7102
7103 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7104 if (mbxdata == NULL) {
7105 ocs_log_err(hw->os, "failed to malloc mbox\n");
7106 return OCS_HW_RTN_NO_MEMORY;
7107 }
7108
7109 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_clear_cb_arg_t), OCS_M_NOWAIT);
7110 if (cb_arg == NULL) {
7111 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7112 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7113 return OCS_HW_RTN_NO_MEMORY;
7114 }
7115
7116 cb_arg->cb = cb;
7117 cb_arg->arg = arg;
7118 cb_arg->mbox_cmd = mbxdata;
7119
7120 if (sli_cmd_common_delete_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7121 "/dbg/dump.bin")) {
7122 rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_clear, cb_arg);
7123 if (rc == 0 && opts == OCS_CMD_POLL) {
7124 ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
7125 rc = ocs_hw_cb_dump_clear(hw, 0, mbxdata, cb_arg);
7126 }
7127 }
7128
7129 if (rc != OCS_HW_RTN_SUCCESS) {
7130 ocs_log_test(hw->os, "COMMON_DELETE_OBJECT failed\n");
7131 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7132 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
7133 }
7134
7135 return rc;
7136 }
7137
7138 typedef struct ocs_hw_get_port_protocol_cb_arg_s {
7139 ocs_get_port_protocol_cb_t cb;
7140 void *arg;
7141 uint32_t pci_func;
7142 ocs_dma_t payload;
7143 } ocs_hw_get_port_protocol_cb_arg_t;
7144
7145 /**
7146 * @brief Called for the completion of get_port_profile for a
7147 * user request.
7148 *
7149 * @param hw Hardware context.
7150 * @param status The status from the MQE.
7151 * @param mqe Pointer to mailbox command buffer.
7152 * @param arg Pointer to a callback argument.
7153 *
7154 * @return Returns 0 on success, or a non-zero value on failure.
7155 */
7156 static int32_t
ocs_hw_get_port_protocol_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7157 ocs_hw_get_port_protocol_cb(ocs_hw_t *hw, int32_t status,
7158 uint8_t *mqe, void *arg)
7159 {
7160 ocs_hw_get_port_protocol_cb_arg_t *cb_arg = arg;
7161 ocs_dma_t *payload = &(cb_arg->payload);
7162 sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7163 ocs_hw_port_protocol_e port_protocol;
7164 int num_descriptors;
7165 sli4_resource_descriptor_v1_t *desc_p;
7166 sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7167 int i;
7168
7169 port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7170
7171 num_descriptors = response->desc_count;
7172 desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7173 for (i=0; i<num_descriptors; i++) {
7174 if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7175 pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7176 if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7177 switch(pcie_desc_p->pf_type) {
7178 case 0x02:
7179 port_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
7180 break;
7181 case 0x04:
7182 port_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
7183 break;
7184 case 0x10:
7185 port_protocol = OCS_HW_PORT_PROTOCOL_FC;
7186 break;
7187 default:
7188 port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
7189 break;
7190 }
7191 }
7192 }
7193
7194 desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7195 }
7196
7197 if (cb_arg->cb) {
7198 cb_arg->cb(status, port_protocol, cb_arg->arg);
7199
7200 }
7201
7202 ocs_dma_free(hw->os, &cb_arg->payload);
7203 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7204 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7205
7206 return 0;
7207 }
7208
7209 /**
7210 * @ingroup io
7211 * @brief Get the current port protocol.
7212 * @par Description
7213 * Issues a SLI4 COMMON_GET_PROFILE_CONFIG mailbox. When the
7214 * command completes the provided mgmt callback function is
7215 * called.
7216 *
7217 * @param hw Hardware context.
7218 * @param pci_func PCI function to query for current protocol.
7219 * @param cb Callback function to be called when the command completes.
7220 * @param ul_arg An argument that is passed to the callback function.
7221 *
7222 * @return
7223 * - OCS_HW_RTN_SUCCESS on success.
7224 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7225 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7226 * context.
7227 * - OCS_HW_RTN_ERROR on any other error.
7228 */
7229 ocs_hw_rtn_e
ocs_hw_get_port_protocol(ocs_hw_t * hw,uint32_t pci_func,ocs_get_port_protocol_cb_t cb,void * ul_arg)7230 ocs_hw_get_port_protocol(ocs_hw_t *hw, uint32_t pci_func,
7231 ocs_get_port_protocol_cb_t cb, void* ul_arg)
7232 {
7233 uint8_t *mbxdata;
7234 ocs_hw_get_port_protocol_cb_arg_t *cb_arg;
7235 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7236
7237 /* Only supported on Skyhawk */
7238 if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7239 return OCS_HW_RTN_ERROR;
7240 }
7241
7242 /* mbxdata holds the header of the command */
7243 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7244 if (mbxdata == NULL) {
7245 ocs_log_err(hw->os, "failed to malloc mbox\n");
7246 return OCS_HW_RTN_NO_MEMORY;
7247 }
7248
7249
7250 /* cb_arg holds the data that will be passed to the callback on completion */
7251 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7252 if (cb_arg == NULL) {
7253 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7254 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7255 return OCS_HW_RTN_NO_MEMORY;
7256 }
7257
7258 cb_arg->cb = cb;
7259 cb_arg->arg = ul_arg;
7260 cb_arg->pci_func = pci_func;
7261
7262 /* dma_mem holds the non-embedded portion */
7263 if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7264 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7265 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7266 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7267 return OCS_HW_RTN_NO_MEMORY;
7268 }
7269
7270 if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7271 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_port_protocol_cb, cb_arg);
7272 }
7273
7274 if (rc != OCS_HW_RTN_SUCCESS) {
7275 ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7276 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7277 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7278 ocs_dma_free(hw->os, &cb_arg->payload);
7279 }
7280
7281 return rc;
7282
7283 }
7284
7285 typedef struct ocs_hw_set_port_protocol_cb_arg_s {
7286 ocs_set_port_protocol_cb_t cb;
7287 void *arg;
7288 ocs_dma_t payload;
7289 uint32_t new_protocol;
7290 uint32_t pci_func;
7291 } ocs_hw_set_port_protocol_cb_arg_t;
7292
7293 /**
7294 * @brief Called for the completion of set_port_profile for a
7295 * user request.
7296 *
7297 * @par Description
7298 * This is the second of two callbacks for the set_port_protocol
7299 * function. The set operation is a read-modify-write. This
7300 * callback is called when the write (SET_PROFILE_CONFIG)
7301 * completes.
7302 *
7303 * @param hw Hardware context.
7304 * @param status The status from the MQE.
7305 * @param mqe Pointer to mailbox command buffer.
7306 * @param arg Pointer to a callback argument.
7307 *
7308 * @return 0 on success, non-zero otherwise
7309 */
7310 static int32_t
ocs_hw_set_port_protocol_cb2(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7311 ocs_hw_set_port_protocol_cb2(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7312 {
7313 ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7314
7315 if (cb_arg->cb) {
7316 cb_arg->cb( status, cb_arg->arg);
7317 }
7318
7319 ocs_dma_free(hw->os, &(cb_arg->payload));
7320 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7321 ocs_free(hw->os, arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7322
7323 return 0;
7324 }
7325
7326 /**
7327 * @brief Called for the completion of set_port_profile for a
7328 * user request.
7329 *
7330 * @par Description
7331 * This is the first of two callbacks for the set_port_protocol
7332 * function. The set operation is a read-modify-write. This
7333 * callback is called when the read completes
7334 * (GET_PROFILE_CONFG). It will updated the resource
7335 * descriptors, then queue the write (SET_PROFILE_CONFIG).
7336 *
7337 * On entry there are three memory areas that were allocated by
7338 * ocs_hw_set_port_protocol. If a failure is detected in this
7339 * function those need to be freed. If this function succeeds
7340 * it allocates three more areas.
7341 *
7342 * @param hw Hardware context.
7343 * @param status The status from the MQE
7344 * @param mqe Pointer to mailbox command buffer.
7345 * @param arg Pointer to a callback argument.
7346 *
7347 * @return Returns 0 on success, or a non-zero value otherwise.
7348 */
7349 static int32_t
ocs_hw_set_port_protocol_cb1(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7350 ocs_hw_set_port_protocol_cb1(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7351 {
7352 ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
7353 ocs_dma_t *payload = &(cb_arg->payload);
7354 sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
7355 int num_descriptors;
7356 sli4_resource_descriptor_v1_t *desc_p;
7357 sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
7358 int i;
7359 ocs_hw_set_port_protocol_cb_arg_t *new_cb_arg;
7360 ocs_hw_port_protocol_e new_protocol;
7361 uint8_t *dst;
7362 sli4_isap_resouce_descriptor_v1_t *isap_desc_p;
7363 uint8_t *mbxdata;
7364 int pci_descriptor_count;
7365 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7366 int num_fcoe_ports = 0;
7367 int num_iscsi_ports = 0;
7368
7369 new_protocol = (ocs_hw_port_protocol_e)cb_arg->new_protocol;
7370
7371 num_descriptors = response->desc_count;
7372
7373 /* Count PCI descriptors */
7374 pci_descriptor_count = 0;
7375 desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7376 for (i=0; i<num_descriptors; i++) {
7377 if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7378 ++pci_descriptor_count;
7379 }
7380 desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7381 }
7382
7383 /* mbxdata holds the header of the command */
7384 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7385 if (mbxdata == NULL) {
7386 ocs_log_err(hw->os, "failed to malloc mbox\n");
7387 return OCS_HW_RTN_NO_MEMORY;
7388 }
7389
7390
7391 /* cb_arg holds the data that will be passed to the callback on completion */
7392 new_cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7393 if (new_cb_arg == NULL) {
7394 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7395 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7396 return OCS_HW_RTN_NO_MEMORY;
7397 }
7398
7399 new_cb_arg->cb = cb_arg->cb;
7400 new_cb_arg->arg = cb_arg->arg;
7401
7402 /* Allocate memory for the descriptors we're going to send. This is
7403 * one for each PCI descriptor plus one ISAP descriptor. */
7404 if (ocs_dma_alloc(hw->os, &new_cb_arg->payload, sizeof(sli4_req_common_set_profile_config_t) +
7405 (pci_descriptor_count * sizeof(sli4_pcie_resource_descriptor_v1_t)) +
7406 sizeof(sli4_isap_resouce_descriptor_v1_t), 4)) {
7407 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7408 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7409 ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7410 return OCS_HW_RTN_NO_MEMORY;
7411 }
7412
7413 sli_cmd_common_set_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
7414 &new_cb_arg->payload,
7415 0, pci_descriptor_count+1, 1);
7416
7417 /* Point dst to the first descriptor entry in the SET_PROFILE_CONFIG command */
7418 dst = (uint8_t *)&(((sli4_req_common_set_profile_config_t *) new_cb_arg->payload.virt)->desc);
7419
7420 /* Loop over all descriptors. If the descriptor is a PCIe descriptor, copy it
7421 * to the SET_PROFILE_CONFIG command to be written back. If it's the descriptor
7422 * that we're trying to change also set its pf_type.
7423 */
7424 desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
7425 for (i=0; i<num_descriptors; i++) {
7426 if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
7427 pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
7428 if (pcie_desc_p->pf_number == cb_arg->pci_func) {
7429 /* This is the PCIe descriptor for this OCS instance.
7430 * Update it with the new pf_type */
7431 switch(new_protocol) {
7432 case OCS_HW_PORT_PROTOCOL_FC:
7433 pcie_desc_p->pf_type = SLI4_PROTOCOL_FC;
7434 break;
7435 case OCS_HW_PORT_PROTOCOL_FCOE:
7436 pcie_desc_p->pf_type = SLI4_PROTOCOL_FCOE;
7437 break;
7438 case OCS_HW_PORT_PROTOCOL_ISCSI:
7439 pcie_desc_p->pf_type = SLI4_PROTOCOL_ISCSI;
7440 break;
7441 default:
7442 pcie_desc_p->pf_type = SLI4_PROTOCOL_DEFAULT;
7443 break;
7444 }
7445
7446 }
7447
7448 if (pcie_desc_p->pf_type == SLI4_PROTOCOL_FCOE) {
7449 ++num_fcoe_ports;
7450 }
7451 if (pcie_desc_p->pf_type == SLI4_PROTOCOL_ISCSI) {
7452 ++num_iscsi_ports;
7453 }
7454 ocs_memcpy(dst, pcie_desc_p, sizeof(sli4_pcie_resource_descriptor_v1_t));
7455 dst += sizeof(sli4_pcie_resource_descriptor_v1_t);
7456 }
7457
7458 desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
7459 }
7460
7461 /* Create an ISAP resource descriptor */
7462 isap_desc_p = (sli4_isap_resouce_descriptor_v1_t*)dst;
7463 isap_desc_p->descriptor_type = SLI4_RESOURCE_DESCRIPTOR_TYPE_ISAP;
7464 isap_desc_p->descriptor_length = sizeof(sli4_isap_resouce_descriptor_v1_t);
7465 if (num_iscsi_ports > 0) {
7466 isap_desc_p->iscsi_tgt = 1;
7467 isap_desc_p->iscsi_ini = 1;
7468 isap_desc_p->iscsi_dif = 1;
7469 }
7470 if (num_fcoe_ports > 0) {
7471 isap_desc_p->fcoe_tgt = 1;
7472 isap_desc_p->fcoe_ini = 1;
7473 isap_desc_p->fcoe_dif = 1;
7474 }
7475
7476 /* At this point we're done with the memory allocated by ocs_port_set_protocol */
7477 ocs_dma_free(hw->os, &cb_arg->payload);
7478 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7479 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7480
7481
7482 /* Send a SET_PROFILE_CONFIG mailbox command with the new descriptors */
7483 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb2, new_cb_arg);
7484 if (rc) {
7485 ocs_log_err(hw->os, "Error posting COMMON_SET_PROFILE_CONFIG\n");
7486 /* Call the upper level callback to report a failure */
7487 if (new_cb_arg->cb) {
7488 new_cb_arg->cb( rc, new_cb_arg->arg);
7489 }
7490
7491 /* Free the memory allocated by this function */
7492 ocs_dma_free(hw->os, &new_cb_arg->payload);
7493 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7494 ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
7495 }
7496
7497
7498 return rc;
7499 }
7500
7501 /**
7502 * @ingroup io
7503 * @brief Set the port protocol.
7504 * @par Description
7505 * Setting the port protocol is a read-modify-write operation.
7506 * This function submits a GET_PROFILE_CONFIG command to read
7507 * the current settings. The callback function will modify the
7508 * settings and issue the write.
7509 *
7510 * On successful completion this function will have allocated
7511 * two regular memory areas and one dma area which will need to
7512 * get freed later in the callbacks.
7513 *
7514 * @param hw Hardware context.
7515 * @param new_protocol New protocol to use.
7516 * @param pci_func PCI function to configure.
7517 * @param cb Callback function to be called when the command completes.
7518 * @param ul_arg An argument that is passed to the callback function.
7519 *
7520 * @return
7521 * - OCS_HW_RTN_SUCCESS on success.
7522 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7523 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7524 * context.
7525 * - OCS_HW_RTN_ERROR on any other error.
7526 */
7527 ocs_hw_rtn_e
ocs_hw_set_port_protocol(ocs_hw_t * hw,ocs_hw_port_protocol_e new_protocol,uint32_t pci_func,ocs_set_port_protocol_cb_t cb,void * ul_arg)7528 ocs_hw_set_port_protocol(ocs_hw_t *hw, ocs_hw_port_protocol_e new_protocol,
7529 uint32_t pci_func, ocs_set_port_protocol_cb_t cb, void *ul_arg)
7530 {
7531 uint8_t *mbxdata;
7532 ocs_hw_set_port_protocol_cb_arg_t *cb_arg;
7533 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
7534
7535 /* Only supported on Skyhawk */
7536 if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7537 return OCS_HW_RTN_ERROR;
7538 }
7539
7540 /* mbxdata holds the header of the command */
7541 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7542 if (mbxdata == NULL) {
7543 ocs_log_err(hw->os, "failed to malloc mbox\n");
7544 return OCS_HW_RTN_NO_MEMORY;
7545 }
7546
7547
7548 /* cb_arg holds the data that will be passed to the callback on completion */
7549 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
7550 if (cb_arg == NULL) {
7551 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7552 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7553 return OCS_HW_RTN_NO_MEMORY;
7554 }
7555
7556 cb_arg->cb = cb;
7557 cb_arg->arg = ul_arg;
7558 cb_arg->new_protocol = new_protocol;
7559 cb_arg->pci_func = pci_func;
7560
7561 /* dma_mem holds the non-embedded portion */
7562 if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
7563 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7564 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7565 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
7566 return OCS_HW_RTN_NO_MEMORY;
7567 }
7568
7569 if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
7570 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb1, cb_arg);
7571 }
7572
7573 if (rc != OCS_HW_RTN_SUCCESS) {
7574 ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
7575 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7576 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
7577 ocs_dma_free(hw->os, &cb_arg->payload);
7578 }
7579
7580 return rc;
7581 }
7582
7583 typedef struct ocs_hw_get_profile_list_cb_arg_s {
7584 ocs_get_profile_list_cb_t cb;
7585 void *arg;
7586 ocs_dma_t payload;
7587 } ocs_hw_get_profile_list_cb_arg_t;
7588
7589 /**
7590 * @brief Called for the completion of get_profile_list for a
7591 * user request.
7592 * @par Description
7593 * This function is called when the COMMMON_GET_PROFILE_LIST
7594 * mailbox completes. The response will be in
7595 * ctx->non_embedded_mem.virt. This function parses the
7596 * response and creates a ocs_hw_profile_list, then calls the
7597 * mgmt_cb callback function and passes that list to it.
7598 *
7599 * @param hw Hardware context.
7600 * @param status The status from the MQE
7601 * @param mqe Pointer to mailbox command buffer.
7602 * @param arg Pointer to a callback argument.
7603 *
7604 * @return Returns 0 on success, or a non-zero value on failure.
7605 */
7606 static int32_t
ocs_hw_get_profile_list_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7607 ocs_hw_get_profile_list_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7608 {
7609 ocs_hw_profile_list_t *list;
7610 ocs_hw_get_profile_list_cb_arg_t *cb_arg = arg;
7611 ocs_dma_t *payload = &(cb_arg->payload);
7612 sli4_res_common_get_profile_list_t *response = (sli4_res_common_get_profile_list_t *)payload->virt;
7613 int i;
7614 int num_descriptors;
7615
7616 list = ocs_malloc(hw->os, sizeof(ocs_hw_profile_list_t), OCS_M_ZERO);
7617 if (list == NULL) {
7618 ocs_log_err(hw->os, "failed to malloc list\n");
7619 return OCS_HW_RTN_NO_MEMORY;
7620 }
7621
7622 list->num_descriptors = response->profile_descriptor_count;
7623
7624 num_descriptors = list->num_descriptors;
7625 if (num_descriptors > OCS_HW_MAX_PROFILES) {
7626 num_descriptors = OCS_HW_MAX_PROFILES;
7627 }
7628
7629 for (i=0; i<num_descriptors; i++) {
7630 list->descriptors[i].profile_id = response->profile_descriptor[i].profile_id;
7631 list->descriptors[i].profile_index = response->profile_descriptor[i].profile_index;
7632 ocs_strcpy(list->descriptors[i].profile_description, (char *)response->profile_descriptor[i].profile_description);
7633 }
7634
7635 if (cb_arg->cb) {
7636 cb_arg->cb(status, list, cb_arg->arg);
7637 } else {
7638 ocs_free(hw->os, list, sizeof(*list));
7639 }
7640
7641 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7642 ocs_dma_free(hw->os, &cb_arg->payload);
7643 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7644
7645 return 0;
7646 }
7647
7648 /**
7649 * @ingroup io
7650 * @brief Get a list of available profiles.
7651 * @par Description
7652 * Issues a SLI-4 COMMON_GET_PROFILE_LIST mailbox. When the
7653 * command completes the provided mgmt callback function is
7654 * called.
7655 *
7656 * @param hw Hardware context.
7657 * @param cb Callback function to be called when the
7658 * command completes.
7659 * @param ul_arg An argument that is passed to the callback
7660 * function.
7661 *
7662 * @return
7663 * - OCS_HW_RTN_SUCCESS on success.
7664 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7665 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7666 * context.
7667 * - OCS_HW_RTN_ERROR on any other error.
7668 */
7669 ocs_hw_rtn_e
ocs_hw_get_profile_list(ocs_hw_t * hw,ocs_get_profile_list_cb_t cb,void * ul_arg)7670 ocs_hw_get_profile_list(ocs_hw_t *hw, ocs_get_profile_list_cb_t cb, void* ul_arg)
7671 {
7672 uint8_t *mbxdata;
7673 ocs_hw_get_profile_list_cb_arg_t *cb_arg;
7674 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7675
7676 /* Only supported on Skyhawk */
7677 if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7678 return OCS_HW_RTN_ERROR;
7679 }
7680
7681 /* mbxdata holds the header of the command */
7682 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7683 if (mbxdata == NULL) {
7684 ocs_log_err(hw->os, "failed to malloc mbox\n");
7685 return OCS_HW_RTN_NO_MEMORY;
7686 }
7687
7688
7689 /* cb_arg holds the data that will be passed to the callback on completion */
7690 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_profile_list_cb_arg_t), OCS_M_NOWAIT);
7691 if (cb_arg == NULL) {
7692 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7693 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7694 return OCS_HW_RTN_NO_MEMORY;
7695 }
7696
7697 cb_arg->cb = cb;
7698 cb_arg->arg = ul_arg;
7699
7700 /* dma_mem holds the non-embedded portion */
7701 if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_get_profile_list_t), 4)) {
7702 ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
7703 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7704 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7705 return OCS_HW_RTN_NO_MEMORY;
7706 }
7707
7708 if (sli_cmd_common_get_profile_list(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, &cb_arg->payload)) {
7709 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_profile_list_cb, cb_arg);
7710 }
7711
7712 if (rc != OCS_HW_RTN_SUCCESS) {
7713 ocs_log_test(hw->os, "GET_PROFILE_LIST failed\n");
7714 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7715 ocs_dma_free(hw->os, &cb_arg->payload);
7716 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
7717 }
7718
7719 return rc;
7720 }
7721
7722 typedef struct ocs_hw_get_active_profile_cb_arg_s {
7723 ocs_get_active_profile_cb_t cb;
7724 void *arg;
7725 } ocs_hw_get_active_profile_cb_arg_t;
7726
7727 /**
7728 * @brief Called for the completion of get_active_profile for a
7729 * user request.
7730 *
7731 * @param hw Hardware context.
7732 * @param status The status from the MQE
7733 * @param mqe Pointer to mailbox command buffer.
7734 * @param arg Pointer to a callback argument.
7735 *
7736 * @return Returns 0 on success, or a non-zero value on failure.
7737 */
7738 static int32_t
ocs_hw_get_active_profile_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7739 ocs_hw_get_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7740 {
7741 ocs_hw_get_active_profile_cb_arg_t *cb_arg = arg;
7742 sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
7743 sli4_res_common_get_active_profile_t* response = (sli4_res_common_get_active_profile_t*) mbox_rsp->payload.embed;
7744 uint32_t active_profile;
7745
7746 active_profile = response->active_profile_id;
7747
7748 if (cb_arg->cb) {
7749 cb_arg->cb(status, active_profile, cb_arg->arg);
7750 }
7751
7752 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7753 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7754
7755 return 0;
7756 }
7757
7758 /**
7759 * @ingroup io
7760 * @brief Get the currently active profile.
7761 * @par Description
7762 * Issues a SLI-4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
7763 * command completes the provided mgmt callback function is
7764 * called.
7765 *
7766 * @param hw Hardware context.
7767 * @param cb Callback function to be called when the
7768 * command completes.
7769 * @param ul_arg An argument that is passed to the callback
7770 * function.
7771 *
7772 * @return
7773 * - OCS_HW_RTN_SUCCESS on success.
7774 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7775 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7776 * context.
7777 * - OCS_HW_RTN_ERROR on any other error.
7778 */
7779 int32_t
ocs_hw_get_active_profile(ocs_hw_t * hw,ocs_get_active_profile_cb_t cb,void * ul_arg)7780 ocs_hw_get_active_profile(ocs_hw_t *hw, ocs_get_active_profile_cb_t cb, void* ul_arg)
7781 {
7782 uint8_t *mbxdata;
7783 ocs_hw_get_active_profile_cb_arg_t *cb_arg;
7784 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7785
7786 /* Only supported on Skyhawk */
7787 if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
7788 return OCS_HW_RTN_ERROR;
7789 }
7790
7791 /* mbxdata holds the header of the command */
7792 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7793 if (mbxdata == NULL) {
7794 ocs_log_err(hw->os, "failed to malloc mbox\n");
7795 return OCS_HW_RTN_NO_MEMORY;
7796 }
7797
7798 /* cb_arg holds the data that will be passed to the callback on completion */
7799 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_active_profile_cb_arg_t), OCS_M_NOWAIT);
7800 if (cb_arg == NULL) {
7801 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7802 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7803 return OCS_HW_RTN_NO_MEMORY;
7804 }
7805
7806 cb_arg->cb = cb;
7807 cb_arg->arg = ul_arg;
7808
7809 if (sli_cmd_common_get_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7810 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_active_profile_cb, cb_arg);
7811 }
7812
7813 if (rc != OCS_HW_RTN_SUCCESS) {
7814 ocs_log_test(hw->os, "GET_ACTIVE_PROFILE failed\n");
7815 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7816 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
7817 }
7818
7819 return rc;
7820 }
7821
7822 typedef struct ocs_hw_get_nvparms_cb_arg_s {
7823 ocs_get_nvparms_cb_t cb;
7824 void *arg;
7825 } ocs_hw_get_nvparms_cb_arg_t;
7826
7827 /**
7828 * @brief Called for the completion of get_nvparms for a
7829 * user request.
7830 *
7831 * @param hw Hardware context.
7832 * @param status The status from the MQE.
7833 * @param mqe Pointer to mailbox command buffer.
7834 * @param arg Pointer to a callback argument.
7835 *
7836 * @return 0 on success, non-zero otherwise
7837 */
7838 static int32_t
ocs_hw_get_nvparms_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7839 ocs_hw_get_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7840 {
7841 ocs_hw_get_nvparms_cb_arg_t *cb_arg = arg;
7842 sli4_cmd_read_nvparms_t* mbox_rsp = (sli4_cmd_read_nvparms_t*) mqe;
7843
7844 if (cb_arg->cb) {
7845 cb_arg->cb(status, mbox_rsp->wwpn, mbox_rsp->wwnn, mbox_rsp->hard_alpa,
7846 mbox_rsp->preferred_d_id, cb_arg->arg);
7847 }
7848
7849 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7850 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7851
7852 return 0;
7853 }
7854
7855 /**
7856 * @ingroup io
7857 * @brief Read non-volatile parms.
7858 * @par Description
7859 * Issues a SLI-4 READ_NVPARMS mailbox. When the
7860 * command completes the provided mgmt callback function is
7861 * called.
7862 *
7863 * @param hw Hardware context.
7864 * @param cb Callback function to be called when the
7865 * command completes.
7866 * @param ul_arg An argument that is passed to the callback
7867 * function.
7868 *
7869 * @return
7870 * - OCS_HW_RTN_SUCCESS on success.
7871 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7872 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7873 * context.
7874 * - OCS_HW_RTN_ERROR on any other error.
7875 */
7876 int32_t
ocs_hw_get_nvparms(ocs_hw_t * hw,ocs_get_nvparms_cb_t cb,void * ul_arg)7877 ocs_hw_get_nvparms(ocs_hw_t *hw, ocs_get_nvparms_cb_t cb, void* ul_arg)
7878 {
7879 uint8_t *mbxdata;
7880 ocs_hw_get_nvparms_cb_arg_t *cb_arg;
7881 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7882
7883 /* mbxdata holds the header of the command */
7884 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7885 if (mbxdata == NULL) {
7886 ocs_log_err(hw->os, "failed to malloc mbox\n");
7887 return OCS_HW_RTN_NO_MEMORY;
7888 }
7889
7890 /* cb_arg holds the data that will be passed to the callback on completion */
7891 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_nvparms_cb_arg_t), OCS_M_NOWAIT);
7892 if (cb_arg == NULL) {
7893 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7894 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7895 return OCS_HW_RTN_NO_MEMORY;
7896 }
7897
7898 cb_arg->cb = cb;
7899 cb_arg->arg = ul_arg;
7900
7901 if (sli_cmd_read_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
7902 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_nvparms_cb, cb_arg);
7903 }
7904
7905 if (rc != OCS_HW_RTN_SUCCESS) {
7906 ocs_log_test(hw->os, "READ_NVPARMS failed\n");
7907 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7908 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
7909 }
7910
7911 return rc;
7912 }
7913
7914 typedef struct ocs_hw_set_nvparms_cb_arg_s {
7915 ocs_set_nvparms_cb_t cb;
7916 void *arg;
7917 } ocs_hw_set_nvparms_cb_arg_t;
7918
7919 /**
7920 * @brief Called for the completion of set_nvparms for a
7921 * user request.
7922 *
7923 * @param hw Hardware context.
7924 * @param status The status from the MQE.
7925 * @param mqe Pointer to mailbox command buffer.
7926 * @param arg Pointer to a callback argument.
7927 *
7928 * @return Returns 0 on success, or a non-zero value on failure.
7929 */
7930 static int32_t
ocs_hw_set_nvparms_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)7931 ocs_hw_set_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
7932 {
7933 ocs_hw_set_nvparms_cb_arg_t *cb_arg = arg;
7934
7935 if (cb_arg->cb) {
7936 cb_arg->cb(status, cb_arg->arg);
7937 }
7938
7939 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
7940 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
7941
7942 return 0;
7943 }
7944
7945 /**
7946 * @ingroup io
7947 * @brief Write non-volatile parms.
7948 * @par Description
7949 * Issues a SLI-4 WRITE_NVPARMS mailbox. When the
7950 * command completes the provided mgmt callback function is
7951 * called.
7952 *
7953 * @param hw Hardware context.
7954 * @param cb Callback function to be called when the
7955 * command completes.
7956 * @param wwpn Port's WWPN in big-endian order, or NULL to use default.
7957 * @param wwnn Port's WWNN in big-endian order, or NULL to use default.
7958 * @param hard_alpa A hard AL_PA address setting used during loop
7959 * initialization. If no hard AL_PA is required, set to 0.
7960 * @param preferred_d_id A preferred D_ID address setting
7961 * that may be overridden with the CONFIG_LINK mailbox command.
7962 * If there is no preference, set to 0.
7963 * @param ul_arg An argument that is passed to the callback
7964 * function.
7965 *
7966 * @return
7967 * - OCS_HW_RTN_SUCCESS on success.
7968 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
7969 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
7970 * context.
7971 * - OCS_HW_RTN_ERROR on any other error.
7972 */
7973 int32_t
ocs_hw_set_nvparms(ocs_hw_t * hw,ocs_set_nvparms_cb_t cb,uint8_t * wwpn,uint8_t * wwnn,uint8_t hard_alpa,uint32_t preferred_d_id,void * ul_arg)7974 ocs_hw_set_nvparms(ocs_hw_t *hw, ocs_set_nvparms_cb_t cb, uint8_t *wwpn,
7975 uint8_t *wwnn, uint8_t hard_alpa, uint32_t preferred_d_id, void* ul_arg)
7976 {
7977 uint8_t *mbxdata;
7978 ocs_hw_set_nvparms_cb_arg_t *cb_arg;
7979 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
7980
7981 /* mbxdata holds the header of the command */
7982 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
7983 if (mbxdata == NULL) {
7984 ocs_log_err(hw->os, "failed to malloc mbox\n");
7985 return OCS_HW_RTN_NO_MEMORY;
7986 }
7987
7988 /* cb_arg holds the data that will be passed to the callback on completion */
7989 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_nvparms_cb_arg_t), OCS_M_NOWAIT);
7990 if (cb_arg == NULL) {
7991 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
7992 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
7993 return OCS_HW_RTN_NO_MEMORY;
7994 }
7995
7996 cb_arg->cb = cb;
7997 cb_arg->arg = ul_arg;
7998
7999 if (sli_cmd_write_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE, wwpn, wwnn, hard_alpa, preferred_d_id)) {
8000 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_nvparms_cb, cb_arg);
8001 }
8002
8003 if (rc != OCS_HW_RTN_SUCCESS) {
8004 ocs_log_test(hw->os, "SET_NVPARMS failed\n");
8005 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8006 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
8007 }
8008
8009 return rc;
8010 }
8011
8012
8013
8014 /**
8015 * @brief Called to obtain the count for the specified type.
8016 *
8017 * @param hw Hardware context.
8018 * @param io_count_type IO count type (inuse, free, wait_free).
8019 *
8020 * @return Returns the number of IOs on the specified list type.
8021 */
8022 uint32_t
ocs_hw_io_get_count(ocs_hw_t * hw,ocs_hw_io_count_type_e io_count_type)8023 ocs_hw_io_get_count(ocs_hw_t *hw, ocs_hw_io_count_type_e io_count_type)
8024 {
8025 ocs_hw_io_t *io = NULL;
8026 uint32_t count = 0;
8027
8028 ocs_lock(&hw->io_lock);
8029
8030 switch (io_count_type) {
8031 case OCS_HW_IO_INUSE_COUNT :
8032 ocs_list_foreach(&hw->io_inuse, io) {
8033 count++;
8034 }
8035 break;
8036 case OCS_HW_IO_FREE_COUNT :
8037 ocs_list_foreach(&hw->io_free, io) {
8038 count++;
8039 }
8040 break;
8041 case OCS_HW_IO_WAIT_FREE_COUNT :
8042 ocs_list_foreach(&hw->io_wait_free, io) {
8043 count++;
8044 }
8045 break;
8046 case OCS_HW_IO_PORT_OWNED_COUNT:
8047 ocs_list_foreach(&hw->io_port_owned, io) {
8048 count++;
8049 }
8050 break;
8051 case OCS_HW_IO_N_TOTAL_IO_COUNT :
8052 count = hw->config.n_io;
8053 break;
8054 }
8055
8056 ocs_unlock(&hw->io_lock);
8057
8058 return count;
8059 }
8060
8061 /**
8062 * @brief Called to obtain the count of produced RQs.
8063 *
8064 * @param hw Hardware context.
8065 *
8066 * @return Returns the number of RQs produced.
8067 */
8068 uint32_t
ocs_hw_get_rqes_produced_count(ocs_hw_t * hw)8069 ocs_hw_get_rqes_produced_count(ocs_hw_t *hw)
8070 {
8071 uint32_t count = 0;
8072 uint32_t i;
8073 uint32_t j;
8074
8075 for (i = 0; i < hw->hw_rq_count; i++) {
8076 hw_rq_t *rq = hw->hw_rq[i];
8077 if (rq->rq_tracker != NULL) {
8078 for (j = 0; j < rq->entry_count; j++) {
8079 if (rq->rq_tracker[j] != NULL) {
8080 count++;
8081 }
8082 }
8083 }
8084 }
8085
8086 return count;
8087 }
8088
8089 typedef struct ocs_hw_set_active_profile_cb_arg_s {
8090 ocs_set_active_profile_cb_t cb;
8091 void *arg;
8092 } ocs_hw_set_active_profile_cb_arg_t;
8093
8094 /**
8095 * @brief Called for the completion of set_active_profile for a
8096 * user request.
8097 *
8098 * @param hw Hardware context.
8099 * @param status The status from the MQE
8100 * @param mqe Pointer to mailbox command buffer.
8101 * @param arg Pointer to a callback argument.
8102 *
8103 * @return Returns 0 on success, or a non-zero value on failure.
8104 */
8105 static int32_t
ocs_hw_set_active_profile_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)8106 ocs_hw_set_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
8107 {
8108 ocs_hw_set_active_profile_cb_arg_t *cb_arg = arg;
8109
8110 if (cb_arg->cb) {
8111 cb_arg->cb(status, cb_arg->arg);
8112 }
8113
8114 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
8115 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
8116
8117 return 0;
8118 }
8119
8120 /**
8121 * @ingroup io
8122 * @brief Set the currently active profile.
8123 * @par Description
8124 * Issues a SLI4 COMMON_GET_ACTIVE_PROFILE mailbox. When the
8125 * command completes the provided mgmt callback function is
8126 * called.
8127 *
8128 * @param hw Hardware context.
8129 * @param profile_id Profile ID to activate.
8130 * @param cb Callback function to be called when the command completes.
8131 * @param ul_arg An argument that is passed to the callback function.
8132 *
8133 * @return
8134 * - OCS_HW_RTN_SUCCESS on success.
8135 * - OCS_HW_RTN_NO_MEMORY if a malloc fails.
8136 * - OCS_HW_RTN_NO_RESOURCES if unable to get a command
8137 * context.
8138 * - OCS_HW_RTN_ERROR on any other error.
8139 */
8140 int32_t
ocs_hw_set_active_profile(ocs_hw_t * hw,ocs_set_active_profile_cb_t cb,uint32_t profile_id,void * ul_arg)8141 ocs_hw_set_active_profile(ocs_hw_t *hw, ocs_set_active_profile_cb_t cb, uint32_t profile_id, void* ul_arg)
8142 {
8143 uint8_t *mbxdata;
8144 ocs_hw_set_active_profile_cb_arg_t *cb_arg;
8145 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
8146
8147 /* Only supported on Skyhawk */
8148 if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
8149 return OCS_HW_RTN_ERROR;
8150 }
8151
8152 /* mbxdata holds the header of the command */
8153 mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
8154 if (mbxdata == NULL) {
8155 ocs_log_err(hw->os, "failed to malloc mbox\n");
8156 return OCS_HW_RTN_NO_MEMORY;
8157 }
8158
8159
8160 /* cb_arg holds the data that will be passed to the callback on completion */
8161 cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_active_profile_cb_arg_t), OCS_M_NOWAIT);
8162 if (cb_arg == NULL) {
8163 ocs_log_err(hw->os, "failed to malloc cb_arg\n");
8164 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8165 return OCS_HW_RTN_NO_MEMORY;
8166 }
8167
8168 cb_arg->cb = cb;
8169 cb_arg->arg = ul_arg;
8170
8171 if (sli_cmd_common_set_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, profile_id)) {
8172 rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_active_profile_cb, cb_arg);
8173 }
8174
8175 if (rc != OCS_HW_RTN_SUCCESS) {
8176 ocs_log_test(hw->os, "SET_ACTIVE_PROFILE failed\n");
8177 ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
8178 ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_active_profile_cb_arg_t));
8179 }
8180
8181 return rc;
8182 }
8183
8184
8185
8186 /*
8187 * Private functions
8188 */
8189
8190 /**
8191 * @brief Update the queue hash with the ID and index.
8192 *
8193 * @param hash Pointer to hash table.
8194 * @param id ID that was created.
8195 * @param index The index into the hash object.
8196 */
8197 static void
ocs_hw_queue_hash_add(ocs_queue_hash_t * hash,uint16_t id,uint16_t index)8198 ocs_hw_queue_hash_add(ocs_queue_hash_t *hash, uint16_t id, uint16_t index)
8199 {
8200 uint32_t hash_index = id & (OCS_HW_Q_HASH_SIZE - 1);
8201
8202 /*
8203 * Since the hash is always bigger than the number of queues, then we
8204 * never have to worry about an infinite loop.
8205 */
8206 while(hash[hash_index].in_use) {
8207 hash_index = (hash_index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8208 }
8209
8210 /* not used, claim the entry */
8211 hash[hash_index].id = id;
8212 hash[hash_index].in_use = 1;
8213 hash[hash_index].index = index;
8214 }
8215
8216 /**
8217 * @brief Find index given queue ID.
8218 *
8219 * @param hash Pointer to hash table.
8220 * @param id ID to find.
8221 *
8222 * @return Returns the index into the HW cq array or -1 if not found.
8223 */
8224 int32_t
ocs_hw_queue_hash_find(ocs_queue_hash_t * hash,uint16_t id)8225 ocs_hw_queue_hash_find(ocs_queue_hash_t *hash, uint16_t id)
8226 {
8227 int32_t rc = -1;
8228 int32_t index = id & (OCS_HW_Q_HASH_SIZE - 1);
8229
8230 /*
8231 * Since the hash is always bigger than the maximum number of Qs, then we
8232 * never have to worry about an infinite loop. We will always find an
8233 * unused entry.
8234 */
8235 do {
8236 if (hash[index].in_use &&
8237 hash[index].id == id) {
8238 rc = hash[index].index;
8239 } else {
8240 index = (index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
8241 }
8242 } while(rc == -1 && hash[index].in_use);
8243
8244 return rc;
8245 }
8246
8247 static int32_t
ocs_hw_domain_add(ocs_hw_t * hw,ocs_domain_t * domain)8248 ocs_hw_domain_add(ocs_hw_t *hw, ocs_domain_t *domain)
8249 {
8250 int32_t rc = OCS_HW_RTN_ERROR;
8251 uint16_t fcfi = UINT16_MAX;
8252
8253 if ((hw == NULL) || (domain == NULL)) {
8254 ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8255 hw, domain);
8256 return OCS_HW_RTN_ERROR;
8257 }
8258
8259 fcfi = domain->fcf_indicator;
8260
8261 if (fcfi < SLI4_MAX_FCFI) {
8262 uint16_t fcf_index = UINT16_MAX;
8263
8264 ocs_log_debug(hw->os, "adding domain %p @ %#x\n",
8265 domain, fcfi);
8266 hw->domains[fcfi] = domain;
8267
8268 /* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8269 if (hw->workaround.override_fcfi) {
8270 if (hw->first_domain_idx < 0) {
8271 hw->first_domain_idx = fcfi;
8272 }
8273 }
8274
8275 fcf_index = domain->fcf;
8276
8277 if (fcf_index < SLI4_MAX_FCF_INDEX) {
8278 ocs_log_debug(hw->os, "adding map of FCF index %d to FCFI %d\n",
8279 fcf_index, fcfi);
8280 hw->fcf_index_fcfi[fcf_index] = fcfi;
8281 rc = OCS_HW_RTN_SUCCESS;
8282 } else {
8283 ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8284 fcf_index, SLI4_MAX_FCF_INDEX);
8285 hw->domains[fcfi] = NULL;
8286 }
8287 } else {
8288 ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8289 fcfi, SLI4_MAX_FCFI);
8290 }
8291
8292 return rc;
8293 }
8294
8295 static int32_t
ocs_hw_domain_del(ocs_hw_t * hw,ocs_domain_t * domain)8296 ocs_hw_domain_del(ocs_hw_t *hw, ocs_domain_t *domain)
8297 {
8298 int32_t rc = OCS_HW_RTN_ERROR;
8299 uint16_t fcfi = UINT16_MAX;
8300
8301 if ((hw == NULL) || (domain == NULL)) {
8302 ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
8303 hw, domain);
8304 return OCS_HW_RTN_ERROR;
8305 }
8306
8307 fcfi = domain->fcf_indicator;
8308
8309 if (fcfi < SLI4_MAX_FCFI) {
8310 uint16_t fcf_index = UINT16_MAX;
8311
8312 ocs_log_debug(hw->os, "deleting domain %p @ %#x\n",
8313 domain, fcfi);
8314
8315 if (domain != hw->domains[fcfi]) {
8316 ocs_log_test(hw->os, "provided domain %p does not match stored domain %p\n",
8317 domain, hw->domains[fcfi]);
8318 return OCS_HW_RTN_ERROR;
8319 }
8320
8321 hw->domains[fcfi] = NULL;
8322
8323 /* HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB */
8324 if (hw->workaround.override_fcfi) {
8325 if (hw->first_domain_idx == fcfi) {
8326 hw->first_domain_idx = -1;
8327 }
8328 }
8329
8330 fcf_index = domain->fcf;
8331
8332 if (fcf_index < SLI4_MAX_FCF_INDEX) {
8333 if (hw->fcf_index_fcfi[fcf_index] == fcfi) {
8334 hw->fcf_index_fcfi[fcf_index] = 0;
8335 rc = OCS_HW_RTN_SUCCESS;
8336 } else {
8337 ocs_log_test(hw->os, "indexed FCFI %#x doesn't match provided %#x @ %d\n",
8338 hw->fcf_index_fcfi[fcf_index], fcfi, fcf_index);
8339 }
8340 } else {
8341 ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8342 fcf_index, SLI4_MAX_FCF_INDEX);
8343 }
8344 } else {
8345 ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8346 fcfi, SLI4_MAX_FCFI);
8347 }
8348
8349 return rc;
8350 }
8351
8352 ocs_domain_t *
ocs_hw_domain_get(ocs_hw_t * hw,uint16_t fcfi)8353 ocs_hw_domain_get(ocs_hw_t *hw, uint16_t fcfi)
8354 {
8355
8356 if (hw == NULL) {
8357 ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8358 return NULL;
8359 }
8360
8361 if (fcfi < SLI4_MAX_FCFI) {
8362 return hw->domains[fcfi];
8363 } else {
8364 ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
8365 fcfi, SLI4_MAX_FCFI);
8366 return NULL;
8367 }
8368 }
8369
8370 static ocs_domain_t *
ocs_hw_domain_get_indexed(ocs_hw_t * hw,uint16_t fcf_index)8371 ocs_hw_domain_get_indexed(ocs_hw_t *hw, uint16_t fcf_index)
8372 {
8373
8374 if (hw == NULL) {
8375 ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
8376 return NULL;
8377 }
8378
8379 if (fcf_index < SLI4_MAX_FCF_INDEX) {
8380 return ocs_hw_domain_get(hw, hw->fcf_index_fcfi[fcf_index]);
8381 } else {
8382 ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
8383 fcf_index, SLI4_MAX_FCF_INDEX);
8384 return NULL;
8385 }
8386 }
8387
8388 /**
8389 * @brief Quaratine an IO by taking a reference count and adding it to the
8390 * quarantine list. When the IO is popped from the list then the
8391 * count is released and the IO MAY be freed depending on whether
8392 * it is still referenced by the IO.
8393 *
8394 * @n @b Note: BZ 160124 - If this is a target write or an initiator read using
8395 * DIF, then we must add the XRI to a quarantine list until we receive
8396 * 4 more completions of this same type.
8397 *
8398 * @param hw Hardware context.
8399 * @param wq Pointer to the WQ associated with the IO object to quarantine.
8400 * @param io Pointer to the io object to quarantine.
8401 */
8402 static void
ocs_hw_io_quarantine(ocs_hw_t * hw,hw_wq_t * wq,ocs_hw_io_t * io)8403 ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io)
8404 {
8405 ocs_quarantine_info_t *q_info = &wq->quarantine_info;
8406 uint32_t index;
8407 ocs_hw_io_t *free_io = NULL;
8408
8409 /* return if the QX bit was clear */
8410 if (!io->quarantine) {
8411 return;
8412 }
8413
8414 /* increment the IO refcount to prevent it from being freed before the quarantine is over */
8415 if (ocs_ref_get_unless_zero(&io->ref) == 0) {
8416 /* command no longer active */
8417 ocs_log_debug(hw ? hw->os : NULL,
8418 "io not active xri=0x%x tag=0x%x\n",
8419 io->indicator, io->reqtag);
8420 return;
8421 }
8422
8423 sli_queue_lock(wq->queue);
8424 index = q_info->quarantine_index;
8425 free_io = q_info->quarantine_ios[index];
8426 q_info->quarantine_ios[index] = io;
8427 q_info->quarantine_index = (index + 1) % OCS_HW_QUARANTINE_QUEUE_DEPTH;
8428 sli_queue_unlock(wq->queue);
8429
8430 if (free_io != NULL) {
8431 ocs_ref_put(&free_io->ref); /* ocs_ref_get(): same function */
8432 }
8433 }
8434
8435 /**
8436 * @brief Process entries on the given completion queue.
8437 *
8438 * @param hw Hardware context.
8439 * @param cq Pointer to the HW completion queue object.
8440 *
8441 * @return None.
8442 */
8443 void
ocs_hw_cq_process(ocs_hw_t * hw,hw_cq_t * cq)8444 ocs_hw_cq_process(ocs_hw_t *hw, hw_cq_t *cq)
8445 {
8446 uint8_t cqe[sizeof(sli4_mcqe_t)];
8447 uint16_t rid = UINT16_MAX;
8448 sli4_qentry_e ctype; /* completion type */
8449 int32_t status;
8450 uint32_t n_processed = 0;
8451 time_t tstart;
8452 time_t telapsed;
8453
8454 tstart = ocs_msectime();
8455
8456 while (!sli_queue_read(&hw->sli, cq->queue, cqe)) {
8457 status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
8458 /*
8459 * The sign of status is significant. If status is:
8460 * == 0 : call completed correctly and the CQE indicated success
8461 * > 0 : call completed correctly and the CQE indicated an error
8462 * < 0 : call failed and no information is available about the CQE
8463 */
8464 if (status < 0) {
8465 if (status == -2) {
8466 /* Notification that an entry was consumed, but not completed */
8467 continue;
8468 }
8469
8470 break;
8471 }
8472
8473 switch (ctype) {
8474 case SLI_QENTRY_ASYNC:
8475 CPUTRACE("async");
8476 sli_cqe_async(&hw->sli, cqe);
8477 break;
8478 case SLI_QENTRY_MQ:
8479 /*
8480 * Process MQ entry. Note there is no way to determine
8481 * the MQ_ID from the completion entry.
8482 */
8483 CPUTRACE("mq");
8484 ocs_hw_mq_process(hw, status, hw->mq);
8485 break;
8486 case SLI_QENTRY_OPT_WRITE_CMD:
8487 ocs_hw_rqpair_process_auto_xfr_rdy_cmd(hw, cq, cqe);
8488 break;
8489 case SLI_QENTRY_OPT_WRITE_DATA:
8490 ocs_hw_rqpair_process_auto_xfr_rdy_data(hw, cq, cqe);
8491 break;
8492 case SLI_QENTRY_WQ:
8493 CPUTRACE("wq");
8494 ocs_hw_wq_process(hw, cq, cqe, status, rid);
8495 break;
8496 case SLI_QENTRY_WQ_RELEASE: {
8497 uint32_t wq_id = rid;
8498 uint32_t index = ocs_hw_queue_hash_find(hw->wq_hash, wq_id);
8499 hw_wq_t *wq = hw->hw_wq[index];
8500
8501 /* Submit any HW IOs that are on the WQ pending list */
8502 hw_wq_submit_pending(wq, wq->wqec_set_count);
8503
8504 break;
8505 }
8506
8507 case SLI_QENTRY_RQ:
8508 CPUTRACE("rq");
8509 ocs_hw_rqpair_process_rq(hw, cq, cqe);
8510 break;
8511 case SLI_QENTRY_XABT: {
8512 CPUTRACE("xabt");
8513 ocs_hw_xabt_process(hw, cq, cqe, rid);
8514 break;
8515
8516 }
8517 default:
8518 ocs_log_test(hw->os, "unhandled ctype=%#x rid=%#x\n", ctype, rid);
8519 break;
8520 }
8521
8522 n_processed++;
8523 if (n_processed == cq->queue->proc_limit) {
8524 break;
8525 }
8526
8527 if (cq->queue->n_posted >= (cq->queue->posted_limit)) {
8528 sli_queue_arm(&hw->sli, cq->queue, FALSE);
8529 }
8530 }
8531
8532 sli_queue_arm(&hw->sli, cq->queue, TRUE);
8533
8534 if (n_processed > cq->queue->max_num_processed) {
8535 cq->queue->max_num_processed = n_processed;
8536 }
8537 telapsed = ocs_msectime() - tstart;
8538 if (telapsed > cq->queue->max_process_time) {
8539 cq->queue->max_process_time = telapsed;
8540 }
8541 }
8542
8543 /**
8544 * @brief Process WQ completion queue entries.
8545 *
8546 * @param hw Hardware context.
8547 * @param cq Pointer to the HW completion queue object.
8548 * @param cqe Pointer to WQ completion queue.
8549 * @param status Completion status.
8550 * @param rid Resource ID (IO tag).
8551 *
8552 * @return none
8553 */
8554 void
ocs_hw_wq_process(ocs_hw_t * hw,hw_cq_t * cq,uint8_t * cqe,int32_t status,uint16_t rid)8555 ocs_hw_wq_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, int32_t status, uint16_t rid)
8556 {
8557 hw_wq_callback_t *wqcb;
8558
8559 ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_WQ, (void *)cqe, ((sli4_fc_wcqe_t *)cqe)->status, cq->queue->id,
8560 ((cq->queue->index - 1) & (cq->queue->length - 1)));
8561
8562 if(rid == OCS_HW_REQUE_XRI_REGTAG) {
8563 if(status) {
8564 ocs_log_err(hw->os, "reque xri failed, status = %d \n", status);
8565 }
8566 return;
8567 }
8568
8569 wqcb = ocs_hw_reqtag_get_instance(hw, rid);
8570 if (wqcb == NULL) {
8571 ocs_log_err(hw->os, "invalid request tag: x%x\n", rid);
8572 return;
8573 }
8574
8575 if (wqcb->callback == NULL) {
8576 ocs_log_err(hw->os, "wqcb callback is NULL\n");
8577 return;
8578 }
8579
8580 (*wqcb->callback)(wqcb->arg, cqe, status);
8581 }
8582
8583 /**
8584 * @brief Process WQ completions for IO requests
8585 *
8586 * @param arg Generic callback argument
8587 * @param cqe Pointer to completion queue entry
8588 * @param status Completion status
8589 *
8590 * @par Description
8591 * @n @b Note: Regarding io->reqtag, the reqtag is assigned once when HW IOs are initialized
8592 * in ocs_hw_setup_io(), and don't need to be returned to the hw->wq_reqtag_pool.
8593 *
8594 * @return None.
8595 */
8596 static void
ocs_hw_wq_process_io(void * arg,uint8_t * cqe,int32_t status)8597 ocs_hw_wq_process_io(void *arg, uint8_t *cqe, int32_t status)
8598 {
8599 ocs_hw_io_t *io = arg;
8600 ocs_hw_t *hw = io->hw;
8601 sli4_fc_wcqe_t *wcqe = (void *)cqe;
8602 uint32_t len = 0;
8603 uint32_t ext = 0;
8604 uint8_t out_of_order_axr_cmd = 0;
8605 uint8_t out_of_order_axr_data = 0;
8606 uint8_t lock_taken = 0;
8607 #if defined(OCS_DISC_SPIN_DELAY)
8608 uint32_t delay = 0;
8609 char prop_buf[32];
8610 #endif
8611
8612 /*
8613 * For the primary IO, this will also be used for the
8614 * response. So it is important to only set/clear this
8615 * flag on the first data phase of the IO because
8616 * subsequent phases will be done on the secondary XRI.
8617 */
8618 if (io->quarantine && io->quarantine_first_phase) {
8619 io->quarantine = (wcqe->qx == 1);
8620 ocs_hw_io_quarantine(hw, io->wq, io);
8621 }
8622 io->quarantine_first_phase = FALSE;
8623
8624 /* BZ 161832 - free secondary HW IO */
8625 if (io->sec_hio != NULL &&
8626 io->sec_hio->quarantine) {
8627 /*
8628 * If the quarantine flag is set on the
8629 * IO, then set it on the secondary IO
8630 * based on the quarantine XRI (QX) bit
8631 * sent by the FW.
8632 */
8633 io->sec_hio->quarantine = (wcqe->qx == 1);
8634 /* use the primary io->wq because it is not set on the secondary IO. */
8635 ocs_hw_io_quarantine(hw, io->wq, io->sec_hio);
8636 }
8637
8638 ocs_hw_remove_io_timed_wqe(hw, io);
8639
8640 /* clear xbusy flag if WCQE[XB] is clear */
8641 if (io->xbusy && wcqe->xb == 0) {
8642 io->xbusy = FALSE;
8643 }
8644
8645 /* get extended CQE status */
8646 switch (io->type) {
8647 case OCS_HW_BLS_ACC:
8648 case OCS_HW_BLS_ACC_SID:
8649 break;
8650 case OCS_HW_ELS_REQ:
8651 sli_fc_els_did(&hw->sli, cqe, &ext);
8652 len = sli_fc_response_length(&hw->sli, cqe);
8653 break;
8654 case OCS_HW_ELS_RSP:
8655 case OCS_HW_ELS_RSP_SID:
8656 case OCS_HW_FC_CT_RSP:
8657 break;
8658 case OCS_HW_FC_CT:
8659 len = sli_fc_response_length(&hw->sli, cqe);
8660 break;
8661 case OCS_HW_IO_TARGET_WRITE:
8662 len = sli_fc_io_length(&hw->sli, cqe);
8663 #if defined(OCS_DISC_SPIN_DELAY)
8664 if (ocs_get_property("disk_spin_delay", prop_buf, sizeof(prop_buf)) == 0) {
8665 delay = ocs_strtoul(prop_buf, 0, 0);
8666 ocs_udelay(delay);
8667 }
8668 #endif
8669 break;
8670 case OCS_HW_IO_TARGET_READ:
8671 len = sli_fc_io_length(&hw->sli, cqe);
8672 /*
8673 * if_type == 2 seems to return 0 "total length placed" on
8674 * FCP_TSEND64_WQE completions. If this appears to happen,
8675 * use the CTIO data transfer length instead.
8676 */
8677 if (hw->workaround.retain_tsend_io_length && !len && !status) {
8678 len = io->length;
8679 }
8680
8681 break;
8682 case OCS_HW_IO_TARGET_RSP:
8683 if(io->is_port_owned) {
8684 ocs_lock(&io->axr_lock);
8685 lock_taken = 1;
8686 if(io->axr_buf->call_axr_cmd) {
8687 out_of_order_axr_cmd = 1;
8688 }
8689 if(io->axr_buf->call_axr_data) {
8690 out_of_order_axr_data = 1;
8691 }
8692 }
8693 break;
8694 case OCS_HW_IO_INITIATOR_READ:
8695 len = sli_fc_io_length(&hw->sli, cqe);
8696 break;
8697 case OCS_HW_IO_INITIATOR_WRITE:
8698 len = sli_fc_io_length(&hw->sli, cqe);
8699 break;
8700 case OCS_HW_IO_INITIATOR_NODATA:
8701 break;
8702 case OCS_HW_IO_DNRX_REQUEUE:
8703 /* release the count for re-posting the buffer */
8704 //ocs_hw_io_free(hw, io);
8705 break;
8706 default:
8707 ocs_log_test(hw->os, "XXX unhandled io type %#x for XRI 0x%x\n",
8708 io->type, io->indicator);
8709 break;
8710 }
8711 if (status) {
8712 ext = sli_fc_ext_status(&hw->sli, cqe);
8713 /* Emulate IAAB=0 for initiator WQEs only; i.e. automatically
8714 * abort exchange if an error occurred and exchange is still busy.
8715 */
8716 if (hw->config.i_only_aab &&
8717 (ocs_hw_iotype_is_originator(io->type)) &&
8718 (ocs_hw_wcqe_abort_needed(status, ext, wcqe->xb))) {
8719 ocs_hw_rtn_e rc;
8720
8721 ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
8722 io->indicator, io->reqtag);
8723 /*
8724 * Because the initiator will not issue another IO phase, then it is OK to issue the
8725 * callback on the abort completion, but for consistency with the target, wait for the
8726 * XRI_ABORTED CQE to issue the IO callback.
8727 */
8728 rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
8729
8730 if (rc == OCS_HW_RTN_SUCCESS) {
8731 /* latch status to return after abort is complete */
8732 io->status_saved = 1;
8733 io->saved_status = status;
8734 io->saved_ext = ext;
8735 io->saved_len = len;
8736 goto exit_ocs_hw_wq_process_io;
8737 } else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8738 /*
8739 * Already being aborted by someone else (ABTS
8740 * perhaps). Just fall through and return original
8741 * error.
8742 */
8743 ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8744 io->indicator, io->reqtag);
8745
8746 } else {
8747 /* Failed to abort for some other reason, log error */
8748 ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8749 io->indicator, io->reqtag, rc);
8750 }
8751 }
8752
8753 /*
8754 * If we're not an originator IO, and XB is set, then issue abort for the IO from within the HW
8755 */
8756 if ( (! ocs_hw_iotype_is_originator(io->type)) && wcqe->xb) {
8757 ocs_hw_rtn_e rc;
8758
8759 ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", io->indicator, io->reqtag);
8760
8761 /*
8762 * Because targets may send a response when the IO completes using the same XRI, we must
8763 * wait for the XRI_ABORTED CQE to issue the IO callback
8764 */
8765 rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
8766 if (rc == OCS_HW_RTN_SUCCESS) {
8767 /* latch status to return after abort is complete */
8768 io->status_saved = 1;
8769 io->saved_status = status;
8770 io->saved_ext = ext;
8771 io->saved_len = len;
8772 goto exit_ocs_hw_wq_process_io;
8773 } else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
8774 /*
8775 * Already being aborted by someone else (ABTS
8776 * perhaps). Just fall through and return original
8777 * error.
8778 */
8779 ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
8780 io->indicator, io->reqtag);
8781
8782 } else {
8783 /* Failed to abort for some other reason, log error */
8784 ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
8785 io->indicator, io->reqtag, rc);
8786 }
8787 }
8788 }
8789 /* BZ 161832 - free secondary HW IO */
8790 if (io->sec_hio != NULL) {
8791 ocs_hw_io_free(hw, io->sec_hio);
8792 io->sec_hio = NULL;
8793 }
8794
8795 if (io->done != NULL) {
8796 ocs_hw_done_t done = io->done;
8797 void *arg = io->arg;
8798
8799 io->done = NULL;
8800
8801 if (io->status_saved) {
8802 /* use latched status if exists */
8803 status = io->saved_status;
8804 len = io->saved_len;
8805 ext = io->saved_ext;
8806 io->status_saved = 0;
8807 }
8808
8809 /* Restore default SGL */
8810 ocs_hw_io_restore_sgl(hw, io);
8811 done(io, io->rnode, len, status, ext, arg);
8812 }
8813
8814 if(out_of_order_axr_cmd) {
8815 /* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8816 if (hw->config.bounce) {
8817 fc_header_t *hdr = io->axr_buf->cmd_seq->header->dma.virt;
8818 uint32_t s_id = fc_be24toh(hdr->s_id);
8819 uint32_t d_id = fc_be24toh(hdr->d_id);
8820 uint32_t ox_id = ocs_be16toh(hdr->ox_id);
8821 if (hw->callback.bounce != NULL) {
8822 (*hw->callback.bounce)(ocs_hw_unsol_process_bounce, io->axr_buf->cmd_seq, s_id, d_id, ox_id);
8823 }
8824 }else {
8825 hw->callback.unsolicited(hw->args.unsolicited, io->axr_buf->cmd_seq);
8826 }
8827
8828 if(out_of_order_axr_data) {
8829 /* bounce enabled, single RQ, we snoop the ox_id to choose the cpuidx */
8830 if (hw->config.bounce) {
8831 fc_header_t *hdr = io->axr_buf->seq.header->dma.virt;
8832 uint32_t s_id = fc_be24toh(hdr->s_id);
8833 uint32_t d_id = fc_be24toh(hdr->d_id);
8834 uint32_t ox_id = ocs_be16toh(hdr->ox_id);
8835 if (hw->callback.bounce != NULL) {
8836 (*hw->callback.bounce)(ocs_hw_unsol_process_bounce, &io->axr_buf->seq, s_id, d_id, ox_id);
8837 }
8838 }else {
8839 hw->callback.unsolicited(hw->args.unsolicited, &io->axr_buf->seq);
8840 }
8841 }
8842 }
8843
8844 exit_ocs_hw_wq_process_io:
8845 if(lock_taken) {
8846 ocs_unlock(&io->axr_lock);
8847 }
8848 }
8849
8850 /**
8851 * @brief Process WQ completions for abort requests.
8852 *
8853 * @param arg Generic callback argument.
8854 * @param cqe Pointer to completion queue entry.
8855 * @param status Completion status.
8856 *
8857 * @return None.
8858 */
8859 static void
ocs_hw_wq_process_abort(void * arg,uint8_t * cqe,int32_t status)8860 ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status)
8861 {
8862 ocs_hw_io_t *io = arg;
8863 ocs_hw_t *hw = io->hw;
8864 uint32_t ext = 0;
8865 uint32_t len = 0;
8866 hw_wq_callback_t *wqcb;
8867
8868 /*
8869 * For IOs that were aborted internally, we may need to issue the callback here depending
8870 * on whether a XRI_ABORTED CQE is expected ot not. If the status is Local Reject/No XRI, then
8871 * issue the callback now.
8872 */
8873 ext = sli_fc_ext_status(&hw->sli, cqe);
8874 if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
8875 ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
8876 io->done != NULL) {
8877 ocs_hw_done_t done = io->done;
8878 void *arg = io->arg;
8879
8880 io->done = NULL;
8881
8882 /*
8883 * Use latched status as this is always saved for an internal abort
8884 *
8885 * Note: We wont have both a done and abort_done function, so don't worry about
8886 * clobbering the len, status and ext fields.
8887 */
8888 status = io->saved_status;
8889 len = io->saved_len;
8890 ext = io->saved_ext;
8891 io->status_saved = 0;
8892 done(io, io->rnode, len, status, ext, arg);
8893 }
8894
8895 if (io->abort_done != NULL) {
8896 ocs_hw_done_t done = io->abort_done;
8897 void *arg = io->abort_arg;
8898
8899 io->abort_done = NULL;
8900
8901 done(io, io->rnode, len, status, ext, arg);
8902 }
8903 ocs_lock(&hw->io_abort_lock);
8904 /* clear abort bit to indicate abort is complete */
8905 io->abort_in_progress = 0;
8906 ocs_unlock(&hw->io_abort_lock);
8907
8908 /* Free the WQ callback */
8909 ocs_hw_assert(io->abort_reqtag != UINT32_MAX);
8910 wqcb = ocs_hw_reqtag_get_instance(hw, io->abort_reqtag);
8911 ocs_hw_reqtag_free(hw, wqcb);
8912
8913 /*
8914 * Call ocs_hw_io_free() because this releases the WQ reservation as
8915 * well as doing the refcount put. Don't duplicate the code here.
8916 */
8917 (void)ocs_hw_io_free(hw, io);
8918 }
8919
8920 /**
8921 * @brief Process XABT completions
8922 *
8923 * @param hw Hardware context.
8924 * @param cq Pointer to the HW completion queue object.
8925 * @param cqe Pointer to WQ completion queue.
8926 * @param rid Resource ID (IO tag).
8927 *
8928 *
8929 * @return None.
8930 */
8931 void
ocs_hw_xabt_process(ocs_hw_t * hw,hw_cq_t * cq,uint8_t * cqe,uint16_t rid)8932 ocs_hw_xabt_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, uint16_t rid)
8933 {
8934 /* search IOs wait free list */
8935 ocs_hw_io_t *io = NULL;
8936
8937 io = ocs_hw_io_lookup(hw, rid);
8938
8939 ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_XABT, (void *)cqe, 0, cq->queue->id,
8940 ((cq->queue->index - 1) & (cq->queue->length - 1)));
8941 if (io == NULL) {
8942 /* IO lookup failure should never happen */
8943 ocs_log_err(hw->os, "Error: xabt io lookup failed rid=%#x\n", rid);
8944 return;
8945 }
8946
8947 if (!io->xbusy) {
8948 ocs_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
8949 } else {
8950 /* mark IO as no longer busy */
8951 io->xbusy = FALSE;
8952 }
8953
8954 if (io->is_port_owned) {
8955 ocs_lock(&hw->io_lock);
8956 /* Take reference so that below callback will not free io before reque */
8957 ocs_ref_get(&io->ref);
8958 ocs_unlock(&hw->io_lock);
8959 }
8960
8961
8962
8963 /* For IOs that were aborted internally, we need to issue any pending callback here. */
8964 if (io->done != NULL) {
8965 ocs_hw_done_t done = io->done;
8966 void *arg = io->arg;
8967
8968 /* Use latched status as this is always saved for an internal abort */
8969 int32_t status = io->saved_status;
8970 uint32_t len = io->saved_len;
8971 uint32_t ext = io->saved_ext;
8972
8973 io->done = NULL;
8974 io->status_saved = 0;
8975
8976 done(io, io->rnode, len, status, ext, arg);
8977 }
8978
8979 /* Check to see if this is a port owned XRI */
8980 if (io->is_port_owned) {
8981 ocs_lock(&hw->io_lock);
8982 ocs_hw_reque_xri(hw, io);
8983 ocs_unlock(&hw->io_lock);
8984 /* Not hanlding reque xri completion, free io */
8985 ocs_hw_io_free(hw, io);
8986 return;
8987 }
8988
8989 ocs_lock(&hw->io_lock);
8990 if ((io->state == OCS_HW_IO_STATE_INUSE) || (io->state == OCS_HW_IO_STATE_WAIT_FREE)) {
8991 /* if on wait_free list, caller has already freed IO;
8992 * remove from wait_free list and add to free list.
8993 * if on in-use list, already marked as no longer busy;
8994 * just leave there and wait for caller to free.
8995 */
8996 if (io->state == OCS_HW_IO_STATE_WAIT_FREE) {
8997 io->state = OCS_HW_IO_STATE_FREE;
8998 ocs_list_remove(&hw->io_wait_free, io);
8999 ocs_hw_io_free_move_correct_list(hw, io);
9000 }
9001 }
9002 ocs_unlock(&hw->io_lock);
9003 }
9004
9005 /**
9006 * @brief Adjust the number of WQs and CQs within the HW.
9007 *
9008 * @par Description
9009 * Calculates the number of WQs and associated CQs needed in the HW based on
9010 * the number of IOs. Calculates the starting CQ index for each WQ, RQ and
9011 * MQ.
9012 *
9013 * @param hw Hardware context allocated by the caller.
9014 */
9015 static void
ocs_hw_adjust_wqs(ocs_hw_t * hw)9016 ocs_hw_adjust_wqs(ocs_hw_t *hw)
9017 {
9018 uint32_t max_wq_num = sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ);
9019 uint32_t max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ];
9020 uint32_t max_cq_entries = hw->num_qentries[SLI_QTYPE_CQ];
9021
9022 /*
9023 * possibly adjust the the size of the WQs so that the CQ is twice as
9024 * big as the WQ to allow for 2 completions per IO. This allows us to
9025 * handle multi-phase as well as aborts.
9026 */
9027 if (max_cq_entries < max_wq_entries * 2) {
9028 max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ] = max_cq_entries / 2;
9029 }
9030
9031 /*
9032 * Calculate the number of WQs to use base on the number of IOs.
9033 *
9034 * Note: We need to reserve room for aborts which must be sent down
9035 * the same WQ as the IO. So we allocate enough WQ space to
9036 * handle 2 times the number of IOs. Half of the space will be
9037 * used for normal IOs and the other hwf is reserved for aborts.
9038 */
9039 hw->config.n_wq = ((hw->config.n_io * 2) + (max_wq_entries - 1)) / max_wq_entries;
9040
9041 /*
9042 * For performance reasons, it is best to use use a minimum of 4 WQs
9043 * for BE3 and Skyhawk.
9044 */
9045 if (hw->config.n_wq < 4 &&
9046 SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
9047 hw->config.n_wq = 4;
9048 }
9049
9050 /*
9051 * For dual-chute support, we need to have at least one WQ per chute.
9052 */
9053 if (hw->config.n_wq < 2 &&
9054 ocs_hw_get_num_chutes(hw) > 1) {
9055 hw->config.n_wq = 2;
9056 }
9057
9058 /* make sure we haven't exceeded the max supported in the HW */
9059 if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
9060 hw->config.n_wq = OCS_HW_MAX_NUM_WQ;
9061 }
9062
9063 /* make sure we haven't exceeded the chip maximum */
9064 if (hw->config.n_wq > max_wq_num) {
9065 hw->config.n_wq = max_wq_num;
9066 }
9067
9068 /*
9069 * Using Queue Topology string, we divide by number of chutes
9070 */
9071 hw->config.n_wq /= ocs_hw_get_num_chutes(hw);
9072 }
9073
9074 static int32_t
ocs_hw_command_process(ocs_hw_t * hw,int32_t status,uint8_t * mqe,size_t size)9075 ocs_hw_command_process(ocs_hw_t *hw, int32_t status, uint8_t *mqe, size_t size)
9076 {
9077 ocs_command_ctx_t *ctx = NULL;
9078
9079 ocs_lock(&hw->cmd_lock);
9080 if (NULL == (ctx = ocs_list_remove_head(&hw->cmd_head))) {
9081 ocs_log_err(hw->os, "XXX no command context?!?\n");
9082 ocs_unlock(&hw->cmd_lock);
9083 return -1;
9084 }
9085
9086 hw->cmd_head_count--;
9087
9088 /* Post any pending requests */
9089 ocs_hw_cmd_submit_pending(hw);
9090
9091 ocs_unlock(&hw->cmd_lock);
9092
9093 if (ctx->cb) {
9094 if (ctx->buf) {
9095 ocs_memcpy(ctx->buf, mqe, size);
9096 }
9097 ctx->cb(hw, status, ctx->buf, ctx->arg);
9098 }
9099
9100 ocs_memset(ctx, 0, sizeof(ocs_command_ctx_t));
9101 ocs_free(hw->os, ctx, sizeof(ocs_command_ctx_t));
9102
9103 return 0;
9104 }
9105
9106
9107
9108
9109 /**
9110 * @brief Process entries on the given mailbox queue.
9111 *
9112 * @param hw Hardware context.
9113 * @param status CQE status.
9114 * @param mq Pointer to the mailbox queue object.
9115 *
9116 * @return Returns 0 on success, or a non-zero value on failure.
9117 */
9118 static int32_t
ocs_hw_mq_process(ocs_hw_t * hw,int32_t status,sli4_queue_t * mq)9119 ocs_hw_mq_process(ocs_hw_t *hw, int32_t status, sli4_queue_t *mq)
9120 {
9121 uint8_t mqe[SLI4_BMBX_SIZE];
9122
9123 if (!sli_queue_read(&hw->sli, mq, mqe)) {
9124 ocs_hw_command_process(hw, status, mqe, mq->size);
9125 }
9126
9127 return 0;
9128 }
9129
9130 /**
9131 * @brief Read a FCF table entry.
9132 *
9133 * @param hw Hardware context.
9134 * @param index Table index to read. Use SLI4_FCOE_FCF_TABLE_FIRST for the first
9135 * read and the next_index field from the FCOE_READ_FCF_TABLE command
9136 * for subsequent reads.
9137 *
9138 * @return Returns 0 on success, or a non-zero value on failure.
9139 */
9140 static ocs_hw_rtn_e
ocs_hw_read_fcf(ocs_hw_t * hw,uint32_t index)9141 ocs_hw_read_fcf(ocs_hw_t *hw, uint32_t index)
9142 {
9143 uint8_t *buf = NULL;
9144 int32_t rc = OCS_HW_RTN_ERROR;
9145
9146 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9147 if (!buf) {
9148 ocs_log_err(hw->os, "no buffer for command\n");
9149 return OCS_HW_RTN_NO_MEMORY;
9150 }
9151
9152 if (sli_cmd_fcoe_read_fcf_table(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->fcf_dmem,
9153 index)) {
9154 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_read_fcf, &hw->fcf_dmem);
9155 }
9156
9157 if (rc != OCS_HW_RTN_SUCCESS) {
9158 ocs_log_test(hw->os, "FCOE_READ_FCF_TABLE failed\n");
9159 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9160 }
9161
9162 return rc;
9163 }
9164
9165 /**
9166 * @brief Callback function for the FCOE_READ_FCF_TABLE command.
9167 *
9168 * @par Description
9169 * Note that the caller has allocated:
9170 * - DMA memory to hold the table contents
9171 * - DMA memory structure
9172 * - Command/results buffer
9173 * .
9174 * Each of these must be freed here.
9175 *
9176 * @param hw Hardware context.
9177 * @param status Hardware status.
9178 * @param mqe Pointer to the mailbox command/results buffer.
9179 * @param arg Pointer to the DMA memory structure.
9180 *
9181 * @return Returns 0 on success, or a non-zero value on failure.
9182 */
9183 static int32_t
ocs_hw_cb_read_fcf(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9184 ocs_hw_cb_read_fcf(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9185 {
9186 ocs_dma_t *dma = arg;
9187 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
9188
9189 if (status || hdr->status) {
9190 ocs_log_test(hw->os, "bad status cqe=%#x mqe=%#x\n",
9191 status, hdr->status);
9192 } else if (dma->virt) {
9193 sli4_res_fcoe_read_fcf_table_t *read_fcf = dma->virt;
9194
9195 /* if FC or FCOE and FCF entry valid, process it */
9196 if (read_fcf->fcf_entry.fc ||
9197 (read_fcf->fcf_entry.val && !read_fcf->fcf_entry.sol)) {
9198 if (hw->callback.domain != NULL) {
9199 ocs_domain_record_t drec = {0};
9200
9201 if (read_fcf->fcf_entry.fc) {
9202 /*
9203 * This is a pseudo FCF entry. Create a domain
9204 * record based on the read topology information
9205 */
9206 drec.speed = hw->link.speed;
9207 drec.fc_id = hw->link.fc_id;
9208 drec.is_fc = TRUE;
9209 if (SLI_LINK_TOPO_LOOP == hw->link.topology) {
9210 drec.is_loop = TRUE;
9211 ocs_memcpy(drec.map.loop, hw->link.loop_map,
9212 sizeof(drec.map.loop));
9213 } else if (SLI_LINK_TOPO_NPORT == hw->link.topology) {
9214 drec.is_nport = TRUE;
9215 }
9216 } else {
9217 drec.index = read_fcf->fcf_entry.fcf_index;
9218 drec.priority = read_fcf->fcf_entry.fip_priority;
9219
9220 /* copy address, wwn and vlan_bitmap */
9221 ocs_memcpy(drec.address, read_fcf->fcf_entry.fcf_mac_address,
9222 sizeof(drec.address));
9223 ocs_memcpy(drec.wwn, read_fcf->fcf_entry.fabric_name_id,
9224 sizeof(drec.wwn));
9225 ocs_memcpy(drec.map.vlan, read_fcf->fcf_entry.vlan_bitmap,
9226 sizeof(drec.map.vlan));
9227
9228 drec.is_ethernet = TRUE;
9229 drec.is_nport = TRUE;
9230 }
9231
9232 hw->callback.domain(hw->args.domain,
9233 OCS_HW_DOMAIN_FOUND,
9234 &drec);
9235 }
9236 } else {
9237 /* if FCOE and FCF is not valid, ignore it */
9238 ocs_log_test(hw->os, "ignore invalid FCF entry\n");
9239 }
9240
9241 if (SLI4_FCOE_FCF_TABLE_LAST != read_fcf->next_index) {
9242 ocs_hw_read_fcf(hw, read_fcf->next_index);
9243 }
9244 }
9245
9246 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9247 //ocs_dma_free(hw->os, dma);
9248 //ocs_free(hw->os, dma, sizeof(ocs_dma_t));
9249
9250 return 0;
9251 }
9252
9253 /**
9254 * @brief Callback function for the SLI link events.
9255 *
9256 * @par Description
9257 * This function allocates memory which must be freed in its callback.
9258 *
9259 * @param ctx Hardware context pointer (that is, ocs_hw_t *).
9260 * @param e Event structure pointer (that is, sli4_link_event_t *).
9261 *
9262 * @return Returns 0 on success, or a non-zero value on failure.
9263 */
9264 static int32_t
ocs_hw_cb_link(void * ctx,void * e)9265 ocs_hw_cb_link(void *ctx, void *e)
9266 {
9267 ocs_hw_t *hw = ctx;
9268 sli4_link_event_t *event = e;
9269 ocs_domain_t *d = NULL;
9270 uint32_t i = 0;
9271 int32_t rc = OCS_HW_RTN_ERROR;
9272 ocs_t *ocs = hw->os;
9273
9274 ocs_hw_link_event_init(hw);
9275
9276 switch (event->status) {
9277 case SLI_LINK_STATUS_UP:
9278
9279 hw->link = *event;
9280
9281 if (SLI_LINK_TOPO_NPORT == event->topology) {
9282 device_printf(ocs->dev, "Link Up, NPORT, speed is %d\n", event->speed);
9283 ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9284 } else if (SLI_LINK_TOPO_LOOP == event->topology) {
9285 uint8_t *buf = NULL;
9286 device_printf(ocs->dev, "Link Up, LOOP, speed is %d\n", event->speed);
9287
9288 buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
9289 if (!buf) {
9290 ocs_log_err(hw->os, "no buffer for command\n");
9291 break;
9292 }
9293
9294 if (sli_cmd_read_topology(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->loop_map)) {
9295 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, __ocs_read_topology_cb, NULL);
9296 }
9297
9298 if (rc != OCS_HW_RTN_SUCCESS) {
9299 ocs_log_test(hw->os, "READ_TOPOLOGY failed\n");
9300 ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
9301 }
9302 } else {
9303 device_printf(ocs->dev, "Link Up, unsupported topology (%#x), speed is %d\n",
9304 event->topology, event->speed);
9305 }
9306 break;
9307 case SLI_LINK_STATUS_DOWN:
9308 device_printf(ocs->dev, "Link Down\n");
9309
9310 hw->link.status = event->status;
9311
9312 for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9313 if (d != NULL &&
9314 hw->callback.domain != NULL) {
9315 hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, d);
9316 }
9317 }
9318 break;
9319 default:
9320 ocs_log_test(hw->os, "unhandled link status %#x\n", event->status);
9321 break;
9322 }
9323
9324 return 0;
9325 }
9326
9327 static int32_t
ocs_hw_cb_fip(void * ctx,void * e)9328 ocs_hw_cb_fip(void *ctx, void *e)
9329 {
9330 ocs_hw_t *hw = ctx;
9331 ocs_domain_t *domain = NULL;
9332 sli4_fip_event_t *event = e;
9333
9334 /* Find the associated domain object */
9335 if (event->type == SLI4_FCOE_FIP_FCF_CLEAR_VLINK) {
9336 ocs_domain_t *d = NULL;
9337 uint32_t i = 0;
9338
9339 /* Clear VLINK is different from the other FIP events as it passes back
9340 * a VPI instead of a FCF index. Check all attached SLI ports for a
9341 * matching VPI */
9342 for (i = 0; d = hw->domains[i], i < SLI4_MAX_FCFI; i++) {
9343 if (d != NULL) {
9344 ocs_sport_t *sport = NULL;
9345
9346 ocs_list_foreach(&d->sport_list, sport) {
9347 if (sport->indicator == event->index) {
9348 domain = d;
9349 break;
9350 }
9351 }
9352
9353 if (domain != NULL) {
9354 break;
9355 }
9356 }
9357 }
9358 } else {
9359 domain = ocs_hw_domain_get_indexed(hw, event->index);
9360 }
9361
9362 switch (event->type) {
9363 case SLI4_FCOE_FIP_FCF_DISCOVERED:
9364 ocs_hw_read_fcf(hw, event->index);
9365 break;
9366 case SLI4_FCOE_FIP_FCF_DEAD:
9367 if (domain != NULL &&
9368 hw->callback.domain != NULL) {
9369 hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9370 }
9371 break;
9372 case SLI4_FCOE_FIP_FCF_CLEAR_VLINK:
9373 if (domain != NULL &&
9374 hw->callback.domain != NULL) {
9375 /*
9376 * We will want to issue rediscover FCF when this domain is free'd in order
9377 * to invalidate the FCF table
9378 */
9379 domain->req_rediscover_fcf = TRUE;
9380 hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9381 }
9382 break;
9383 case SLI4_FCOE_FIP_FCF_MODIFIED:
9384 if (domain != NULL &&
9385 hw->callback.domain != NULL) {
9386 hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
9387 }
9388
9389 ocs_hw_read_fcf(hw, event->index);
9390 break;
9391 default:
9392 ocs_log_test(hw->os, "unsupported event %#x\n", event->type);
9393 }
9394
9395 return 0;
9396 }
9397
9398 static int32_t
ocs_hw_cb_node_attach(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9399 ocs_hw_cb_node_attach(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9400 {
9401 ocs_remote_node_t *rnode = arg;
9402 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
9403 ocs_hw_remote_node_event_e evt = 0;
9404
9405 if (status || hdr->status) {
9406 ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9407 hdr->status);
9408 ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
9409 rnode->attached = FALSE;
9410 ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9411 evt = OCS_HW_NODE_ATTACH_FAIL;
9412 } else {
9413 rnode->attached = TRUE;
9414 ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 1);
9415 evt = OCS_HW_NODE_ATTACH_OK;
9416 }
9417
9418 if (hw->callback.rnode != NULL) {
9419 hw->callback.rnode(hw->args.rnode, evt, rnode);
9420 }
9421 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9422
9423 return 0;
9424 }
9425
9426 static int32_t
ocs_hw_cb_node_free(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9427 ocs_hw_cb_node_free(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9428 {
9429 ocs_remote_node_t *rnode = arg;
9430 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
9431 ocs_hw_remote_node_event_e evt = OCS_HW_NODE_FREE_FAIL;
9432 int32_t rc = 0;
9433
9434 if (status || hdr->status) {
9435 ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9436 hdr->status);
9437
9438 /*
9439 * In certain cases, a non-zero MQE status is OK (all must be true):
9440 * - node is attached
9441 * - if High Login Mode is enabled, node is part of a node group
9442 * - status is 0x1400
9443 */
9444 if (!rnode->attached || ((sli_get_hlm(&hw->sli) == TRUE) && !rnode->node_group) ||
9445 (hdr->status != SLI4_MBOX_STATUS_RPI_NOT_REG)) {
9446 rc = -1;
9447 }
9448 }
9449
9450 if (rc == 0) {
9451 rnode->node_group = FALSE;
9452 rnode->attached = FALSE;
9453
9454 if (ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_count) == 0) {
9455 ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
9456 }
9457
9458 evt = OCS_HW_NODE_FREE_OK;
9459 }
9460
9461 if (hw->callback.rnode != NULL) {
9462 hw->callback.rnode(hw->args.rnode, evt, rnode);
9463 }
9464
9465 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9466
9467 return rc;
9468 }
9469
9470 static int32_t
ocs_hw_cb_node_free_all(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9471 ocs_hw_cb_node_free_all(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9472 {
9473 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
9474 ocs_hw_remote_node_event_e evt = OCS_HW_NODE_FREE_FAIL;
9475 int32_t rc = 0;
9476 uint32_t i;
9477
9478 if (status || hdr->status) {
9479 ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
9480 hdr->status);
9481 } else {
9482 evt = OCS_HW_NODE_FREE_ALL_OK;
9483 }
9484
9485 if (evt == OCS_HW_NODE_FREE_ALL_OK) {
9486 for (i = 0; i < sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI); i++) {
9487 ocs_atomic_set(&hw->rpi_ref[i].rpi_count, 0);
9488 }
9489
9490 if (sli_resource_reset(&hw->sli, SLI_RSRC_FCOE_RPI)) {
9491 ocs_log_test(hw->os, "FCOE_RPI free all failure\n");
9492 rc = -1;
9493 }
9494 }
9495
9496 if (hw->callback.rnode != NULL) {
9497 hw->callback.rnode(hw->args.rnode, evt, NULL);
9498 }
9499
9500 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9501
9502 return rc;
9503 }
9504
9505 /**
9506 * @brief Initialize the pool of HW IO objects.
9507 *
9508 * @param hw Hardware context.
9509 *
9510 * @return Returns 0 on success, or a non-zero value on failure.
9511 */
9512 static ocs_hw_rtn_e
ocs_hw_setup_io(ocs_hw_t * hw)9513 ocs_hw_setup_io(ocs_hw_t *hw)
9514 {
9515 uint32_t i = 0;
9516 ocs_hw_io_t *io = NULL;
9517 uintptr_t xfer_virt = 0;
9518 uintptr_t xfer_phys = 0;
9519 uint32_t index;
9520 uint8_t new_alloc = TRUE;
9521
9522 if (NULL == hw->io) {
9523 hw->io = ocs_malloc(hw->os, hw->config.n_io * sizeof(ocs_hw_io_t *), OCS_M_ZERO | OCS_M_NOWAIT);
9524
9525 if (NULL == hw->io) {
9526 ocs_log_err(hw->os, "IO pointer memory allocation failed, %d Ios at size %zu\n",
9527 hw->config.n_io,
9528 sizeof(ocs_hw_io_t *));
9529 return OCS_HW_RTN_NO_MEMORY;
9530 }
9531 for (i = 0; i < hw->config.n_io; i++) {
9532 hw->io[i] = ocs_malloc(hw->os, sizeof(ocs_hw_io_t),
9533 OCS_M_ZERO | OCS_M_NOWAIT);
9534 if (hw->io[i] == NULL) {
9535 ocs_log_err(hw->os, "IO(%d) memory allocation failed\n", i);
9536 goto error;
9537 }
9538 }
9539
9540 /* Create WQE buffs for IO */
9541 hw->wqe_buffs = ocs_malloc(hw->os, hw->config.n_io * hw->sli.config.wqe_size,
9542 OCS_M_ZERO | OCS_M_NOWAIT);
9543 if (NULL == hw->wqe_buffs) {
9544 ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t));
9545 ocs_log_err(hw->os, "%s: IO WQE buff allocation failed, %d Ios at size %zu\n",
9546 __func__, hw->config.n_io, hw->sli.config.wqe_size);
9547 return OCS_HW_RTN_NO_MEMORY;
9548 }
9549
9550 } else {
9551 /* re-use existing IOs, including SGLs */
9552 new_alloc = FALSE;
9553 }
9554
9555 if (new_alloc) {
9556 if (ocs_dma_alloc(hw->os, &hw->xfer_rdy,
9557 sizeof(fcp_xfer_rdy_iu_t) * hw->config.n_io,
9558 4/*XXX what does this need to be? */)) {
9559 ocs_log_err(hw->os, "XFER_RDY buffer allocation failed\n");
9560 return OCS_HW_RTN_NO_MEMORY;
9561 }
9562 }
9563 xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
9564 xfer_phys = hw->xfer_rdy.phys;
9565
9566 for (i = 0; i < hw->config.n_io; i++) {
9567 hw_wq_callback_t *wqcb;
9568
9569 io = hw->io[i];
9570
9571 /* initialize IO fields */
9572 io->hw = hw;
9573
9574 /* Assign a WQE buff */
9575 io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.config.wqe_size];
9576
9577 /* Allocate the request tag for this IO */
9578 wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_io, io);
9579 if (wqcb == NULL) {
9580 ocs_log_err(hw->os, "can't allocate request tag\n");
9581 return OCS_HW_RTN_NO_RESOURCES;
9582 }
9583 io->reqtag = wqcb->instance_index;
9584
9585 /* Now for the fields that are initialized on each free */
9586 ocs_hw_init_free_io(io);
9587
9588 /* The XB flag isn't cleared on IO free, so initialize it to zero here */
9589 io->xbusy = 0;
9590
9591 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_XRI, &io->indicator, &index)) {
9592 ocs_log_err(hw->os, "sli_resource_alloc failed @ %d\n", i);
9593 return OCS_HW_RTN_NO_MEMORY;
9594 }
9595
9596 if (new_alloc && ocs_dma_alloc(hw->os, &io->def_sgl, hw->config.n_sgl * sizeof(sli4_sge_t), 64)) {
9597 ocs_log_err(hw->os, "ocs_dma_alloc failed @ %d\n", i);
9598 ocs_memset(&io->def_sgl, 0, sizeof(ocs_dma_t));
9599 return OCS_HW_RTN_NO_MEMORY;
9600 }
9601 io->def_sgl_count = hw->config.n_sgl;
9602 io->sgl = &io->def_sgl;
9603 io->sgl_count = io->def_sgl_count;
9604
9605 if (hw->xfer_rdy.size) {
9606 io->xfer_rdy.virt = (void *)xfer_virt;
9607 io->xfer_rdy.phys = xfer_phys;
9608 io->xfer_rdy.size = sizeof(fcp_xfer_rdy_iu_t);
9609
9610 xfer_virt += sizeof(fcp_xfer_rdy_iu_t);
9611 xfer_phys += sizeof(fcp_xfer_rdy_iu_t);
9612 }
9613 }
9614
9615 return OCS_HW_RTN_SUCCESS;
9616 error:
9617 for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
9618 ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
9619 hw->io[i] = NULL;
9620 }
9621
9622 return OCS_HW_RTN_NO_MEMORY;
9623 }
9624
9625 static ocs_hw_rtn_e
ocs_hw_init_io(ocs_hw_t * hw)9626 ocs_hw_init_io(ocs_hw_t *hw)
9627 {
9628 uint32_t i = 0, io_index = 0;
9629 uint32_t prereg = 0;
9630 ocs_hw_io_t *io = NULL;
9631 uint8_t cmd[SLI4_BMBX_SIZE];
9632 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
9633 uint32_t nremaining;
9634 uint32_t n = 0;
9635 uint32_t sgls_per_request = 256;
9636 ocs_dma_t **sgls = NULL;
9637 ocs_dma_t reqbuf = { 0 };
9638
9639 prereg = sli_get_sgl_preregister(&hw->sli);
9640
9641 if (prereg) {
9642 sgls = ocs_malloc(hw->os, sizeof(*sgls) * sgls_per_request, OCS_M_NOWAIT);
9643 if (sgls == NULL) {
9644 ocs_log_err(hw->os, "ocs_malloc sgls failed\n");
9645 return OCS_HW_RTN_NO_MEMORY;
9646 }
9647
9648 rc = ocs_dma_alloc(hw->os, &reqbuf, 32 + sgls_per_request*16, OCS_MIN_DMA_ALIGNMENT);
9649 if (rc) {
9650 ocs_log_err(hw->os, "ocs_dma_alloc reqbuf failed\n");
9651 ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9652 return OCS_HW_RTN_NO_MEMORY;
9653 }
9654 }
9655
9656 io = hw->io[io_index];
9657 for (nremaining = hw->config.n_io; nremaining; nremaining -= n) {
9658 if (prereg) {
9659 /* Copy address of SGL's into local sgls[] array, break out if the xri
9660 * is not contiguous.
9661 */
9662 for (n = 0; n < MIN(sgls_per_request, nremaining); n++) {
9663 /* Check that we have contiguous xri values */
9664 if (n > 0) {
9665 if (hw->io[io_index + n]->indicator != (hw->io[io_index + n-1]->indicator+1)) {
9666 break;
9667 }
9668 }
9669 sgls[n] = hw->io[io_index + n]->sgl;
9670 }
9671
9672 if (sli_cmd_fcoe_post_sgl_pages(&hw->sli, cmd, sizeof(cmd),
9673 io->indicator, n, sgls, NULL, &reqbuf)) {
9674 if (ocs_hw_command(hw, cmd, OCS_CMD_POLL, NULL, NULL)) {
9675 rc = OCS_HW_RTN_ERROR;
9676 ocs_log_err(hw->os, "SGL post failed\n");
9677 break;
9678 }
9679 }
9680 } else {
9681 n = nremaining;
9682 }
9683
9684 /* Add to tail if successful */
9685 for (i = 0; i < n; i ++) {
9686 io->is_port_owned = 0;
9687 io->state = OCS_HW_IO_STATE_FREE;
9688 ocs_list_add_tail(&hw->io_free, io);
9689 io = hw->io[io_index+1];
9690 io_index++;
9691 }
9692 }
9693
9694 if (prereg) {
9695 ocs_dma_free(hw->os, &reqbuf);
9696 ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
9697 }
9698
9699 return rc;
9700 }
9701
9702 static int32_t
ocs_hw_flush(ocs_hw_t * hw)9703 ocs_hw_flush(ocs_hw_t *hw)
9704 {
9705 uint32_t i = 0;
9706
9707 /* Process any remaining completions */
9708 for (i = 0; i < hw->eq_count; i++) {
9709 ocs_hw_process(hw, i, ~0);
9710 }
9711
9712 return 0;
9713 }
9714
9715 static int32_t
ocs_hw_command_cancel(ocs_hw_t * hw)9716 ocs_hw_command_cancel(ocs_hw_t *hw)
9717 {
9718
9719 ocs_lock(&hw->cmd_lock);
9720
9721 /*
9722 * Manually clean up remaining commands. Note: since this calls
9723 * ocs_hw_command_process(), we'll also process the cmd_pending
9724 * list, so no need to manually clean that out.
9725 */
9726 while (!ocs_list_empty(&hw->cmd_head)) {
9727 uint8_t mqe[SLI4_BMBX_SIZE] = { 0 };
9728 ocs_command_ctx_t *ctx = ocs_list_get_head(&hw->cmd_head);
9729
9730 ocs_log_test(hw->os, "hung command %08x\n",
9731 NULL == ctx ? UINT32_MAX :
9732 (NULL == ctx->buf ? UINT32_MAX : *((uint32_t *)ctx->buf)));
9733 ocs_unlock(&hw->cmd_lock);
9734 ocs_hw_command_process(hw, -1/*Bad status*/, mqe, SLI4_BMBX_SIZE);
9735 ocs_lock(&hw->cmd_lock);
9736 }
9737
9738 ocs_unlock(&hw->cmd_lock);
9739
9740 return 0;
9741 }
9742
9743 /**
9744 * @brief Find IO given indicator (xri).
9745 *
9746 * @param hw Hal context.
9747 * @param indicator Indicator (xri) to look for.
9748 *
9749 * @return Returns io if found, NULL otherwise.
9750 */
9751 ocs_hw_io_t *
ocs_hw_io_lookup(ocs_hw_t * hw,uint32_t xri)9752 ocs_hw_io_lookup(ocs_hw_t *hw, uint32_t xri)
9753 {
9754 uint32_t ioindex;
9755 ioindex = xri - hw->sli.config.extent[SLI_RSRC_FCOE_XRI].base[0];
9756 return hw->io[ioindex];
9757 }
9758
9759 /**
9760 * @brief Issue any pending callbacks for an IO and remove off the timer and pending lists.
9761 *
9762 * @param hw Hal context.
9763 * @param io Pointer to the IO to cleanup.
9764 */
9765 static void
ocs_hw_io_cancel_cleanup(ocs_hw_t * hw,ocs_hw_io_t * io)9766 ocs_hw_io_cancel_cleanup(ocs_hw_t *hw, ocs_hw_io_t *io)
9767 {
9768 ocs_hw_done_t done = io->done;
9769 ocs_hw_done_t abort_done = io->abort_done;
9770
9771 /* first check active_wqe list and remove if there */
9772 if (ocs_list_on_list(&io->wqe_link)) {
9773 ocs_list_remove(&hw->io_timed_wqe, io);
9774 }
9775
9776 /* Remove from WQ pending list */
9777 if ((io->wq != NULL) && ocs_list_on_list(&io->wq->pending_list)) {
9778 ocs_list_remove(&io->wq->pending_list, io);
9779 }
9780
9781 if (io->done) {
9782 void *arg = io->arg;
9783
9784 io->done = NULL;
9785 ocs_unlock(&hw->io_lock);
9786 done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, arg);
9787 ocs_lock(&hw->io_lock);
9788 }
9789
9790 if (io->abort_done != NULL) {
9791 void *abort_arg = io->abort_arg;
9792
9793 io->abort_done = NULL;
9794 ocs_unlock(&hw->io_lock);
9795 abort_done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, abort_arg);
9796 ocs_lock(&hw->io_lock);
9797 }
9798 }
9799
9800 static int32_t
ocs_hw_io_cancel(ocs_hw_t * hw)9801 ocs_hw_io_cancel(ocs_hw_t *hw)
9802 {
9803 ocs_hw_io_t *io = NULL;
9804 ocs_hw_io_t *tmp_io = NULL;
9805 uint32_t iters = 100; /* One second limit */
9806
9807 /*
9808 * Manually clean up outstanding IO.
9809 * Only walk through list once: the backend will cleanup any IOs when done/abort_done is called.
9810 */
9811 ocs_lock(&hw->io_lock);
9812 ocs_list_foreach_safe(&hw->io_inuse, io, tmp_io) {
9813 ocs_hw_done_t done = io->done;
9814 ocs_hw_done_t abort_done = io->abort_done;
9815
9816 ocs_hw_io_cancel_cleanup(hw, io);
9817
9818 /*
9819 * Since this is called in a reset/shutdown
9820 * case, If there is no callback, then just
9821 * free the IO.
9822 *
9823 * Note: A port owned XRI cannot be on
9824 * the in use list. We cannot call
9825 * ocs_hw_io_free() because we already
9826 * hold the io_lock.
9827 */
9828 if (done == NULL &&
9829 abort_done == NULL) {
9830 /*
9831 * Since this is called in a reset/shutdown
9832 * case, If there is no callback, then just
9833 * free the IO.
9834 */
9835 ocs_hw_io_free_common(hw, io);
9836 ocs_list_remove(&hw->io_inuse, io);
9837 ocs_hw_io_free_move_correct_list(hw, io);
9838 }
9839 }
9840
9841 /*
9842 * For port owned XRIs, they are not on the in use list, so
9843 * walk though XRIs and issue any callbacks.
9844 */
9845 ocs_list_foreach_safe(&hw->io_port_owned, io, tmp_io) {
9846 /* check list and remove if there */
9847 if (ocs_list_on_list(&io->dnrx_link)) {
9848 ocs_list_remove(&hw->io_port_dnrx, io);
9849 ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
9850 }
9851 ocs_hw_io_cancel_cleanup(hw, io);
9852 ocs_list_remove(&hw->io_port_owned, io);
9853 ocs_hw_io_free_common(hw, io);
9854 }
9855 ocs_unlock(&hw->io_lock);
9856
9857 /* Give time for the callbacks to complete */
9858 do {
9859 ocs_udelay(10000);
9860 iters--;
9861 } while (!ocs_list_empty(&hw->io_inuse) && iters);
9862
9863 /* Leave a breadcrumb that cleanup is not yet complete. */
9864 if (!ocs_list_empty(&hw->io_inuse)) {
9865 ocs_log_test(hw->os, "io_inuse list is not empty\n");
9866 }
9867
9868 return 0;
9869 }
9870
9871 static int32_t
ocs_hw_io_ini_sge(ocs_hw_t * hw,ocs_hw_io_t * io,ocs_dma_t * cmnd,uint32_t cmnd_size,ocs_dma_t * rsp)9872 ocs_hw_io_ini_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *cmnd, uint32_t cmnd_size,
9873 ocs_dma_t *rsp)
9874 {
9875 sli4_sge_t *data = NULL;
9876
9877 if (!hw || !io) {
9878 ocs_log_err(NULL, "bad parm hw=%p io=%p\n", hw, io);
9879 return OCS_HW_RTN_ERROR;
9880 }
9881
9882 data = io->def_sgl.virt;
9883
9884 /* setup command pointer */
9885 data->buffer_address_high = ocs_addr32_hi(cmnd->phys);
9886 data->buffer_address_low = ocs_addr32_lo(cmnd->phys);
9887 data->buffer_length = cmnd_size;
9888 data++;
9889
9890 /* setup response pointer */
9891 data->buffer_address_high = ocs_addr32_hi(rsp->phys);
9892 data->buffer_address_low = ocs_addr32_lo(rsp->phys);
9893 data->buffer_length = rsp->size;
9894
9895 return 0;
9896 }
9897
9898 static int32_t
__ocs_read_topology_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)9899 __ocs_read_topology_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
9900 {
9901 sli4_cmd_read_topology_t *read_topo = (sli4_cmd_read_topology_t *)mqe;
9902
9903 if (status || read_topo->hdr.status) {
9904 ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n",
9905 status, read_topo->hdr.status);
9906 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9907 return -1;
9908 }
9909
9910 switch (read_topo->attention_type) {
9911 case SLI4_READ_TOPOLOGY_LINK_UP:
9912 hw->link.status = SLI_LINK_STATUS_UP;
9913 break;
9914 case SLI4_READ_TOPOLOGY_LINK_DOWN:
9915 hw->link.status = SLI_LINK_STATUS_DOWN;
9916 break;
9917 case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
9918 hw->link.status = SLI_LINK_STATUS_NO_ALPA;
9919 break;
9920 default:
9921 hw->link.status = SLI_LINK_STATUS_MAX;
9922 break;
9923 }
9924
9925 switch (read_topo->topology) {
9926 case SLI4_READ_TOPOLOGY_NPORT:
9927 hw->link.topology = SLI_LINK_TOPO_NPORT;
9928 break;
9929 case SLI4_READ_TOPOLOGY_FC_AL:
9930 hw->link.topology = SLI_LINK_TOPO_LOOP;
9931 if (SLI_LINK_STATUS_UP == hw->link.status) {
9932 hw->link.loop_map = hw->loop_map.virt;
9933 }
9934 hw->link.fc_id = read_topo->acquired_al_pa;
9935 break;
9936 default:
9937 hw->link.topology = SLI_LINK_TOPO_MAX;
9938 break;
9939 }
9940
9941 hw->link.medium = SLI_LINK_MEDIUM_FC;
9942
9943 switch (read_topo->link_current.link_speed) {
9944 case SLI4_READ_TOPOLOGY_SPEED_1G:
9945 hw->link.speed = 1 * 1000;
9946 break;
9947 case SLI4_READ_TOPOLOGY_SPEED_2G:
9948 hw->link.speed = 2 * 1000;
9949 break;
9950 case SLI4_READ_TOPOLOGY_SPEED_4G:
9951 hw->link.speed = 4 * 1000;
9952 break;
9953 case SLI4_READ_TOPOLOGY_SPEED_8G:
9954 hw->link.speed = 8 * 1000;
9955 break;
9956 case SLI4_READ_TOPOLOGY_SPEED_16G:
9957 hw->link.speed = 16 * 1000;
9958 hw->link.loop_map = NULL;
9959 break;
9960 case SLI4_READ_TOPOLOGY_SPEED_32G:
9961 hw->link.speed = 32 * 1000;
9962 hw->link.loop_map = NULL;
9963 break;
9964 }
9965
9966 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
9967
9968 ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
9969
9970 return 0;
9971 }
9972
9973 static int32_t
__ocs_hw_port_common(const char * funcname,ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)9974 __ocs_hw_port_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
9975 {
9976 ocs_sli_port_t *sport = ctx->app;
9977 ocs_hw_t *hw = sport->hw;
9978
9979 smtrace("port");
9980
9981 switch (evt) {
9982 case OCS_EVT_EXIT:
9983 /* ignore */
9984 break;
9985
9986 case OCS_EVT_HW_PORT_REQ_FREE:
9987 case OCS_EVT_HW_PORT_REQ_ATTACH:
9988 if (data != NULL) {
9989 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
9990 }
9991 /* fall through */
9992 default:
9993 ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
9994 break;
9995 }
9996
9997 return 0;
9998 }
9999
10000 static void *
__ocs_hw_port_free_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10001 __ocs_hw_port_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10002 {
10003 ocs_sli_port_t *sport = ctx->app;
10004 ocs_hw_t *hw = sport->hw;
10005
10006 smtrace("port");
10007
10008 switch (evt) {
10009 case OCS_EVT_ENTER:
10010 if (data != NULL) {
10011 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10012 }
10013 if (hw->callback.port != NULL) {
10014 hw->callback.port(hw->args.port,
10015 OCS_HW_PORT_FREE_FAIL, sport);
10016 }
10017 break;
10018 default:
10019 break;
10020 }
10021
10022 return NULL;
10023 }
10024
10025 static void *
__ocs_hw_port_freed(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10026 __ocs_hw_port_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10027 {
10028 ocs_sli_port_t *sport = ctx->app;
10029 ocs_hw_t *hw = sport->hw;
10030
10031 smtrace("port");
10032
10033 switch (evt) {
10034 case OCS_EVT_ENTER:
10035 /* free SLI resource */
10036 if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator)) {
10037 ocs_log_err(hw->os, "FCOE_VPI free failure addr=%#x\n", sport->fc_id);
10038 }
10039
10040 /* free mailbox buffer */
10041 if (data != NULL) {
10042 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10043 }
10044 if (hw->callback.port != NULL) {
10045 hw->callback.port(hw->args.port,
10046 OCS_HW_PORT_FREE_OK, sport);
10047 }
10048 break;
10049 default:
10050 break;
10051 }
10052
10053 return NULL;
10054 }
10055
10056 static void *
__ocs_hw_port_attach_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10057 __ocs_hw_port_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10058 {
10059 ocs_sli_port_t *sport = ctx->app;
10060 ocs_hw_t *hw = sport->hw;
10061
10062 smtrace("port");
10063
10064 switch (evt) {
10065 case OCS_EVT_ENTER:
10066 /* free SLI resource */
10067 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10068
10069 /* free mailbox buffer */
10070 if (data != NULL) {
10071 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10072 }
10073
10074 if (hw->callback.port != NULL) {
10075 hw->callback.port(hw->args.port,
10076 OCS_HW_PORT_ATTACH_FAIL, sport);
10077 }
10078 if (sport->sm_free_req_pending) {
10079 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10080 }
10081 break;
10082 default:
10083 __ocs_hw_port_common(__func__, ctx, evt, data);
10084 break;
10085 }
10086
10087 return NULL;
10088 }
10089
10090 static void *
__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10091 __ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10092 {
10093 ocs_sli_port_t *sport = ctx->app;
10094 ocs_hw_t *hw = sport->hw;
10095 uint8_t *cmd = NULL;
10096
10097 smtrace("port");
10098
10099 switch (evt) {
10100 case OCS_EVT_ENTER:
10101 /* allocate memory and send unreg_vpi */
10102 cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10103 if (!cmd) {
10104 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10105 break;
10106 }
10107
10108 if (0 == sli_cmd_unreg_vpi(&hw->sli, cmd, SLI4_BMBX_SIZE, sport->indicator,
10109 SLI4_UNREG_TYPE_PORT)) {
10110 ocs_log_err(hw->os, "UNREG_VPI format failure\n");
10111 ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10112 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10113 break;
10114 }
10115
10116 if (ocs_hw_command(hw, cmd, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10117 ocs_log_err(hw->os, "UNREG_VPI command failure\n");
10118 ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
10119 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10120 break;
10121 }
10122 break;
10123 case OCS_EVT_RESPONSE:
10124 ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10125 break;
10126 case OCS_EVT_ERROR:
10127 ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10128 break;
10129 default:
10130 __ocs_hw_port_common(__func__, ctx, evt, data);
10131 break;
10132 }
10133
10134 return NULL;
10135 }
10136
10137 static void *
__ocs_hw_port_free_nop(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10138 __ocs_hw_port_free_nop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10139 {
10140 ocs_sli_port_t *sport = ctx->app;
10141 ocs_hw_t *hw = sport->hw;
10142
10143 smtrace("port");
10144
10145 switch (evt) {
10146 case OCS_EVT_ENTER:
10147 /* Forward to execute in mailbox completion processing context */
10148 if (ocs_hw_async_call(hw, __ocs_hw_port_realloc_cb, sport)) {
10149 ocs_log_err(hw->os, "ocs_hw_async_call failed\n");
10150 }
10151 break;
10152 case OCS_EVT_RESPONSE:
10153 ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
10154 break;
10155 case OCS_EVT_ERROR:
10156 ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
10157 break;
10158 default:
10159 break;
10160 }
10161
10162 return NULL;
10163 }
10164
10165 static void *
__ocs_hw_port_attached(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10166 __ocs_hw_port_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10167 {
10168 ocs_sli_port_t *sport = ctx->app;
10169 ocs_hw_t *hw = sport->hw;
10170
10171 smtrace("port");
10172
10173 switch (evt) {
10174 case OCS_EVT_ENTER:
10175 if (data != NULL) {
10176 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10177 }
10178 if (hw->callback.port != NULL) {
10179 hw->callback.port(hw->args.port,
10180 OCS_HW_PORT_ATTACH_OK, sport);
10181 }
10182 if (sport->sm_free_req_pending) {
10183 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10184 }
10185 break;
10186 case OCS_EVT_HW_PORT_REQ_FREE:
10187 /* virtual/physical port request free */
10188 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10189 break;
10190 default:
10191 __ocs_hw_port_common(__func__, ctx, evt, data);
10192 break;
10193 }
10194
10195 return NULL;
10196 }
10197
10198 static void *
__ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10199 __ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10200 {
10201 ocs_sli_port_t *sport = ctx->app;
10202 ocs_hw_t *hw = sport->hw;
10203
10204 smtrace("port");
10205
10206 switch (evt) {
10207 case OCS_EVT_ENTER:
10208 if (0 == sli_cmd_reg_vpi(&hw->sli, data, SLI4_BMBX_SIZE, sport, FALSE)) {
10209 ocs_log_err(hw->os, "REG_VPI format failure\n");
10210 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10211 break;
10212 }
10213
10214 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10215 ocs_log_err(hw->os, "REG_VPI command failure\n");
10216 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10217 break;
10218 }
10219 break;
10220 case OCS_EVT_RESPONSE:
10221 ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10222 break;
10223 case OCS_EVT_ERROR:
10224 ocs_sm_transition(ctx, __ocs_hw_port_attach_report_fail, data);
10225 break;
10226 case OCS_EVT_HW_PORT_REQ_FREE:
10227 /* Wait for attach response and then free */
10228 sport->sm_free_req_pending = 1;
10229 break;
10230 default:
10231 __ocs_hw_port_common(__func__, ctx, evt, data);
10232 break;
10233 }
10234
10235 return NULL;
10236 }
10237
10238 static void *
__ocs_hw_port_done(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10239 __ocs_hw_port_done(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10240 {
10241 ocs_sli_port_t *sport = ctx->app;
10242 ocs_hw_t *hw = sport->hw;
10243
10244 smtrace("port");
10245
10246 switch (evt) {
10247 case OCS_EVT_ENTER:
10248 /* free SLI resource */
10249 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10250
10251 /* free mailbox buffer */
10252 if (data != NULL) {
10253 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10254 }
10255 break;
10256 default:
10257 __ocs_hw_port_common(__func__, ctx, evt, data);
10258 break;
10259 }
10260
10261 return NULL;
10262 }
10263
10264 static void *
__ocs_hw_port_allocated(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10265 __ocs_hw_port_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10266 {
10267 ocs_sli_port_t *sport = ctx->app;
10268 ocs_hw_t *hw = sport->hw;
10269
10270 smtrace("port");
10271
10272 switch (evt) {
10273 case OCS_EVT_ENTER:
10274 if (data != NULL) {
10275 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10276 }
10277 if (hw->callback.port != NULL) {
10278 hw->callback.port(hw->args.port,
10279 OCS_HW_PORT_ALLOC_OK, sport);
10280 }
10281 /* If there is a pending free request, then handle it now */
10282 if (sport->sm_free_req_pending) {
10283 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10284 }
10285 break;
10286 case OCS_EVT_HW_PORT_REQ_ATTACH:
10287 /* virtual port requests attach */
10288 ocs_sm_transition(ctx, __ocs_hw_port_attach_reg_vpi, data);
10289 break;
10290 case OCS_EVT_HW_PORT_ATTACH_OK:
10291 /* physical port attached (as part of attaching domain) */
10292 ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
10293 break;
10294 case OCS_EVT_HW_PORT_REQ_FREE:
10295 /* virtual port request free */
10296 if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
10297 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10298 } else {
10299 /*
10300 * Note: BE3/Skyhawk will respond with a status of 0x20
10301 * unless the reg_vpi has been issued, so we can
10302 * skip the unreg_vpi for these adapters.
10303 *
10304 * Send a nop to make sure that free doesn't occur in
10305 * same context
10306 */
10307 ocs_sm_transition(ctx, __ocs_hw_port_free_nop, NULL);
10308 }
10309 break;
10310 default:
10311 __ocs_hw_port_common(__func__, ctx, evt, data);
10312 break;
10313 }
10314
10315 return NULL;
10316 }
10317
10318 static void *
__ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10319 __ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10320 {
10321 ocs_sli_port_t *sport = ctx->app;
10322 ocs_hw_t *hw = sport->hw;
10323
10324 smtrace("port");
10325
10326 switch (evt) {
10327 case OCS_EVT_ENTER:
10328 /* free SLI resource */
10329 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
10330
10331 /* free mailbox buffer */
10332 if (data != NULL) {
10333 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10334 }
10335
10336 if (hw->callback.port != NULL) {
10337 hw->callback.port(hw->args.port,
10338 OCS_HW_PORT_ALLOC_FAIL, sport);
10339 }
10340
10341 /* If there is a pending free request, then handle it now */
10342 if (sport->sm_free_req_pending) {
10343 ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
10344 }
10345 break;
10346 default:
10347 __ocs_hw_port_common(__func__, ctx, evt, data);
10348 break;
10349 }
10350
10351 return NULL;
10352 }
10353
10354 static void *
__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10355 __ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10356 {
10357 ocs_sli_port_t *sport = ctx->app;
10358 ocs_hw_t *hw = sport->hw;
10359 uint8_t *payload = NULL;
10360
10361 smtrace("port");
10362
10363 switch (evt) {
10364 case OCS_EVT_ENTER:
10365 /* allocate memory for the service parameters */
10366 if (ocs_dma_alloc(hw->os, &sport->dma, 112, 4)) {
10367 ocs_log_err(hw->os, "Failed to allocate DMA memory\n");
10368 ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10369 break;
10370 }
10371
10372 if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10373 &sport->dma, sport->indicator)) {
10374 ocs_log_err(hw->os, "READ_SPARM64 allocation failure\n");
10375 ocs_dma_free(hw->os, &sport->dma);
10376 ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10377 break;
10378 }
10379
10380 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10381 ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10382 ocs_dma_free(hw->os, &sport->dma);
10383 ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10384 break;
10385 }
10386 break;
10387 case OCS_EVT_RESPONSE:
10388 payload = sport->dma.virt;
10389
10390 ocs_display_sparams(sport->display_name, "sport sparm64", 0, NULL, payload);
10391
10392 ocs_memcpy(&sport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
10393 sizeof(sport->sli_wwpn));
10394 ocs_memcpy(&sport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
10395 sizeof(sport->sli_wwnn));
10396
10397 ocs_dma_free(hw->os, &sport->dma);
10398 ocs_sm_transition(ctx, __ocs_hw_port_alloc_init_vpi, data);
10399 break;
10400 case OCS_EVT_ERROR:
10401 ocs_dma_free(hw->os, &sport->dma);
10402 ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10403 break;
10404 case OCS_EVT_HW_PORT_REQ_FREE:
10405 /* Wait for attach response and then free */
10406 sport->sm_free_req_pending = 1;
10407 break;
10408 case OCS_EVT_EXIT:
10409 break;
10410 default:
10411 __ocs_hw_port_common(__func__, ctx, evt, data);
10412 break;
10413 }
10414
10415 return NULL;
10416 }
10417
10418 static void *
__ocs_hw_port_alloc_init(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10419 __ocs_hw_port_alloc_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10420 {
10421 ocs_sli_port_t *sport = ctx->app;
10422
10423 smtrace("port");
10424
10425 switch (evt) {
10426 case OCS_EVT_ENTER:
10427 /* no-op */
10428 break;
10429 case OCS_EVT_HW_PORT_ALLOC_OK:
10430 ocs_sm_transition(ctx, __ocs_hw_port_allocated, NULL);
10431 break;
10432 case OCS_EVT_HW_PORT_ALLOC_FAIL:
10433 ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, NULL);
10434 break;
10435 case OCS_EVT_HW_PORT_REQ_FREE:
10436 /* Wait for attach response and then free */
10437 sport->sm_free_req_pending = 1;
10438 break;
10439 default:
10440 __ocs_hw_port_common(__func__, ctx, evt, data);
10441 break;
10442 }
10443
10444 return NULL;
10445 }
10446
10447 static void *
__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10448 __ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10449 {
10450 ocs_sli_port_t *sport = ctx->app;
10451 ocs_hw_t *hw = sport->hw;
10452
10453 smtrace("port");
10454
10455 switch (evt) {
10456 case OCS_EVT_ENTER:
10457 /* If there is a pending free request, then handle it now */
10458 if (sport->sm_free_req_pending) {
10459 ocs_sm_transition(ctx, __ocs_hw_port_freed, NULL);
10460 return NULL;
10461 }
10462
10463 /* TODO XXX transitioning to done only works if this is called
10464 * directly from ocs_hw_port_alloc BUT not if called from
10465 * read_sparm64. In the later case, we actually want to go
10466 * through report_ok/fail
10467 */
10468 if (0 == sli_cmd_init_vpi(&hw->sli, data, SLI4_BMBX_SIZE,
10469 sport->indicator, sport->domain->indicator)) {
10470 ocs_log_err(hw->os, "INIT_VPI allocation failure\n");
10471 ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10472 break;
10473 }
10474
10475 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
10476 ocs_log_err(hw->os, "INIT_VPI command failure\n");
10477 ocs_sm_transition(ctx, __ocs_hw_port_done, data);
10478 break;
10479 }
10480 break;
10481 case OCS_EVT_RESPONSE:
10482 ocs_sm_transition(ctx, __ocs_hw_port_allocated, data);
10483 break;
10484 case OCS_EVT_ERROR:
10485 ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
10486 break;
10487 case OCS_EVT_HW_PORT_REQ_FREE:
10488 /* Wait for attach response and then free */
10489 sport->sm_free_req_pending = 1;
10490 break;
10491 case OCS_EVT_EXIT:
10492 break;
10493 default:
10494 __ocs_hw_port_common(__func__, ctx, evt, data);
10495 break;
10496 }
10497
10498 return NULL;
10499 }
10500
10501 static int32_t
__ocs_hw_port_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)10502 __ocs_hw_port_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10503 {
10504 ocs_sli_port_t *sport = arg;
10505 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
10506 ocs_sm_event_t evt;
10507
10508 if (status || hdr->status) {
10509 ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10510 sport->indicator, status, hdr->status);
10511 evt = OCS_EVT_ERROR;
10512 } else {
10513 evt = OCS_EVT_RESPONSE;
10514 }
10515
10516 ocs_sm_post_event(&sport->ctx, evt, mqe);
10517
10518 return 0;
10519 }
10520
10521 static int32_t
__ocs_hw_port_realloc_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)10522 __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
10523 {
10524 ocs_sli_port_t *sport = arg;
10525 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
10526 ocs_sm_event_t evt;
10527 uint8_t *mqecpy;
10528
10529 if (status || hdr->status) {
10530 ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
10531 sport->indicator, status, hdr->status);
10532 evt = OCS_EVT_ERROR;
10533 } else {
10534 evt = OCS_EVT_RESPONSE;
10535 }
10536
10537 /*
10538 * In this case we have to malloc a mailbox command buffer, as it is reused
10539 * in the state machine post event call, and eventually freed
10540 */
10541 mqecpy = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
10542 if (mqecpy == NULL) {
10543 ocs_log_err(hw->os, "malloc mqecpy failed\n");
10544 return -1;
10545 }
10546 ocs_memcpy(mqecpy, mqe, SLI4_BMBX_SIZE);
10547
10548 ocs_sm_post_event(&sport->ctx, evt, mqecpy);
10549
10550 return 0;
10551 }
10552
10553 /***************************************************************************
10554 * Domain state machine
10555 */
10556
10557 static int32_t
__ocs_hw_domain_common(const char * funcname,ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10558 __ocs_hw_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10559 {
10560 ocs_domain_t *domain = ctx->app;
10561 ocs_hw_t *hw = domain->hw;
10562
10563 smtrace("domain");
10564
10565 switch (evt) {
10566 case OCS_EVT_EXIT:
10567 /* ignore */
10568 break;
10569
10570 default:
10571 ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
10572 break;
10573 }
10574
10575 return 0;
10576 }
10577
10578 static void *
__ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10579 __ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10580 {
10581 ocs_domain_t *domain = ctx->app;
10582 ocs_hw_t *hw = domain->hw;
10583
10584 smtrace("domain");
10585
10586 switch (evt) {
10587 case OCS_EVT_ENTER:
10588 /* free command buffer */
10589 if (data != NULL) {
10590 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10591 }
10592 /* free SLI resources */
10593 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10594 /* TODO how to free FCFI (or do we at all)? */
10595
10596 if (hw->callback.domain != NULL) {
10597 hw->callback.domain(hw->args.domain,
10598 OCS_HW_DOMAIN_ALLOC_FAIL,
10599 domain);
10600 }
10601 break;
10602 default:
10603 __ocs_hw_domain_common(__func__, ctx, evt, data);
10604 break;
10605 }
10606
10607 return NULL;
10608 }
10609
10610 static void *
__ocs_hw_domain_attached(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10611 __ocs_hw_domain_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10612 {
10613 ocs_domain_t *domain = ctx->app;
10614 ocs_hw_t *hw = domain->hw;
10615
10616 smtrace("domain");
10617
10618 switch (evt) {
10619 case OCS_EVT_ENTER:
10620 /* free mailbox buffer and send alloc ok to physical sport */
10621 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10622 ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ATTACH_OK, NULL);
10623
10624 /* now inform registered callbacks */
10625 if (hw->callback.domain != NULL) {
10626 hw->callback.domain(hw->args.domain,
10627 OCS_HW_DOMAIN_ATTACH_OK,
10628 domain);
10629 }
10630 break;
10631 case OCS_EVT_HW_DOMAIN_REQ_FREE:
10632 ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10633 break;
10634 default:
10635 __ocs_hw_domain_common(__func__, ctx, evt, data);
10636 break;
10637 }
10638
10639 return NULL;
10640 }
10641
10642 static void *
__ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10643 __ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10644 {
10645 ocs_domain_t *domain = ctx->app;
10646 ocs_hw_t *hw = domain->hw;
10647
10648 smtrace("domain");
10649
10650 switch (evt) {
10651 case OCS_EVT_ENTER:
10652 if (data != NULL) {
10653 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10654 }
10655 /* free SLI resources */
10656 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
10657 /* TODO how to free FCFI (or do we at all)? */
10658
10659 if (hw->callback.domain != NULL) {
10660 hw->callback.domain(hw->args.domain,
10661 OCS_HW_DOMAIN_ATTACH_FAIL,
10662 domain);
10663 }
10664 break;
10665 case OCS_EVT_EXIT:
10666 break;
10667 default:
10668 __ocs_hw_domain_common(__func__, ctx, evt, data);
10669 break;
10670 }
10671
10672 return NULL;
10673 }
10674
10675 static void *
__ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10676 __ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10677 {
10678 ocs_domain_t *domain = ctx->app;
10679 ocs_hw_t *hw = domain->hw;
10680
10681 smtrace("domain");
10682
10683 switch (evt) {
10684 case OCS_EVT_ENTER:
10685
10686 ocs_display_sparams("", "reg vpi", 0, NULL, domain->dma.virt);
10687
10688 if (0 == sli_cmd_reg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain)) {
10689 ocs_log_err(hw->os, "REG_VFI format failure\n");
10690 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10691 break;
10692 }
10693
10694 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10695 ocs_log_err(hw->os, "REG_VFI command failure\n");
10696 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10697 break;
10698 }
10699 break;
10700 case OCS_EVT_RESPONSE:
10701 ocs_sm_transition(ctx, __ocs_hw_domain_attached, data);
10702 break;
10703 case OCS_EVT_ERROR:
10704 ocs_sm_transition(ctx, __ocs_hw_domain_attach_report_fail, data);
10705 break;
10706 default:
10707 __ocs_hw_domain_common(__func__, ctx, evt, data);
10708 break;
10709 }
10710
10711 return NULL;
10712 }
10713
10714 static void *
__ocs_hw_domain_allocated(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10715 __ocs_hw_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10716 {
10717 ocs_domain_t *domain = ctx->app;
10718 ocs_hw_t *hw = domain->hw;
10719
10720 smtrace("domain");
10721
10722 switch (evt) {
10723 case OCS_EVT_ENTER:
10724 /* free mailbox buffer and send alloc ok to physical sport */
10725 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
10726 ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ALLOC_OK, NULL);
10727
10728 ocs_hw_domain_add(hw, domain);
10729
10730 /* now inform registered callbacks */
10731 if (hw->callback.domain != NULL) {
10732 hw->callback.domain(hw->args.domain,
10733 OCS_HW_DOMAIN_ALLOC_OK,
10734 domain);
10735 }
10736 break;
10737 case OCS_EVT_HW_DOMAIN_REQ_ATTACH:
10738 ocs_sm_transition(ctx, __ocs_hw_domain_attach_reg_vfi, data);
10739 break;
10740 case OCS_EVT_HW_DOMAIN_REQ_FREE:
10741 /* unreg_fcfi/vfi */
10742 if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10743 ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, NULL);
10744 } else {
10745 ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
10746 }
10747 break;
10748 default:
10749 __ocs_hw_domain_common(__func__, ctx, evt, data);
10750 break;
10751 }
10752
10753 return NULL;
10754 }
10755
10756 static void *
__ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10757 __ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10758 {
10759 ocs_domain_t *domain = ctx->app;
10760 ocs_hw_t *hw = domain->hw;
10761
10762 smtrace("domain");
10763
10764 switch (evt) {
10765 case OCS_EVT_ENTER:
10766 if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
10767 &domain->dma, SLI4_READ_SPARM64_VPI_DEFAULT)) {
10768 ocs_log_err(hw->os, "READ_SPARM64 format failure\n");
10769 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10770 break;
10771 }
10772
10773 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10774 ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
10775 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10776 break;
10777 }
10778 break;
10779 case OCS_EVT_EXIT:
10780 break;
10781 case OCS_EVT_RESPONSE:
10782 ocs_display_sparams(domain->display_name, "domain sparm64", 0, NULL, domain->dma.virt);
10783
10784 ocs_sm_transition(ctx, __ocs_hw_domain_allocated, data);
10785 break;
10786 case OCS_EVT_ERROR:
10787 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10788 break;
10789 default:
10790 __ocs_hw_domain_common(__func__, ctx, evt, data);
10791 break;
10792 }
10793
10794 return NULL;
10795 }
10796
10797 static void *
__ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10798 __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10799 {
10800 ocs_domain_t *domain = ctx->app;
10801 ocs_sli_port_t *sport = domain->sport;
10802 ocs_hw_t *hw = domain->hw;
10803
10804 smtrace("domain");
10805
10806 switch (evt) {
10807 case OCS_EVT_ENTER:
10808 if (0 == sli_cmd_init_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->indicator,
10809 domain->fcf_indicator, sport->indicator)) {
10810 ocs_log_err(hw->os, "INIT_VFI format failure\n");
10811 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10812 break;
10813 }
10814 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10815 ocs_log_err(hw->os, "INIT_VFI command failure\n");
10816 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10817 break;
10818 }
10819 break;
10820 case OCS_EVT_EXIT:
10821 break;
10822 case OCS_EVT_RESPONSE:
10823 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10824 break;
10825 case OCS_EVT_ERROR:
10826 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10827 break;
10828 default:
10829 __ocs_hw_domain_common(__func__, ctx, evt, data);
10830 break;
10831 }
10832
10833 return NULL;
10834 }
10835
10836 static void *
__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10837 __ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10838 {
10839 ocs_domain_t *domain = ctx->app;
10840 ocs_hw_t *hw = domain->hw;
10841
10842 smtrace("domain");
10843
10844 switch (evt) {
10845 case OCS_EVT_ENTER: {
10846 sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
10847 uint32_t i;
10848
10849 /* Set the filter match/mask values from hw's filter_def values */
10850 for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
10851 rq_cfg[i].rq_id = 0xffff;
10852 rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
10853 rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
10854 rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
10855 rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
10856 }
10857
10858 /* Set the rq_id for each, in order of RQ definition */
10859 for (i = 0; i < hw->hw_rq_count; i++) {
10860 if (i >= ARRAY_SIZE(rq_cfg)) {
10861 ocs_log_warn(hw->os, "more RQs than REG_FCFI filter entries\n");
10862 break;
10863 }
10864 rq_cfg[i].rq_id = hw->hw_rq[i]->hdr->id;
10865 }
10866
10867 if (!data) {
10868 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10869 break;
10870 }
10871
10872 if (hw->hw_mrq_count) {
10873 if (OCS_HW_RTN_SUCCESS != ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE,
10874 domain->vlan_id, domain->fcf)) {
10875 ocs_log_err(hw->os, "REG_FCFI_MRQ format failure\n");
10876 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10877 break;
10878 }
10879
10880 } else {
10881 if (0 == sli_cmd_reg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf,
10882 rq_cfg, domain->vlan_id)) {
10883 ocs_log_err(hw->os, "REG_FCFI format failure\n");
10884 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10885 break;
10886 }
10887 }
10888
10889 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
10890 ocs_log_err(hw->os, "REG_FCFI command failure\n");
10891 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10892 break;
10893 }
10894 break;
10895 }
10896 case OCS_EVT_EXIT:
10897 break;
10898 case OCS_EVT_RESPONSE:
10899 if (!data) {
10900 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
10901 break;
10902 }
10903
10904 domain->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)data)->fcfi;
10905
10906 /*
10907 * IF_TYPE 0 devices do not support explicit VFI and VPI initialization
10908 * and instead rely on implicit initialization during VFI registration.
10909 * Short circuit normal processing here for those devices.
10910 */
10911 if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
10912 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
10913 } else {
10914 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_init_vfi, data);
10915 }
10916 break;
10917 case OCS_EVT_ERROR:
10918 ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
10919 break;
10920 default:
10921 __ocs_hw_domain_common(__func__, ctx, evt, data);
10922 break;
10923 }
10924
10925 return NULL;
10926 }
10927
10928 static void *
__ocs_hw_domain_init(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10929 __ocs_hw_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10930 {
10931 ocs_domain_t *domain = ctx->app;
10932 ocs_hw_t *hw = domain->hw;
10933
10934 smtrace("domain");
10935
10936 switch (evt) {
10937 case OCS_EVT_ENTER:
10938 if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
10939 /*
10940 * For FC, the HW alread registered a FCFI
10941 * Copy FCF information into the domain and jump to INIT_VFI
10942 */
10943 domain->fcf_indicator = hw->fcf_indicator;
10944 ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_init_vfi, data);
10945 } else {
10946 ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_reg_fcfi, data);
10947 }
10948 break;
10949 default:
10950 __ocs_hw_domain_common(__func__, ctx, evt, data);
10951 break;
10952 }
10953
10954 return NULL;
10955 }
10956
10957 static void *
__ocs_hw_domain_free_report_fail(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10958 __ocs_hw_domain_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10959 {
10960 ocs_domain_t *domain = ctx->app;
10961
10962 smtrace("domain");
10963
10964 switch (evt) {
10965 case OCS_EVT_ENTER:
10966 if (domain != NULL) {
10967 ocs_hw_t *hw = domain->hw;
10968
10969 ocs_hw_domain_del(hw, domain);
10970
10971 if (hw->callback.domain != NULL) {
10972 hw->callback.domain(hw->args.domain,
10973 OCS_HW_DOMAIN_FREE_FAIL,
10974 domain);
10975 }
10976 }
10977
10978 /* free command buffer */
10979 if (data != NULL) {
10980 ocs_free(domain != NULL ? domain->hw->os : NULL, data, SLI4_BMBX_SIZE);
10981 }
10982 break;
10983 case OCS_EVT_EXIT:
10984 break;
10985 default:
10986 __ocs_hw_domain_common(__func__, ctx, evt, data);
10987 break;
10988 }
10989
10990 return NULL;
10991 }
10992
10993 static void *
__ocs_hw_domain_freed(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)10994 __ocs_hw_domain_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
10995 {
10996 ocs_domain_t *domain = ctx->app;
10997
10998 smtrace("domain");
10999
11000 switch (evt) {
11001 case OCS_EVT_ENTER:
11002 /* Free DMA and mailbox buffer */
11003 if (domain != NULL) {
11004 ocs_hw_t *hw = domain->hw;
11005
11006 /* free VFI resource */
11007 sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI,
11008 domain->indicator);
11009
11010 ocs_hw_domain_del(hw, domain);
11011
11012 /* inform registered callbacks */
11013 if (hw->callback.domain != NULL) {
11014 hw->callback.domain(hw->args.domain,
11015 OCS_HW_DOMAIN_FREE_OK,
11016 domain);
11017 }
11018 }
11019 if (data != NULL) {
11020 ocs_free(NULL, data, SLI4_BMBX_SIZE);
11021 }
11022 break;
11023 case OCS_EVT_EXIT:
11024 break;
11025 default:
11026 __ocs_hw_domain_common(__func__, ctx, evt, data);
11027 break;
11028 }
11029
11030 return NULL;
11031 }
11032
11033
11034 static void *
__ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11035 __ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11036 {
11037 ocs_domain_t *domain = ctx->app;
11038 ocs_hw_t *hw = domain->hw;
11039
11040 smtrace("domain");
11041
11042 switch (evt) {
11043 case OCS_EVT_ENTER:
11044 /* if we're in the middle of a teardown, skip sending rediscover */
11045 if (hw->state == OCS_HW_STATE_TEARDOWN_IN_PROGRESS) {
11046 ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11047 break;
11048 }
11049 if (0 == sli_cmd_fcoe_rediscover_fcf(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf)) {
11050 ocs_log_err(hw->os, "REDISCOVER_FCF format failure\n");
11051 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11052 break;
11053 }
11054
11055 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11056 ocs_log_err(hw->os, "REDISCOVER_FCF command failure\n");
11057 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11058 }
11059 break;
11060 case OCS_EVT_RESPONSE:
11061 case OCS_EVT_ERROR:
11062 /* REDISCOVER_FCF can fail if none exist */
11063 ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11064 break;
11065 case OCS_EVT_EXIT:
11066 break;
11067 default:
11068 __ocs_hw_domain_common(__func__, ctx, evt, data);
11069 break;
11070 }
11071
11072 return NULL;
11073 }
11074
11075 static void *
__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11076 __ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11077 {
11078 ocs_domain_t *domain = ctx->app;
11079 ocs_hw_t *hw = domain->hw;
11080
11081 smtrace("domain");
11082
11083 switch (evt) {
11084 case OCS_EVT_ENTER:
11085 if (data == NULL) {
11086 data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11087 if (!data) {
11088 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11089 break;
11090 }
11091 }
11092
11093 if (0 == sli_cmd_unreg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf_indicator)) {
11094 ocs_log_err(hw->os, "UNREG_FCFI format failure\n");
11095 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11096 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11097 break;
11098 }
11099
11100 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11101 ocs_log_err(hw->os, "UNREG_FCFI command failure\n");
11102 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11103 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11104 break;
11105 }
11106 break;
11107 case OCS_EVT_RESPONSE:
11108 if (domain->req_rediscover_fcf) {
11109 domain->req_rediscover_fcf = FALSE;
11110 ocs_sm_transition(ctx, __ocs_hw_domain_free_redisc_fcf, data);
11111 } else {
11112 ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11113 }
11114 break;
11115 case OCS_EVT_ERROR:
11116 ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11117 break;
11118 case OCS_EVT_EXIT:
11119 break;
11120 default:
11121 __ocs_hw_domain_common(__func__, ctx, evt, data);
11122 break;
11123 }
11124
11125 return NULL;
11126 }
11127
11128 static void *
__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t * ctx,ocs_sm_event_t evt,void * data)11129 __ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
11130 {
11131 ocs_domain_t *domain = ctx->app;
11132 ocs_hw_t *hw = domain->hw;
11133 uint8_t is_fc = FALSE;
11134
11135 smtrace("domain");
11136
11137 is_fc = (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC);
11138
11139 switch (evt) {
11140 case OCS_EVT_ENTER:
11141 if (data == NULL) {
11142 data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
11143 if (!data) {
11144 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11145 break;
11146 }
11147 }
11148
11149 if (0 == sli_cmd_unreg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain,
11150 SLI4_UNREG_TYPE_DOMAIN)) {
11151 ocs_log_err(hw->os, "UNREG_VFI format failure\n");
11152 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11153 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11154 break;
11155 }
11156
11157 if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
11158 ocs_log_err(hw->os, "UNREG_VFI command failure\n");
11159 ocs_free(hw->os, data, SLI4_BMBX_SIZE);
11160 ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
11161 break;
11162 }
11163 break;
11164 case OCS_EVT_ERROR:
11165 if (is_fc) {
11166 ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
11167 } else {
11168 ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11169 }
11170 break;
11171 case OCS_EVT_RESPONSE:
11172 if (is_fc) {
11173 ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
11174 } else {
11175 ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
11176 }
11177 break;
11178 default:
11179 __ocs_hw_domain_common(__func__, ctx, evt, data);
11180 break;
11181 }
11182
11183 return NULL;
11184 }
11185
11186 /* callback for domain alloc/attach/free */
11187 static int32_t
__ocs_hw_domain_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11188 __ocs_hw_domain_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11189 {
11190 ocs_domain_t *domain = arg;
11191 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
11192 ocs_sm_event_t evt;
11193
11194 if (status || hdr->status) {
11195 ocs_log_debug(hw->os, "bad status vfi=%#x st=%x hdr=%x\n",
11196 domain->indicator, status, hdr->status);
11197 evt = OCS_EVT_ERROR;
11198 } else {
11199 evt = OCS_EVT_RESPONSE;
11200 }
11201
11202 ocs_sm_post_event(&domain->sm, evt, mqe);
11203
11204 return 0;
11205 }
11206
11207 static int32_t
target_wqe_timer_nop_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11208 target_wqe_timer_nop_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11209 {
11210 ocs_hw_io_t *io = NULL;
11211 ocs_hw_io_t *io_next = NULL;
11212 uint64_t ticks_current = ocs_get_os_ticks();
11213 uint32_t sec_elapsed;
11214
11215 sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
11216
11217 if (status || hdr->status) {
11218 ocs_log_debug(hw->os, "bad status st=%x hdr=%x\n",
11219 status, hdr->status);
11220 /* go ahead and proceed with wqe timer checks... */
11221 }
11222
11223 /* loop through active WQE list and check for timeouts */
11224 ocs_lock(&hw->io_lock);
11225 ocs_list_foreach_safe(&hw->io_timed_wqe, io, io_next) {
11226 sec_elapsed = ((ticks_current - io->submit_ticks) / ocs_get_os_tick_freq());
11227
11228 /*
11229 * If elapsed time > timeout, abort it. No need to check type since
11230 * it wouldn't be on this list unless it was a target WQE
11231 */
11232 if (sec_elapsed > io->tgt_wqe_timeout) {
11233 ocs_log_test(hw->os, "IO timeout xri=0x%x tag=0x%x type=%d\n",
11234 io->indicator, io->reqtag, io->type);
11235
11236 /* remove from active_wqe list so won't try to abort again */
11237 ocs_list_remove(&hw->io_timed_wqe, io);
11238
11239 /* save status of "timed out" for when abort completes */
11240 io->status_saved = 1;
11241 io->saved_status = SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT;
11242 io->saved_ext = 0;
11243 io->saved_len = 0;
11244
11245 /* now abort outstanding IO */
11246 ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
11247 }
11248 /*
11249 * need to go through entire list since each IO could have a
11250 * different timeout value
11251 */
11252 }
11253 ocs_unlock(&hw->io_lock);
11254
11255 /* if we're not in the middle of shutting down, schedule next timer */
11256 if (!hw->active_wqe_timer_shutdown) {
11257 ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw, OCS_HW_WQ_TIMER_PERIOD_MS);
11258 }
11259 hw->in_active_wqe_timer = FALSE;
11260 return 0;
11261 }
11262
11263 static void
target_wqe_timer_cb(void * arg)11264 target_wqe_timer_cb(void *arg)
11265 {
11266 ocs_hw_t *hw = (ocs_hw_t *)arg;
11267
11268 /* delete existing timer; will kick off new timer after checking wqe timeouts */
11269 hw->in_active_wqe_timer = TRUE;
11270 ocs_del_timer(&hw->wqe_timer);
11271
11272 /* Forward timer callback to execute in the mailbox completion processing context */
11273 if (ocs_hw_async_call(hw, target_wqe_timer_nop_cb, hw)) {
11274 ocs_log_test(hw->os, "ocs_hw_async_call failed\n");
11275 }
11276 }
11277
11278 static void
shutdown_target_wqe_timer(ocs_hw_t * hw)11279 shutdown_target_wqe_timer(ocs_hw_t *hw)
11280 {
11281 uint32_t iters = 100;
11282
11283 if (hw->config.emulate_tgt_wqe_timeout) {
11284 /* request active wqe timer shutdown, then wait for it to complete */
11285 hw->active_wqe_timer_shutdown = TRUE;
11286
11287 /* delete WQE timer and wait for timer handler to complete (if necessary) */
11288 ocs_del_timer(&hw->wqe_timer);
11289
11290 /* now wait for timer handler to complete (if necessary) */
11291 while (hw->in_active_wqe_timer && iters) {
11292 /*
11293 * if we happen to have just sent NOP mailbox command, make sure
11294 * completions are being processed
11295 */
11296 ocs_hw_flush(hw);
11297 iters--;
11298 }
11299
11300 if (iters == 0) {
11301 ocs_log_test(hw->os, "Failed to shutdown active wqe timer\n");
11302 }
11303 }
11304 }
11305
11306 /**
11307 * @brief Determine if HW IO is owned by the port.
11308 *
11309 * @par Description
11310 * Determines if the given HW IO has been posted to the chip.
11311 *
11312 * @param hw Hardware context allocated by the caller.
11313 * @param io HW IO.
11314 *
11315 * @return Returns TRUE if given HW IO is port-owned.
11316 */
11317 uint8_t
ocs_hw_is_io_port_owned(ocs_hw_t * hw,ocs_hw_io_t * io)11318 ocs_hw_is_io_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
11319 {
11320 /* Check to see if this is a port owned XRI */
11321 return io->is_port_owned;
11322 }
11323
11324 /**
11325 * @brief Return TRUE if exchange is port-owned.
11326 *
11327 * @par Description
11328 * Test to see if the xri is a port-owned xri.
11329 *
11330 * @param hw Hardware context.
11331 * @param xri Exchange indicator.
11332 *
11333 * @return Returns TRUE if XRI is a port owned XRI.
11334 */
11335
11336 uint8_t
ocs_hw_is_xri_port_owned(ocs_hw_t * hw,uint32_t xri)11337 ocs_hw_is_xri_port_owned(ocs_hw_t *hw, uint32_t xri)
11338 {
11339 ocs_hw_io_t *io = ocs_hw_io_lookup(hw, xri);
11340 return (io == NULL ? FALSE : io->is_port_owned);
11341 }
11342
11343 /**
11344 * @brief Returns an XRI from the port owned list to the host.
11345 *
11346 * @par Description
11347 * Used when the POST_XRI command fails as well as when the RELEASE_XRI completes.
11348 *
11349 * @param hw Hardware context.
11350 * @param xri_base The starting XRI number.
11351 * @param xri_count The number of XRIs to free from the base.
11352 */
11353 static void
ocs_hw_reclaim_xri(ocs_hw_t * hw,uint16_t xri_base,uint16_t xri_count)11354 ocs_hw_reclaim_xri(ocs_hw_t *hw, uint16_t xri_base, uint16_t xri_count)
11355 {
11356 ocs_hw_io_t *io;
11357 uint32_t i;
11358
11359 for (i = 0; i < xri_count; i++) {
11360 io = ocs_hw_io_lookup(hw, xri_base + i);
11361
11362 /*
11363 * if this is an auto xfer rdy XRI, then we need to release any
11364 * buffer attached to the XRI before moving the XRI back to the free pool.
11365 */
11366 if (hw->auto_xfer_rdy_enabled) {
11367 ocs_hw_rqpair_auto_xfer_rdy_move_to_host(hw, io);
11368 }
11369
11370 ocs_lock(&hw->io_lock);
11371 ocs_list_remove(&hw->io_port_owned, io);
11372 io->is_port_owned = 0;
11373 ocs_list_add_tail(&hw->io_free, io);
11374 ocs_unlock(&hw->io_lock);
11375 }
11376 }
11377
11378 /**
11379 * @brief Called when the POST_XRI command completes.
11380 *
11381 * @par Description
11382 * Free the mailbox command buffer and reclaim the XRIs on failure.
11383 *
11384 * @param hw Hardware context.
11385 * @param status Status field from the mbox completion.
11386 * @param mqe Mailbox response structure.
11387 * @param arg Pointer to a callback function that signals the caller that the command is done.
11388 *
11389 * @return Returns 0.
11390 */
11391 static int32_t
ocs_hw_cb_post_xri(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11392 ocs_hw_cb_post_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11393 {
11394 sli4_cmd_post_xri_t *post_xri = (sli4_cmd_post_xri_t*)mqe;
11395
11396 /* Reclaim the XRIs as host owned if the command fails */
11397 if (status != 0) {
11398 ocs_log_debug(hw->os, "Status 0x%x for XRI base 0x%x, cnt =x%x\n",
11399 status, post_xri->xri_base, post_xri->xri_count);
11400 ocs_hw_reclaim_xri(hw, post_xri->xri_base, post_xri->xri_count);
11401 }
11402
11403 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11404 return 0;
11405 }
11406
11407 /**
11408 * @brief Issues a mailbox command to move XRIs from the host-controlled pool to the port.
11409 *
11410 * @param hw Hardware context.
11411 * @param xri_start The starting XRI to post.
11412 * @param num_to_post The number of XRIs to post.
11413 *
11414 * @return Returns OCS_HW_RTN_NO_MEMORY, OCS_HW_RTN_ERROR, or OCS_HW_RTN_SUCCESS.
11415 */
11416
11417 static ocs_hw_rtn_e
ocs_hw_post_xri(ocs_hw_t * hw,uint32_t xri_start,uint32_t num_to_post)11418 ocs_hw_post_xri(ocs_hw_t *hw, uint32_t xri_start, uint32_t num_to_post)
11419 {
11420 uint8_t *post_xri;
11421 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11422
11423 /* Since we need to allocate for mailbox queue, just always allocate */
11424 post_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11425 if (post_xri == NULL) {
11426 ocs_log_err(hw->os, "no buffer for command\n");
11427 return OCS_HW_RTN_NO_MEMORY;
11428 }
11429
11430 /* Register the XRIs */
11431 if (sli_cmd_post_xri(&hw->sli, post_xri, SLI4_BMBX_SIZE,
11432 xri_start, num_to_post)) {
11433 rc = ocs_hw_command(hw, post_xri, OCS_CMD_NOWAIT, ocs_hw_cb_post_xri, NULL);
11434 if (rc != OCS_HW_RTN_SUCCESS) {
11435 ocs_free(hw->os, post_xri, SLI4_BMBX_SIZE);
11436 ocs_log_err(hw->os, "post_xri failed\n");
11437 }
11438 }
11439 return rc;
11440 }
11441
11442 /**
11443 * @brief Move XRIs from the host-controlled pool to the port.
11444 *
11445 * @par Description
11446 * Removes IOs from the free list and moves them to the port.
11447 *
11448 * @param hw Hardware context.
11449 * @param num_xri The number of XRIs being requested to move to the chip.
11450 *
11451 * @return Returns the number of XRIs that were moved.
11452 */
11453
11454 uint32_t
ocs_hw_xri_move_to_port_owned(ocs_hw_t * hw,uint32_t num_xri)11455 ocs_hw_xri_move_to_port_owned(ocs_hw_t *hw, uint32_t num_xri)
11456 {
11457 ocs_hw_io_t *io;
11458 uint32_t i;
11459 uint32_t num_posted = 0;
11460
11461 /*
11462 * Note: We cannot use ocs_hw_io_alloc() because that would place the
11463 * IO on the io_inuse list. We need to move from the io_free to
11464 * the io_port_owned list.
11465 */
11466 ocs_lock(&hw->io_lock);
11467
11468 for (i = 0; i < num_xri; i++) {
11469
11470 if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
11471 ocs_hw_rtn_e rc;
11472
11473 /*
11474 * if this is an auto xfer rdy XRI, then we need to attach a
11475 * buffer to the XRI before submitting it to the chip. If a
11476 * buffer is unavailable, then we cannot post it, so return it
11477 * to the free pool.
11478 */
11479 if (hw->auto_xfer_rdy_enabled) {
11480 /* Note: uses the IO lock to get the auto xfer rdy buffer */
11481 ocs_unlock(&hw->io_lock);
11482 rc = ocs_hw_rqpair_auto_xfer_rdy_move_to_port(hw, io);
11483 ocs_lock(&hw->io_lock);
11484 if (rc != OCS_HW_RTN_SUCCESS) {
11485 ocs_list_add_head(&hw->io_free, io);
11486 break;
11487 }
11488 }
11489 ocs_lock_init(hw->os, &io->axr_lock, "HW_axr_lock[%d]", io->indicator);
11490 io->is_port_owned = 1;
11491 ocs_list_add_tail(&hw->io_port_owned, io);
11492
11493 /* Post XRI */
11494 if (ocs_hw_post_xri(hw, io->indicator, 1) != OCS_HW_RTN_SUCCESS ) {
11495 ocs_hw_reclaim_xri(hw, io->indicator, i);
11496 break;
11497 }
11498 num_posted++;
11499 } else {
11500 /* no more free XRIs */
11501 break;
11502 }
11503 }
11504 ocs_unlock(&hw->io_lock);
11505
11506 return num_posted;
11507 }
11508
11509 /**
11510 * @brief Called when the RELEASE_XRI command completes.
11511 *
11512 * @par Description
11513 * Move the IOs back to the free pool on success.
11514 *
11515 * @param hw Hardware context.
11516 * @param status Status field from the mbox completion.
11517 * @param mqe Mailbox response structure.
11518 * @param arg Pointer to a callback function that signals the caller that the command is done.
11519 *
11520 * @return Returns 0.
11521 */
11522 static int32_t
ocs_hw_cb_release_xri(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11523 ocs_hw_cb_release_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11524 {
11525 sli4_cmd_release_xri_t *release_xri = (sli4_cmd_release_xri_t*)mqe;
11526 uint8_t i;
11527
11528 /* Reclaim the XRIs as host owned if the command fails */
11529 if (status != 0) {
11530 ocs_log_err(hw->os, "Status 0x%x\n", status);
11531 } else {
11532 for (i = 0; i < release_xri->released_xri_count; i++) {
11533 uint16_t xri = ((i & 1) == 0 ? release_xri->xri_tbl[i/2].xri_tag0 :
11534 release_xri->xri_tbl[i/2].xri_tag1);
11535 ocs_hw_reclaim_xri(hw, xri, 1);
11536 }
11537 }
11538
11539 ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
11540 return 0;
11541 }
11542
11543 /**
11544 * @brief Move XRIs from the port-controlled pool to the host.
11545 *
11546 * Requests XRIs from the FW to return to the host-owned pool.
11547 *
11548 * @param hw Hardware context.
11549 * @param num_xri The number of XRIs being requested to moved from the chip.
11550 *
11551 * @return Returns 0 for success, or a negative error code value for failure.
11552 */
11553
11554 ocs_hw_rtn_e
ocs_hw_xri_move_to_host_owned(ocs_hw_t * hw,uint8_t num_xri)11555 ocs_hw_xri_move_to_host_owned(ocs_hw_t *hw, uint8_t num_xri)
11556 {
11557 uint8_t *release_xri;
11558 ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
11559
11560 /* non-local buffer required for mailbox queue */
11561 release_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
11562 if (release_xri == NULL) {
11563 ocs_log_err(hw->os, "no buffer for command\n");
11564 return OCS_HW_RTN_NO_MEMORY;
11565 }
11566
11567 /* release the XRIs */
11568 if (sli_cmd_release_xri(&hw->sli, release_xri, SLI4_BMBX_SIZE, num_xri)) {
11569 rc = ocs_hw_command(hw, release_xri, OCS_CMD_NOWAIT, ocs_hw_cb_release_xri, NULL);
11570 if (rc != OCS_HW_RTN_SUCCESS) {
11571 ocs_log_err(hw->os, "release_xri failed\n");
11572 }
11573 }
11574 /* If we are polling or an error occurred, then free the mailbox buffer */
11575 if (release_xri != NULL && rc != OCS_HW_RTN_SUCCESS) {
11576 ocs_free(hw->os, release_xri, SLI4_BMBX_SIZE);
11577 }
11578 return rc;
11579 }
11580
11581
11582 /**
11583 * @brief Allocate an ocs_hw_rx_buffer_t array.
11584 *
11585 * @par Description
11586 * An ocs_hw_rx_buffer_t array is allocated, along with the required DMA memory.
11587 *
11588 * @param hw Pointer to HW object.
11589 * @param rqindex RQ index for this buffer.
11590 * @param count Count of buffers in array.
11591 * @param size Size of buffer.
11592 *
11593 * @return Returns the pointer to the allocated ocs_hw_rq_buffer_t array.
11594 */
11595 static ocs_hw_rq_buffer_t *
ocs_hw_rx_buffer_alloc(ocs_hw_t * hw,uint32_t rqindex,uint32_t count,uint32_t size)11596 ocs_hw_rx_buffer_alloc(ocs_hw_t *hw, uint32_t rqindex, uint32_t count, uint32_t size)
11597 {
11598 ocs_t *ocs = hw->os;
11599 ocs_hw_rq_buffer_t *rq_buf = NULL;
11600 ocs_hw_rq_buffer_t *prq;
11601 uint32_t i;
11602
11603 if (count != 0) {
11604 rq_buf = ocs_malloc(hw->os, sizeof(*rq_buf) * count, OCS_M_NOWAIT | OCS_M_ZERO);
11605 if (rq_buf == NULL) {
11606 ocs_log_err(hw->os, "Failure to allocate unsolicited DMA trackers\n");
11607 return NULL;
11608 }
11609
11610 for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
11611 prq->rqindex = rqindex;
11612 if (ocs_dma_alloc(ocs, &prq->dma, size, OCS_MIN_DMA_ALIGNMENT)) {
11613 ocs_log_err(hw->os, "DMA allocation failed\n");
11614 ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11615 rq_buf = NULL;
11616 break;
11617 }
11618 }
11619 }
11620 return rq_buf;
11621 }
11622
11623 /**
11624 * @brief Free an ocs_hw_rx_buffer_t array.
11625 *
11626 * @par Description
11627 * The ocs_hw_rx_buffer_t array is freed, along with allocated DMA memory.
11628 *
11629 * @param hw Pointer to HW object.
11630 * @param rq_buf Pointer to ocs_hw_rx_buffer_t array.
11631 * @param count Count of buffers in array.
11632 *
11633 * @return None.
11634 */
11635 static void
ocs_hw_rx_buffer_free(ocs_hw_t * hw,ocs_hw_rq_buffer_t * rq_buf,uint32_t count)11636 ocs_hw_rx_buffer_free(ocs_hw_t *hw, ocs_hw_rq_buffer_t *rq_buf, uint32_t count)
11637 {
11638 ocs_t *ocs = hw->os;
11639 uint32_t i;
11640 ocs_hw_rq_buffer_t *prq;
11641
11642 if (rq_buf != NULL) {
11643 for (i = 0, prq = rq_buf; i < count; i++, prq++) {
11644 ocs_dma_free(ocs, &prq->dma);
11645 }
11646 ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
11647 }
11648 }
11649
11650 /**
11651 * @brief Allocate the RQ data buffers.
11652 *
11653 * @param hw Pointer to HW object.
11654 *
11655 * @return Returns 0 on success, or a non-zero value on failure.
11656 */
11657 ocs_hw_rtn_e
ocs_hw_rx_allocate(ocs_hw_t * hw)11658 ocs_hw_rx_allocate(ocs_hw_t *hw)
11659 {
11660 ocs_t *ocs = hw->os;
11661 uint32_t i;
11662 int32_t rc = OCS_HW_RTN_SUCCESS;
11663 uint32_t rqindex = 0;
11664 hw_rq_t *rq;
11665 uint32_t hdr_size = OCS_HW_RQ_SIZE_HDR;
11666 uint32_t payload_size = hw->config.rq_default_buffer_size;
11667
11668 rqindex = 0;
11669
11670 for (i = 0; i < hw->hw_rq_count; i++) {
11671 rq = hw->hw_rq[i];
11672
11673 /* Allocate header buffers */
11674 rq->hdr_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, hdr_size);
11675 if (rq->hdr_buf == NULL) {
11676 ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc hdr_buf failed\n");
11677 rc = OCS_HW_RTN_ERROR;
11678 break;
11679 }
11680
11681 ocs_log_debug(hw->os, "rq[%2d] rq_id %02d header %4d by %4d bytes\n", i, rq->hdr->id,
11682 rq->entry_count, hdr_size);
11683
11684 rqindex++;
11685
11686 /* Allocate payload buffers */
11687 rq->payload_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, payload_size);
11688 if (rq->payload_buf == NULL) {
11689 ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc fb_buf failed\n");
11690 rc = OCS_HW_RTN_ERROR;
11691 break;
11692 }
11693 ocs_log_debug(hw->os, "rq[%2d] rq_id %02d default %4d by %4d bytes\n", i, rq->data->id,
11694 rq->entry_count, payload_size);
11695 rqindex++;
11696 }
11697
11698 return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
11699 }
11700
11701 /**
11702 * @brief Post the RQ data buffers to the chip.
11703 *
11704 * @param hw Pointer to HW object.
11705 *
11706 * @return Returns 0 on success, or a non-zero value on failure.
11707 */
11708 ocs_hw_rtn_e
ocs_hw_rx_post(ocs_hw_t * hw)11709 ocs_hw_rx_post(ocs_hw_t *hw)
11710 {
11711 uint32_t i;
11712 uint32_t idx;
11713 uint32_t rq_idx;
11714 int32_t rc = 0;
11715
11716 /*
11717 * In RQ pair mode, we MUST post the header and payload buffer at the
11718 * same time.
11719 */
11720 for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
11721 hw_rq_t *rq = hw->hw_rq[rq_idx];
11722
11723 for (i = 0; i < rq->entry_count-1; i++) {
11724 ocs_hw_sequence_t *seq = ocs_array_get(hw->seq_pool, idx++);
11725 ocs_hw_assert(seq != NULL);
11726
11727 seq->header = &rq->hdr_buf[i];
11728
11729 seq->payload = &rq->payload_buf[i];
11730
11731 rc = ocs_hw_sequence_free(hw, seq);
11732 if (rc) {
11733 break;
11734 }
11735 }
11736 if (rc) {
11737 break;
11738 }
11739 }
11740
11741 return rc;
11742 }
11743
11744 /**
11745 * @brief Free the RQ data buffers.
11746 *
11747 * @param hw Pointer to HW object.
11748 *
11749 */
11750 void
ocs_hw_rx_free(ocs_hw_t * hw)11751 ocs_hw_rx_free(ocs_hw_t *hw)
11752 {
11753 hw_rq_t *rq;
11754 uint32_t i;
11755
11756 /* Free hw_rq buffers */
11757 for (i = 0; i < hw->hw_rq_count; i++) {
11758 rq = hw->hw_rq[i];
11759 if (rq != NULL) {
11760 ocs_hw_rx_buffer_free(hw, rq->hdr_buf, rq->entry_count);
11761 rq->hdr_buf = NULL;
11762 ocs_hw_rx_buffer_free(hw, rq->payload_buf, rq->entry_count);
11763 rq->payload_buf = NULL;
11764 }
11765 }
11766 }
11767
11768 /**
11769 * @brief HW async call context structure.
11770 */
11771 typedef struct {
11772 ocs_hw_async_cb_t callback;
11773 void *arg;
11774 uint8_t cmd[SLI4_BMBX_SIZE];
11775 } ocs_hw_async_call_ctx_t;
11776
11777 /**
11778 * @brief HW async callback handler
11779 *
11780 * @par Description
11781 * This function is called when the NOP mailbox command completes. The callback stored
11782 * in the requesting context is invoked.
11783 *
11784 * @param hw Pointer to HW object.
11785 * @param status Completion status.
11786 * @param mqe Pointer to mailbox completion queue entry.
11787 * @param arg Caller-provided argument.
11788 *
11789 * @return None.
11790 */
11791 static void
ocs_hw_async_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)11792 ocs_hw_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
11793 {
11794 ocs_hw_async_call_ctx_t *ctx = arg;
11795
11796 if (ctx != NULL) {
11797 if (ctx->callback != NULL) {
11798 (*ctx->callback)(hw, status, mqe, ctx->arg);
11799 }
11800 ocs_free(hw->os, ctx, sizeof(*ctx));
11801 }
11802 }
11803
11804 /**
11805 * @brief Make an async callback using NOP mailbox command
11806 *
11807 * @par Description
11808 * Post a NOP mailbox command; the callback with argument is invoked upon completion
11809 * while in the event processing context.
11810 *
11811 * @param hw Pointer to HW object.
11812 * @param callback Pointer to callback function.
11813 * @param arg Caller-provided callback.
11814 *
11815 * @return Returns 0 on success, or a negative error code value on failure.
11816 */
11817 int32_t
ocs_hw_async_call(ocs_hw_t * hw,ocs_hw_async_cb_t callback,void * arg)11818 ocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
11819 {
11820 ocs_hw_async_call_ctx_t *ctx;
11821
11822 /*
11823 * Allocate a callback context (which includes the mailbox command buffer), we need
11824 * this to be persistent as the mailbox command submission may be queued and executed later
11825 * execution.
11826 */
11827 ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
11828 if (ctx == NULL) {
11829 ocs_log_err(hw->os, "failed to malloc async call context\n");
11830 return OCS_HW_RTN_NO_MEMORY;
11831 }
11832 ctx->callback = callback;
11833 ctx->arg = arg;
11834
11835 /* Build and send a NOP mailbox command */
11836 if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
11837 ocs_log_err(hw->os, "COMMON_NOP format failure\n");
11838 ocs_free(hw->os, ctx, sizeof(*ctx));
11839 return OCS_HW_RTN_ERROR;
11840 }
11841
11842 if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
11843 ocs_log_err(hw->os, "COMMON_NOP command failure\n");
11844 ocs_free(hw->os, ctx, sizeof(*ctx));
11845 return OCS_HW_RTN_ERROR;
11846 }
11847 return OCS_HW_RTN_SUCCESS;
11848 }
11849
11850 /**
11851 * @brief Initialize the reqtag pool.
11852 *
11853 * @par Description
11854 * The WQ request tag pool is initialized.
11855 *
11856 * @param hw Pointer to HW object.
11857 *
11858 * @return Returns 0 on success, or a negative error code value on failure.
11859 */
11860 ocs_hw_rtn_e
ocs_hw_reqtag_init(ocs_hw_t * hw)11861 ocs_hw_reqtag_init(ocs_hw_t *hw)
11862 {
11863 if (hw->wq_reqtag_pool == NULL) {
11864 hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
11865 if (hw->wq_reqtag_pool == NULL) {
11866 ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
11867 return OCS_HW_RTN_NO_MEMORY;
11868 }
11869 }
11870 ocs_hw_reqtag_reset(hw);
11871 return OCS_HW_RTN_SUCCESS;
11872 }
11873
11874 /**
11875 * @brief Allocate a WQ request tag.
11876 *
11877 * Allocate and populate a WQ request tag from the WQ request tag pool.
11878 *
11879 * @param hw Pointer to HW object.
11880 * @param callback Callback function.
11881 * @param arg Pointer to callback argument.
11882 *
11883 * @return Returns pointer to allocated WQ request tag, or NULL if object cannot be allocated.
11884 */
11885 hw_wq_callback_t *
ocs_hw_reqtag_alloc(ocs_hw_t * hw,void (* callback)(void * arg,uint8_t * cqe,int32_t status),void * arg)11886 ocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
11887 {
11888 hw_wq_callback_t *wqcb;
11889
11890 ocs_hw_assert(callback != NULL);
11891
11892 wqcb = ocs_pool_get(hw->wq_reqtag_pool);
11893 if (wqcb != NULL) {
11894 ocs_hw_assert(wqcb->callback == NULL);
11895 wqcb->callback = callback;
11896 wqcb->arg = arg;
11897 }
11898 return wqcb;
11899 }
11900
11901 /**
11902 * @brief Free a WQ request tag.
11903 *
11904 * Free the passed in WQ request tag.
11905 *
11906 * @param hw Pointer to HW object.
11907 * @param wqcb Pointer to WQ request tag object to free.
11908 *
11909 * @return None.
11910 */
11911 void
ocs_hw_reqtag_free(ocs_hw_t * hw,hw_wq_callback_t * wqcb)11912 ocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
11913 {
11914 ocs_hw_assert(wqcb->callback != NULL);
11915 wqcb->callback = NULL;
11916 wqcb->arg = NULL;
11917 ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11918 }
11919
11920 /**
11921 * @brief Return WQ request tag by index.
11922 *
11923 * @par Description
11924 * Return pointer to WQ request tag object given an index.
11925 *
11926 * @param hw Pointer to HW object.
11927 * @param instance_index Index of WQ request tag to return.
11928 *
11929 * @return Pointer to WQ request tag, or NULL.
11930 */
11931 hw_wq_callback_t *
ocs_hw_reqtag_get_instance(ocs_hw_t * hw,uint32_t instance_index)11932 ocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
11933 {
11934 hw_wq_callback_t *wqcb;
11935
11936 wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
11937 if (wqcb == NULL) {
11938 ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
11939 }
11940 return wqcb;
11941 }
11942
11943 /**
11944 * @brief Reset the WQ request tag pool.
11945 *
11946 * @par Description
11947 * Reset the WQ request tag pool, returning all to the free list.
11948 *
11949 * @param hw pointer to HW object.
11950 *
11951 * @return None.
11952 */
11953 void
ocs_hw_reqtag_reset(ocs_hw_t * hw)11954 ocs_hw_reqtag_reset(ocs_hw_t *hw)
11955 {
11956 hw_wq_callback_t *wqcb;
11957 uint32_t i;
11958
11959 /* Remove all from freelist */
11960 while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
11961 ;
11962 }
11963
11964 /* Put them all back */
11965 for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
11966 wqcb->instance_index = i;
11967 wqcb->callback = NULL;
11968 wqcb->arg = NULL;
11969 ocs_pool_put(hw->wq_reqtag_pool, wqcb);
11970 }
11971 }
11972
11973 /**
11974 * @brief Handle HW assertion
11975 *
11976 * HW assert, display diagnostic message, and abort.
11977 *
11978 * @param cond string describing failing assertion condition
11979 * @param filename file name
11980 * @param linenum line number
11981 *
11982 * @return none
11983 */
11984 void
_ocs_hw_assert(const char * cond,const char * filename,int linenum)11985 _ocs_hw_assert(const char *cond, const char *filename, int linenum)
11986 {
11987 ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
11988 ocs_abort();
11989 /* no return */
11990 }
11991
11992 /**
11993 * @brief Handle HW verify
11994 *
11995 * HW verify, display diagnostic message, dump stack and return.
11996 *
11997 * @param cond string describing failing verify condition
11998 * @param filename file name
11999 * @param linenum line number
12000 *
12001 * @return none
12002 */
12003 void
_ocs_hw_verify(const char * cond,const char * filename,int linenum)12004 _ocs_hw_verify(const char *cond, const char *filename, int linenum)
12005 {
12006 ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
12007 ocs_print_stack();
12008 }
12009
12010 /**
12011 * @brief Reque XRI
12012 *
12013 * @par Description
12014 * Reque XRI
12015 *
12016 * @param hw Pointer to HW object.
12017 * @param io Pointer to HW IO
12018 *
12019 * @return Return 0 if successful else returns -1
12020 */
12021 int32_t
ocs_hw_reque_xri(ocs_hw_t * hw,ocs_hw_io_t * io)12022 ocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
12023 {
12024 int32_t rc = 0;
12025
12026 rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
12027 if (rc) {
12028 ocs_list_add_tail(&hw->io_port_dnrx, io);
12029 rc = -1;
12030 goto exit_ocs_hw_reque_xri;
12031 }
12032
12033 io->auto_xfer_rdy_dnrx = 0;
12034 io->type = OCS_HW_IO_DNRX_REQUEUE;
12035 if (sli_requeue_xri_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->indicator, OCS_HW_REQUE_XRI_REGTAG, SLI4_CQ_DEFAULT)) {
12036 /* Clear buffer from XRI */
12037 ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
12038 io->axr_buf = NULL;
12039
12040 ocs_log_err(hw->os, "requeue_xri WQE error\n");
12041 ocs_list_add_tail(&hw->io_port_dnrx, io);
12042
12043 rc = -1;
12044 goto exit_ocs_hw_reque_xri;
12045 }
12046
12047 if (io->wq == NULL) {
12048 io->wq = ocs_hw_queue_next_wq(hw, io);
12049 ocs_hw_assert(io->wq != NULL);
12050 }
12051
12052 /*
12053 * Add IO to active io wqe list before submitting, in case the
12054 * wcqe processing preempts this thread.
12055 */
12056 OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
12057 OCS_STAT(io->wq->use_count++);
12058
12059 rc = hw_wq_write(io->wq, &io->wqe);
12060 if (rc < 0) {
12061 ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
12062 rc = -1;
12063 }
12064
12065 exit_ocs_hw_reque_xri:
12066 return 0;
12067 }
12068
12069 uint32_t
ocs_hw_get_def_wwn(ocs_t * ocs,uint32_t chan,uint64_t * wwpn,uint64_t * wwnn)12070 ocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
12071 {
12072 sli4_t *sli4 = &ocs->hw.sli;
12073 ocs_dma_t dma;
12074 uint8_t *payload = NULL;
12075
12076 int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
12077
12078 /* allocate memory for the service parameters */
12079 if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
12080 ocs_log_err(ocs, "Failed to allocate DMA memory\n");
12081 return 1;
12082 }
12083
12084 if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
12085 &dma, indicator)) {
12086 ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
12087 ocs_dma_free(ocs, &dma);
12088 return 1;
12089 }
12090
12091 if (sli_bmbx_command(sli4)) {
12092 ocs_log_err(ocs, "READ_SPARM64 command failure\n");
12093 ocs_dma_free(ocs, &dma);
12094 return 1;
12095 }
12096
12097 payload = dma.virt;
12098 ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
12099 ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
12100 ocs_dma_free(ocs, &dma);
12101 return 0;
12102 }
12103
12104 uint32_t
ocs_hw_get_config_persistent_topology(ocs_hw_t * hw)12105 ocs_hw_get_config_persistent_topology(ocs_hw_t *hw)
12106 {
12107 uint32_t topology = OCS_HW_TOPOLOGY_AUTO;
12108 sli4_t *sli = &hw->sli;
12109
12110 if (!sli_persist_topology_enabled(sli))
12111 return topology;
12112
12113 switch (sli->config.pt) {
12114 case SLI4_INIT_LINK_F_P2P_ONLY:
12115 topology = OCS_HW_TOPOLOGY_NPORT;
12116 break;
12117 case SLI4_INIT_LINK_F_FCAL_ONLY:
12118 topology = OCS_HW_TOPOLOGY_LOOP;
12119 break;
12120 default:
12121 break;
12122 }
12123
12124 return topology;
12125 }
12126
12127 /*
12128 * @brief Persistent topology configuration callback argument.
12129 */
12130 typedef struct ocs_hw_persistent_topo_cb_arg {
12131 ocs_sem_t semaphore;
12132 int32_t status;
12133 } ocs_hw_persistent_topo_cb_arg_t;
12134
12135 /*
12136 * @brief Called after the completion of set persistent topology request
12137 *
12138 * @par Description
12139 * This is callback fn for the set_persistent_topology
12140 * function. This callback is called when the common feature mbx cmd
12141 * completes.
12142 *
12143 * @param hw Hardware context.
12144 * @param status The status from the MQE.
12145 * @param mqe Pointer to mailbox command buffer.
12146 * @param arg Pointer to a callback argument.
12147 *
12148 * @return 0 on success, non-zero otherwise
12149 */
12150 static int32_t
ocs_hw_set_persistent_topolgy_cb(ocs_hw_t * hw,int32_t status,uint8_t * mqe,void * arg)12151 ocs_hw_set_persistent_topolgy_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
12152 {
12153 ocs_hw_persistent_topo_cb_arg_t *req = (ocs_hw_persistent_topo_cb_arg_t *)arg;
12154
12155 req->status = status;
12156
12157 ocs_sem_v(&req->semaphore);
12158
12159 return 0;
12160 }
12161
12162 /**
12163 * @brief Set persistent topology
12164 *
12165 * Sets the persistent topology(PT) feature using
12166 * COMMON_SET_FEATURES cmd. If mbx cmd succeeds, update the
12167 * topology into sli config. PT stores the value to be set into link_flags
12168 * of the cmd INIT_LINK, to bring up the link.
12169 *
12170 * SLI specs defines following for PT:
12171 * When TF is set to 0:
12172 * 0 Reserved
12173 * 1 Attempt point-to-point initialization (direct attach or Fabric topology).
12174 * 2 Attempt FC-AL loop initialization.
12175 * 3 Reserved
12176 *
12177 * When TF is set to 1:
12178 * 0 Attempt FC-AL loop initialization; if it fails, attempt point-to-point initialization.
12179 * 1 Attempt point-to-point initialization; if it fails, attempt FC-AL loop initialization.
12180 * 2 Reserved
12181 * 3 Reserved
12182 *
12183 * Note: Topology failover is only available on Lancer G5. This command will fail
12184 * if TF is set to 1 on any other ASICs
12185 *
12186 * @param hw Pointer to hw
12187 * @param topology topology value to be set, provided through
12188 * elxsdkutil set-topology cmd
12189 *
12190 * @return Returns 0 on success, or a non-zero value on failure.
12191 */
12192 ocs_hw_rtn_e
ocs_hw_set_persistent_topology(ocs_hw_t * hw,uint32_t topology,uint32_t opts)12193 ocs_hw_set_persistent_topology(ocs_hw_t *hw, uint32_t topology, uint32_t opts)
12194 {
12195 ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
12196 uint8_t buf[SLI4_BMBX_SIZE];
12197 sli4_req_common_set_features_persistent_topo_param_t param;
12198 ocs_hw_persistent_topo_cb_arg_t request;
12199
12200 ocs_memset(¶m, 0, sizeof(param));
12201 param.persistent_topo = topology;
12202
12203 switch (topology) {
12204 case OCS_HW_TOPOLOGY_AUTO:
12205 if (sli_get_asic_type(&hw->sli) == SLI4_ASIC_TYPE_LANCER) {
12206 param.persistent_topo = SLI4_INIT_LINK_F_P2P_FAIL_OVER;
12207 param.topo_failover = 1;
12208 } else {
12209 param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;;
12210 param.topo_failover = 0;
12211 }
12212 break;
12213
12214 case OCS_HW_TOPOLOGY_NPORT:
12215 param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;
12216 param.topo_failover = 0;
12217 break;
12218
12219 case OCS_HW_TOPOLOGY_LOOP:
12220 param.persistent_topo = SLI4_INIT_LINK_F_FCAL_ONLY;
12221 param.topo_failover = 0;
12222 break;
12223
12224 default:
12225 ocs_log_err(hw->os, "unsupported topology %#x\n", topology);
12226 return -1;
12227 }
12228
12229 ocs_sem_init(&request.semaphore, 0, "set_persistent_topo");
12230
12231 /* build the set_features command */
12232 sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
12233 SLI4_SET_FEATURES_PERSISTENT_TOPOLOGY, sizeof(param), ¶m);
12234
12235 if (opts == OCS_CMD_POLL) {
12236 rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
12237 if (rc) {
12238 ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12239 return rc;
12240 }
12241 } else {
12242
12243 // there's no response for this feature command
12244 rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_set_persistent_topolgy_cb, &request);
12245 if (rc) {
12246 ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
12247 return rc;
12248 }
12249
12250 if (ocs_sem_p(&request.semaphore, OCS_SEM_FOREVER)) {
12251 ocs_log_err(hw->os, "ocs_sem_p failed\n");
12252 return -ENXIO;
12253 }
12254
12255 if (request.status) {
12256 ocs_log_err(hw->os, "set persistent topology failed; status: %d\n", request.status);
12257 return -EFAULT;
12258 }
12259 }
12260
12261 sli_config_persistent_topology(&hw->sli, ¶m);
12262
12263 return rc;
12264 }
12265
12266 /**
12267 * @page fc_hw_api_overview HW APIs
12268 * - @ref devInitShutdown
12269 * - @ref domain
12270 * - @ref port
12271 * - @ref node
12272 * - @ref io
12273 * - @ref interrupt
12274 *
12275 * <div class="overview">
12276 * The Hardware Abstraction Layer (HW) insulates the higher-level code from the SLI-4
12277 * message details, but the higher level code must still manage domains, ports,
12278 * IT nexuses, and IOs. The HW API is designed to help the higher level manage
12279 * these objects.<br><br>
12280 *
12281 * The HW uses function callbacks to notify the higher-level code of events
12282 * that are received from the chip. There are currently three types of
12283 * functions that may be registered:
12284 *
12285 * <ul><li>domain – This function is called whenever a domain event is generated
12286 * within the HW. Examples include a new FCF is discovered, a connection
12287 * to a domain is disrupted, and allocation callbacks.</li>
12288 * <li>unsolicited – This function is called whenever new data is received in
12289 * the SLI-4 receive queue.</li>
12290 * <li>rnode – This function is called for remote node events, such as attach status
12291 * and allocation callbacks.</li></ul>
12292 *
12293 * Upper layer functions may be registered by using the ocs_hw_callback() function.
12294 *
12295 * <img src="elx_fc_hw.jpg" alt="FC/FCoE HW" title="FC/FCoE HW" align="right"/>
12296 * <h2>FC/FCoE HW API</h2>
12297 * The FC/FCoE HW component builds upon the SLI-4 component to establish a flexible
12298 * interface for creating the necessary common objects and sending I/Os. It may be used
12299 * “as is” in customer implementations or it can serve as an example of typical interactions
12300 * between a driver and the SLI-4 hardware. The broad categories of functionality include:
12301 *
12302 * <ul><li>Setting-up and tearing-down of the HW.</li>
12303 * <li>Allocating and using the common objects (SLI Port, domain, remote node).</li>
12304 * <li>Sending and receiving I/Os.</li></ul>
12305 *
12306 * <h3>HW Setup</h3>
12307 * To set up the HW:
12308 *
12309 * <ol>
12310 * <li>Set up the HW object using ocs_hw_setup().<br>
12311 * This step performs a basic configuration of the SLI-4 component and the HW to
12312 * enable querying the hardware for its capabilities. At this stage, the HW is not
12313 * capable of general operations (such as, receiving events or sending I/Os).</li><br><br>
12314 * <li>Configure the HW according to the driver requirements.<br>
12315 * The HW provides functions to discover hardware capabilities (ocs_hw_get()), as
12316 * well as configures the amount of resources required (ocs_hw_set()). The driver
12317 * must also register callback functions (ocs_hw_callback()) to receive notification of
12318 * various asynchronous events.<br><br>
12319 * @b Note: Once configured, the driver must initialize the HW (ocs_hw_init()). This
12320 * step creates the underlying queues, commits resources to the hardware, and
12321 * prepares the hardware for operation. While the hardware is operational, the
12322 * port is not online, and cannot send or receive data.</li><br><br>
12323 * <br><br>
12324 * <li>Finally, the driver can bring the port online (ocs_hw_port_control()).<br>
12325 * When the link comes up, the HW determines if a domain is present and notifies the
12326 * driver using the domain callback function. This is the starting point of the driver's
12327 * interaction with the common objects.<br><br>
12328 * @b Note: For FCoE, there may be more than one domain available and, therefore,
12329 * more than one callback.</li>
12330 * </ol>
12331 *
12332 * <h3>Allocating and Using Common Objects</h3>
12333 * Common objects provide a mechanism through which the various OneCore Storage
12334 * driver components share and track information. These data structures are primarily
12335 * used to track SLI component information but can be extended by other components, if
12336 * needed. The main objects are:
12337 *
12338 * <ul><li>DMA – the ocs_dma_t object describes a memory region suitable for direct
12339 * memory access (DMA) transactions.</li>
12340 * <li>SCSI domain – the ocs_domain_t object represents the SCSI domain, including
12341 * any infrastructure devices such as FC switches and FC forwarders. The domain
12342 * object contains both an FCFI and a VFI.</li>
12343 * <li>SLI Port (sport) – the ocs_sli_port_t object represents the connection between
12344 * the driver and the SCSI domain. The SLI Port object contains a VPI.</li>
12345 * <li>Remote node – the ocs_remote_node_t represents a connection between the SLI
12346 * Port and another device in the SCSI domain. The node object contains an RPI.</li></ul>
12347 *
12348 * Before the driver can send I/Os, it must allocate the SCSI domain, SLI Port, and remote
12349 * node common objects and establish the connections between them. The goal is to
12350 * connect the driver to the SCSI domain to exchange I/Os with other devices. These
12351 * common object connections are shown in the following figure, FC Driver Common Objects:
12352 * <img src="elx_fc_common_objects.jpg"
12353 * alt="FC Driver Common Objects" title="FC Driver Common Objects" align="center"/>
12354 *
12355 * The first step is to create a connection to the domain by allocating an SLI Port object.
12356 * The SLI Port object represents a particular FC ID and must be initialized with one. With
12357 * the SLI Port object, the driver can discover the available SCSI domain(s). On identifying
12358 * a domain, the driver allocates a domain object and attaches to it using the previous SLI
12359 * port object.<br><br>
12360 *
12361 * @b Note: In some cases, the driver may need to negotiate service parameters (that is,
12362 * FLOGI) with the domain before attaching.<br><br>
12363 *
12364 * Once attached to the domain, the driver can discover and attach to other devices
12365 * (remote nodes). The exact discovery method depends on the driver, but it typically
12366 * includes using a position map, querying the fabric name server, or an out-of-band
12367 * method. In most cases, it is necessary to log in with devices before performing I/Os.
12368 * Prior to sending login-related ELS commands (ocs_hw_srrs_send()), the driver must
12369 * allocate a remote node object (ocs_hw_node_alloc()). If the login negotiation is
12370 * successful, the driver must attach the nodes (ocs_hw_node_attach()) to the SLI Port
12371 * before exchanging FCP I/O.<br><br>
12372 *
12373 * @b Note: The HW manages both the well known fabric address and the name server as
12374 * nodes in the domain. Therefore, the driver must allocate node objects prior to
12375 * communicating with either of these entities.
12376 *
12377 * <h3>Sending and Receiving I/Os</h3>
12378 * The HW provides separate interfaces for sending BLS/ ELS/ FC-CT and FCP, but the
12379 * commands are conceptually similar. Since the commands complete asynchronously,
12380 * the caller must provide a HW I/O object that maintains the I/O state, as well as
12381 * provide a callback function. The driver may use the same callback function for all I/O
12382 * operations, but each operation must use a unique HW I/O object. In the SLI-4
12383 * architecture, there is a direct association between the HW I/O object and the SGL used
12384 * to describe the data. Therefore, a driver typically performs the following operations:
12385 *
12386 * <ul><li>Allocates a HW I/O object (ocs_hw_io_alloc()).</li>
12387 * <li>Formats the SGL, specifying both the HW I/O object and the SGL.
12388 * (ocs_hw_io_init_sges() and ocs_hw_io_add_sge()).</li>
12389 * <li>Sends the HW I/O (ocs_hw_io_send()).</li></ul>
12390 *
12391 * <h3>HW Tear Down</h3>
12392 * To tear-down the HW:
12393 *
12394 * <ol><li>Take the port offline (ocs_hw_port_control()) to prevent receiving further
12395 * data andevents.</li>
12396 * <li>Destroy the HW object (ocs_hw_teardown()).</li>
12397 * <li>Free any memory used by the HW, such as buffers for unsolicited data.</li></ol>
12398 * <br>
12399 * </div><!-- overview -->
12400 *
12401 */
12402
12403
12404
12405
12406 /**
12407 * This contains all hw runtime workaround code. Based on the asic type,
12408 * asic revision, and range of fw revisions, a particular workaround may be enabled.
12409 *
12410 * A workaround may consist of overriding a particular HW/SLI4 value that was initialized
12411 * during ocs_hw_setup() (for example the MAX_QUEUE overrides for mis-reported queue
12412 * sizes). Or if required, elements of the ocs_hw_workaround_t structure may be set to
12413 * control specific runtime behavior.
12414 *
12415 * It is intended that the controls in ocs_hw_workaround_t be defined functionally. So we
12416 * would have the driver look like: "if (hw->workaround.enable_xxx) then ...", rather than
12417 * what we might previously see as "if this is a BE3, then do xxx"
12418 *
12419 */
12420
12421
12422 #define HW_FWREV_ZERO (0ull)
12423 #define HW_FWREV_MAX (~0ull)
12424
12425 #define SLI4_ASIC_TYPE_ANY 0
12426 #define SLI4_ASIC_REV_ANY 0
12427
12428 /**
12429 * @brief Internal definition of workarounds
12430 */
12431
12432 typedef enum {
12433 HW_WORKAROUND_TEST = 1,
12434 HW_WORKAROUND_MAX_QUEUE, /**< Limits all queues */
12435 HW_WORKAROUND_MAX_RQ, /**< Limits only the RQ */
12436 HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
12437 HW_WORKAROUND_WQE_COUNT_METHOD,
12438 HW_WORKAROUND_RQE_COUNT_METHOD,
12439 HW_WORKAROUND_USE_UNREGISTERD_RPI,
12440 HW_WORKAROUND_DISABLE_AR_TGT_DIF, /**< Disable of auto-response target DIF */
12441 HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
12442 HW_WORKAROUND_USE_DIF_QUARANTINE,
12443 HW_WORKAROUND_USE_DIF_SEC_XRI, /**< Use secondary xri for multiple data phases */
12444 HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, /**< FCFI reported in SRB not correct, use "first" registered domain */
12445 HW_WORKAROUND_FW_VERSION_TOO_LOW, /**< The FW version is not the min version supported by this driver */
12446 HW_WORKAROUND_SGLC_MISREPORTED, /**< Chip supports SGL Chaining but SGLC is not set in SLI4_PARAMS */
12447 HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, /**< Don't use SEND_FRAME capable if FW version is too old */
12448 } hw_workaround_e;
12449
12450 /**
12451 * @brief Internal workaround structure instance
12452 */
12453
12454 typedef struct {
12455 sli4_asic_type_e asic_type;
12456 sli4_asic_rev_e asic_rev;
12457 uint64_t fwrev_low;
12458 uint64_t fwrev_high;
12459
12460 hw_workaround_e workaround;
12461 uint32_t value;
12462 } hw_workaround_t;
12463
12464 static hw_workaround_t hw_workarounds[] = {
12465 {SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12466 HW_WORKAROUND_TEST, 999},
12467
12468 /* Bug: 127585: if_type == 2 returns 0 for total length placed on
12469 * FCP_TSEND64_WQE completions. Note, original driver code enables this
12470 * workaround for all asic types
12471 */
12472 {SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12473 HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
12474
12475 /* Bug: unknown, Lancer A0 has mis-reported max queue depth */
12476 {SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
12477 HW_WORKAROUND_MAX_QUEUE, 2048},
12478
12479 /* Bug: 143399, BE3 has mis-reported max RQ queue depth */
12480 {SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
12481 HW_WORKAROUND_MAX_RQ, 2048},
12482
12483 /* Bug: 143399, skyhawk has mis-reported max RQ queue depth */
12484 {SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
12485 HW_WORKAROUND_MAX_RQ, 2048},
12486
12487 /* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported WQE count method */
12488 {SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12489 HW_WORKAROUND_WQE_COUNT_METHOD, 1},
12490
12491 /* Bug: 103487, BE3 before f/w 4.2.314.0 has mis-reported RQE count method */
12492 {SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
12493 HW_WORKAROUND_RQE_COUNT_METHOD, 1},
12494
12495 /* Bug: 142968, BE3 UE with RPI == 0xffff */
12496 {SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12497 HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
12498
12499 /* Bug: unknown, Skyhawk won't support auto-response on target T10-PI */
12500 {SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12501 HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
12502
12503 {SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
12504 HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
12505
12506 /* Bug: 160124, Skyhawk quarantine DIF XRIs */
12507 {SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12508 HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
12509
12510 /* Bug: 161832, Skyhawk use secondary XRI for multiple data phase TRECV */
12511 {SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12512 HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
12513
12514 /* Bug: xxxxxx, FCFI reported in SRB not corrrect */
12515 {SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12516 HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
12517 #if 0
12518 /* Bug: 165642, FW version check for driver */
12519 {SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
12520 HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12521 #endif
12522 {SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
12523 HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
12524
12525 /* Bug 177061, Lancer FW does not set the SGLC bit */
12526 {SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12527 HW_WORKAROUND_SGLC_MISREPORTED, 0},
12528
12529 /* BZ 181208/183914, enable this workaround for ALL revisions */
12530 {SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
12531 HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
12532 };
12533
12534 /**
12535 * @brief Function prototypes
12536 */
12537
12538 static int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
12539
12540 /**
12541 * @brief Parse the firmware version (name)
12542 *
12543 * Parse a string of the form a.b.c.d, returning a uint64_t packed as defined
12544 * by the HW_FWREV() macro
12545 *
12546 * @param fwrev_string pointer to the firmware string
12547 *
12548 * @return packed firmware revision value
12549 */
12550
12551 static uint64_t
parse_fw_version(const char * fwrev_string)12552 parse_fw_version(const char *fwrev_string)
12553 {
12554 int v[4] = {0};
12555 const char *p;
12556 int i;
12557
12558 for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
12559 v[i] = ocs_strtoul(p, 0, 0);
12560 while(*p && *p != '.') {
12561 p ++;
12562 }
12563 if (*p) {
12564 p ++;
12565 }
12566 }
12567
12568 /* Special case for bootleg releases with f/w rev 0.0.9999.0, set to max value */
12569 if (v[2] == 9999) {
12570 return HW_FWREV_MAX;
12571 } else {
12572 return HW_FWREV(v[0], v[1], v[2], v[3]);
12573 }
12574 }
12575
12576 /**
12577 * @brief Test for a workaround match
12578 *
12579 * Looks at the asic type, asic revision, and fw revision, and returns TRUE if match.
12580 *
12581 * @param hw Pointer to the HW structure
12582 * @param w Pointer to a workaround structure entry
12583 *
12584 * @return Return TRUE for a match
12585 */
12586
12587 static int32_t
ocs_hw_workaround_match(ocs_hw_t * hw,hw_workaround_t * w)12588 ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
12589 {
12590 return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
12591 ((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
12592 (w->fwrev_low <= hw->workaround.fwrev) &&
12593 ((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
12594 }
12595
12596 /**
12597 * @brief Setup HW runtime workarounds
12598 *
12599 * The function is called at the end of ocs_hw_setup() to setup any runtime workarounds
12600 * based on the HW/SLI setup.
12601 *
12602 * @param hw Pointer to HW structure
12603 *
12604 * @return none
12605 */
12606
12607 void
ocs_hw_workaround_setup(struct ocs_hw_s * hw)12608 ocs_hw_workaround_setup(struct ocs_hw_s *hw)
12609 {
12610 hw_workaround_t *w;
12611 sli4_t *sli4 = &hw->sli;
12612 uint32_t i;
12613
12614 /* Initialize the workaround settings */
12615 ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
12616
12617 /* If hw_war_version is non-null, then its a value that was set by a module parameter
12618 * (sorry for the break in abstraction, but workarounds are ... well, workarounds)
12619 */
12620
12621 if (hw->hw_war_version) {
12622 hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
12623 } else {
12624 hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
12625 }
12626
12627 /* Walk the workaround list, if a match is found, then handle it */
12628 for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
12629 if (ocs_hw_workaround_match(hw, w)) {
12630 switch(w->workaround) {
12631
12632 case HW_WORKAROUND_TEST: {
12633 ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
12634 break;
12635 }
12636
12637 case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
12638 ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
12639 hw->workaround.retain_tsend_io_length = 1;
12640 break;
12641 }
12642 case HW_WORKAROUND_MAX_QUEUE: {
12643 sli4_qtype_e q;
12644
12645 ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
12646 for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
12647 if (hw->num_qentries[q] > w->value) {
12648 hw->num_qentries[q] = w->value;
12649 }
12650 }
12651 break;
12652 }
12653 case HW_WORKAROUND_MAX_RQ: {
12654 ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
12655 if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
12656 hw->num_qentries[SLI_QTYPE_RQ] = w->value;
12657 }
12658 break;
12659 }
12660 case HW_WORKAROUND_WQE_COUNT_METHOD: {
12661 ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
12662 sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
12663 sli_calc_max_qentries(sli4);
12664 break;
12665 }
12666 case HW_WORKAROUND_RQE_COUNT_METHOD: {
12667 ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
12668 sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
12669 sli_calc_max_qentries(sli4);
12670 break;
12671 }
12672 case HW_WORKAROUND_USE_UNREGISTERD_RPI:
12673 ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
12674 hw->workaround.use_unregistered_rpi = TRUE;
12675 /*
12676 * Allocate an RPI that is never registered, to be used in the case where
12677 * a node has been unregistered, and its indicator (RPI) value is set to 0xFFFF
12678 */
12679 if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
12680 &hw->workaround.unregistered_index)) {
12681 ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
12682 hw->workaround.use_unregistered_rpi = FALSE;
12683 }
12684 break;
12685 case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
12686 ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
12687 hw->workaround.disable_ar_tgt_dif = TRUE;
12688 break;
12689 case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
12690 ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
12691 hw->workaround.disable_dump_loc = TRUE;
12692 break;
12693 case HW_WORKAROUND_USE_DIF_QUARANTINE:
12694 ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
12695 hw->workaround.use_dif_quarantine = TRUE;
12696 break;
12697 case HW_WORKAROUND_USE_DIF_SEC_XRI:
12698 ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
12699 hw->workaround.use_dif_sec_xri = TRUE;
12700 break;
12701 case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
12702 ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
12703 hw->workaround.override_fcfi = TRUE;
12704 break;
12705
12706 case HW_WORKAROUND_FW_VERSION_TOO_LOW:
12707 ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
12708 hw->workaround.fw_version_too_low = TRUE;
12709 break;
12710 case HW_WORKAROUND_SGLC_MISREPORTED:
12711 ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
12712 hw->workaround.sglc_misreported = TRUE;
12713 break;
12714 case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
12715 ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
12716 hw->workaround.ignore_send_frame = TRUE;
12717 break;
12718 } /* switch(w->workaround) */
12719 }
12720 }
12721 }
12722