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