1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * tavor_cmd.c
29 * Tavor Firmware Command Routines
30 *
31 * Implements all the routines necessary for allocating, posting, and
32 * freeing commands for the Tavor firmware. These routines manage a
33 * preallocated list of command mailboxes and provide interfaces to post
34 * each of the several dozen commands to the Tavor firmware.
35 */
36
37 #include <sys/types.h>
38 #include <sys/conf.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/modctl.h>
42
43 #include <sys/ib/adapters/tavor/tavor.h>
44
45 static int tavor_impl_mbox_alloc(tavor_state_t *state, tavor_mboxlist_t *mblist,
46 tavor_mbox_t **mb, uint_t mbox_wait);
47 static void tavor_impl_mbox_free(tavor_mboxlist_t *mblist, tavor_mbox_t **mb);
48 static int tavor_impl_mboxlist_init(tavor_state_t *state,
49 tavor_mboxlist_t *mblist, uint_t num_mbox, tavor_rsrc_type_t type);
50 static void tavor_impl_mboxlist_fini(tavor_state_t *state,
51 tavor_mboxlist_t *mblist);
52 static int tavor_outstanding_cmd_alloc(tavor_state_t *state,
53 tavor_cmd_t **cmd_ptr, uint_t cmd_wait);
54 static void tavor_outstanding_cmd_free(tavor_state_t *state,
55 tavor_cmd_t **cmd_ptr);
56 static int tavor_write_hcr(tavor_state_t *state, tavor_cmd_post_t *cmdpost,
57 uint16_t token);
58 static void tavor_mbox_sync(tavor_mbox_t *mbox, uint_t offset,
59 uint_t length, uint_t flag);
60
61 /*
62 * tavor_cmd_post()
63 * Context: Can be called from interrupt or base context.
64 *
65 * The "cp_flags" field in cmdpost
66 * is used to determine whether to wait for an available
67 * outstanding command (if necessary) or to return error.
68 */
69 int
tavor_cmd_post(tavor_state_t * state,tavor_cmd_post_t * cmdpost)70 tavor_cmd_post(tavor_state_t *state, tavor_cmd_post_t *cmdpost)
71 {
72 tavor_cmd_t *cmdptr;
73 int status;
74 uint16_t token;
75
76 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdpost))
77
78 /* Determine if we are going to spin until completion */
79 if (cmdpost->cp_flags == TAVOR_CMD_NOSLEEP_SPIN) {
80
81 /* Write the command to the HCR */
82 status = tavor_write_hcr(state, cmdpost, 0);
83 if (status != TAVOR_CMD_SUCCESS) {
84 return (status);
85 }
86
87 return (TAVOR_CMD_SUCCESS);
88
89 } else { /* "TAVOR_CMD_SLEEP_NOSPIN" */
90
91 ASSERT(TAVOR_SLEEPFLAG_FOR_CONTEXT() != TAVOR_NOSLEEP);
92
93 /* NOTE: Expect threads to be waiting in here */
94 status = tavor_outstanding_cmd_alloc(state, &cmdptr,
95 cmdpost->cp_flags);
96 if (status != TAVOR_CMD_SUCCESS) {
97 return (status);
98 }
99 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdptr))
100
101 /*
102 * Set status to "TAVOR_CMD_INVALID_STATUS". It is
103 * appropriate to do this here without the "cmd_comp_lock"
104 * because this register is overloaded. Later it will be
105 * used to indicate - through a change from this invalid
106 * value to some other value - that the condition variable
107 * has been signaled. Once it has, status will then contain
108 * the _real_ completion status
109 */
110 cmdptr->cmd_status = TAVOR_CMD_INVALID_STATUS;
111
112 /* Write the command to the HCR */
113 token = (uint16_t)cmdptr->cmd_indx;
114 status = tavor_write_hcr(state, cmdpost, token);
115 if (status != TAVOR_CMD_SUCCESS) {
116 tavor_outstanding_cmd_free(state, &cmdptr);
117 return (status);
118 }
119
120 /*
121 * cv_wait() on the "command_complete" condition variable.
122 * Note: We have the "__lock_lint" here to workaround warlock.
123 * Since warlock doesn't know that other parts of the Tavor
124 * may occasionally call this routine while holding their own
125 * locks, it complains about this cv_wait. In reality,
126 * however, the rest of the driver never calls this routine
127 * with a lock held unless they pass TAVOR_CMD_NOSLEEP.
128 */
129 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*cmdptr))
130 mutex_enter(&cmdptr->cmd_comp_lock);
131 while (cmdptr->cmd_status == TAVOR_CMD_INVALID_STATUS) {
132 #ifndef __lock_lint
133 cv_wait(&cmdptr->cmd_comp_cv, &cmdptr->cmd_comp_lock);
134 /* NOTE: EXPECT SEVERAL THREADS TO BE WAITING HERE */
135 #endif
136 }
137 mutex_exit(&cmdptr->cmd_comp_lock);
138 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdptr))
139
140 /*
141 * Wake up after command completes (cv_signal). Read status
142 * from the command (success, fail, etc.). It is appropriate
143 * here (as above) to read the status field without the
144 * "cmd_comp_lock" because it is no longer being used to
145 * indicate whether the condition variable has been signaled
146 * (i.e. at this point we are certain that it already has).
147 */
148 status = cmdptr->cmd_status;
149
150 /* Save the "outparam" values into the cmdpost struct */
151 cmdpost->cp_outparm = cmdptr->cmd_outparm;
152
153 /*
154 * Add the command back to the "outstanding commands list".
155 * Signal the "cmd_list" condition variable, if necessary.
156 */
157 tavor_outstanding_cmd_free(state, &cmdptr);
158
159 if (status != TAVOR_CMD_SUCCESS) {
160 return (status);
161 }
162
163 return (TAVOR_CMD_SUCCESS);
164 }
165 }
166
167
168 /*
169 * tavor_mbox_alloc()
170 * Context: Can be called from interrupt or base context.
171 *
172 * The "mbox_wait" parameter is used to determine whether to
173 * wait for a mailbox to become available or not.
174 */
175 int
tavor_mbox_alloc(tavor_state_t * state,tavor_mbox_info_t * mbox_info,uint_t mbox_wait)176 tavor_mbox_alloc(tavor_state_t *state, tavor_mbox_info_t *mbox_info,
177 uint_t mbox_wait)
178 {
179 int status;
180 uint_t sleep_context;
181
182 sleep_context = TAVOR_SLEEPFLAG_FOR_CONTEXT();
183
184 /* Allocate an "In" mailbox */
185 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
186 /* Determine correct mboxlist based on calling context */
187 if (sleep_context == TAVOR_NOSLEEP) {
188 status = tavor_impl_mbox_alloc(state,
189 &state->ts_in_intr_mblist,
190 &mbox_info->mbi_in, mbox_wait);
191
192 ASSERT(status == TAVOR_CMD_SUCCESS);
193 } else {
194 /* NOTE: Expect threads to be waiting in here */
195 status = tavor_impl_mbox_alloc(state,
196 &state->ts_in_mblist, &mbox_info->mbi_in,
197 mbox_wait);
198 if (status != TAVOR_CMD_SUCCESS) {
199 return (status);
200 }
201 }
202
203 }
204
205 /* Allocate an "Out" mailbox */
206 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
207 /* Determine correct mboxlist based on calling context */
208 if (sleep_context == TAVOR_NOSLEEP) {
209 status = tavor_impl_mbox_alloc(state,
210 &state->ts_out_intr_mblist,
211 &mbox_info->mbi_out, mbox_wait);
212
213 ASSERT(status == TAVOR_CMD_SUCCESS);
214 } else {
215 /* NOTE: Expect threads to be waiting in here */
216 status = tavor_impl_mbox_alloc(state,
217 &state->ts_out_mblist, &mbox_info->mbi_out,
218 mbox_wait);
219 if (status != TAVOR_CMD_SUCCESS) {
220 /* If we allocated an "In" mailbox, free it */
221 if (mbox_info->mbi_alloc_flags &
222 TAVOR_ALLOC_INMBOX) {
223 tavor_impl_mbox_free(
224 &state->ts_in_mblist,
225 &mbox_info->mbi_in);
226 }
227 return (status);
228 }
229 }
230 }
231
232 /* Store appropriate context in mbox_info */
233 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(mbox_info->mbi_sleep_context))
234 mbox_info->mbi_sleep_context = sleep_context;
235
236 return (TAVOR_CMD_SUCCESS);
237 }
238
239
240 /*
241 * tavor_mbox_free()
242 * Context: Can be called from interrupt or base context.
243 */
244 void
tavor_mbox_free(tavor_state_t * state,tavor_mbox_info_t * mbox_info)245 tavor_mbox_free(tavor_state_t *state, tavor_mbox_info_t *mbox_info)
246 {
247 /*
248 * The mailbox has to be freed in the same context from which it was
249 * allocated. The context is stored in the mbox_info at
250 * tavor_mbox_alloc() time. We check the stored context against the
251 * current context here.
252 */
253 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(mbox_info->mbi_sleep_context))
254 ASSERT(mbox_info->mbi_sleep_context == TAVOR_SLEEPFLAG_FOR_CONTEXT());
255
256 /* Determine correct mboxlist based on calling context */
257 if (mbox_info->mbi_sleep_context == TAVOR_NOSLEEP) {
258 /* Free the intr "In" mailbox */
259 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
260 tavor_impl_mbox_free(&state->ts_in_intr_mblist,
261 &mbox_info->mbi_in);
262 }
263
264 /* Free the intr "Out" mailbox */
265 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
266 tavor_impl_mbox_free(&state->ts_out_intr_mblist,
267 &mbox_info->mbi_out);
268 }
269 } else {
270 /* Free the "In" mailbox */
271 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
272 tavor_impl_mbox_free(&state->ts_in_mblist,
273 &mbox_info->mbi_in);
274 }
275
276 /* Free the "Out" mailbox */
277 if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
278 tavor_impl_mbox_free(&state->ts_out_mblist,
279 &mbox_info->mbi_out);
280 }
281 }
282 }
283
284
285 /*
286 * tavor_cmd_complete_handler()
287 * Context: Called only from interrupt context.
288 */
289 int
tavor_cmd_complete_handler(tavor_state_t * state,tavor_eqhdl_t eq,tavor_hw_eqe_t * eqe)290 tavor_cmd_complete_handler(tavor_state_t *state, tavor_eqhdl_t eq,
291 tavor_hw_eqe_t *eqe)
292 {
293 tavor_cmd_t *cmdp;
294 uint_t eqe_evttype;
295
296 eqe_evttype = TAVOR_EQE_EVTTYPE_GET(eq, eqe);
297
298 ASSERT(eqe_evttype == TAVOR_EVT_COMMAND_INTF_COMP ||
299 eqe_evttype == TAVOR_EVT_EQ_OVERFLOW);
300
301 if (eqe_evttype == TAVOR_EVT_EQ_OVERFLOW) {
302 tavor_eq_overflow_handler(state, eq, eqe);
303
304 return (DDI_FAILURE);
305 }
306
307 /*
308 * Find the outstanding command pointer based on value returned
309 * in "token"
310 */
311 cmdp = &state->ts_cmd_list.cml_cmd[TAVOR_EQE_CMDTOKEN_GET(eq, eqe)];
312
313 /* Signal the waiting thread */
314 mutex_enter(&cmdp->cmd_comp_lock);
315 cmdp->cmd_outparm = ((uint64_t)TAVOR_EQE_CMDOUTP0_GET(eq, eqe) << 32) |
316 TAVOR_EQE_CMDOUTP1_GET(eq, eqe);
317 cmdp->cmd_status = TAVOR_EQE_CMDSTATUS_GET(eq, eqe);
318
319 cv_signal(&cmdp->cmd_comp_cv);
320 mutex_exit(&cmdp->cmd_comp_lock);
321
322 return (DDI_SUCCESS);
323 }
324
325
326 /*
327 * tavor_inmbox_list_init()
328 * Context: Only called from attach() path context
329 */
330 int
tavor_inmbox_list_init(tavor_state_t * state)331 tavor_inmbox_list_init(tavor_state_t *state)
332 {
333 int status;
334 uint_t num_inmbox;
335
336 /* Initialize the "In" mailbox list */
337 num_inmbox = (1 << state->ts_cfg_profile->cp_log_num_inmbox);
338 status = tavor_impl_mboxlist_init(state, &state->ts_in_mblist,
339 num_inmbox, TAVOR_IN_MBOX);
340 if (status != DDI_SUCCESS) {
341 return (DDI_FAILURE);
342 }
343
344 return (DDI_SUCCESS);
345 }
346
347
348 /*
349 * tavor_intr_inmbox_list_init()
350 * Context: Only called from attach() path context
351 */
352 int
tavor_intr_inmbox_list_init(tavor_state_t * state)353 tavor_intr_inmbox_list_init(tavor_state_t *state)
354 {
355 int status;
356 uint_t num_inmbox;
357
358 /* Initialize the interrupt "In" mailbox list */
359 num_inmbox = (1 << state->ts_cfg_profile->cp_log_num_intr_inmbox);
360 status = tavor_impl_mboxlist_init(state, &state->ts_in_intr_mblist,
361 num_inmbox, TAVOR_INTR_IN_MBOX);
362 if (status != DDI_SUCCESS) {
363 return (DDI_FAILURE);
364 }
365
366 return (DDI_SUCCESS);
367 }
368
369
370 /*
371 * tavor_outmbox_list_init()
372 * Context: Only called from attach() path context
373 */
374 int
tavor_outmbox_list_init(tavor_state_t * state)375 tavor_outmbox_list_init(tavor_state_t *state)
376 {
377 int status;
378 uint_t num_outmbox;
379
380 /* Initialize the "Out" mailbox list */
381 num_outmbox = (1 << state->ts_cfg_profile->cp_log_num_outmbox);
382 status = tavor_impl_mboxlist_init(state, &state->ts_out_mblist,
383 num_outmbox, TAVOR_OUT_MBOX);
384 if (status != DDI_SUCCESS) {
385 return (DDI_FAILURE);
386 }
387
388 return (DDI_SUCCESS);
389 }
390
391
392 /*
393 * tavor_intr_outmbox_list_init()
394 * Context: Only called from attach() path context
395 */
396 int
tavor_intr_outmbox_list_init(tavor_state_t * state)397 tavor_intr_outmbox_list_init(tavor_state_t *state)
398 {
399 int status;
400 uint_t num_outmbox;
401
402 /* Initialize the interrupts "Out" mailbox list */
403 num_outmbox = (1 << state->ts_cfg_profile->cp_log_num_intr_outmbox);
404 status = tavor_impl_mboxlist_init(state, &state->ts_out_intr_mblist,
405 num_outmbox, TAVOR_INTR_OUT_MBOX);
406 if (status != DDI_SUCCESS) {
407 return (DDI_FAILURE);
408 }
409
410 return (DDI_SUCCESS);
411 }
412
413
414 /*
415 * tavor_inmbox_list_fini()
416 * Context: Only called from attach() and/or detach() path contexts
417 */
418 void
tavor_inmbox_list_fini(tavor_state_t * state)419 tavor_inmbox_list_fini(tavor_state_t *state)
420 {
421 /* Free up the "In" mailbox list */
422 tavor_impl_mboxlist_fini(state, &state->ts_in_mblist);
423 }
424
425
426 /*
427 * tavor_intr_inmbox_list_fini()
428 * Context: Only called from attach() and/or detach() path contexts
429 */
430 void
tavor_intr_inmbox_list_fini(tavor_state_t * state)431 tavor_intr_inmbox_list_fini(tavor_state_t *state)
432 {
433 /* Free up the interupts "In" mailbox list */
434 tavor_impl_mboxlist_fini(state, &state->ts_in_intr_mblist);
435 }
436
437
438 /*
439 * tavor_outmbox_list_fini()
440 * Context: Only called from attach() and/or detach() path contexts
441 */
442 void
tavor_outmbox_list_fini(tavor_state_t * state)443 tavor_outmbox_list_fini(tavor_state_t *state)
444 {
445 /* Free up the "Out" mailbox list */
446 tavor_impl_mboxlist_fini(state, &state->ts_out_mblist);
447 }
448
449
450 /*
451 * tavor_intr_outmbox_list_fini()
452 * Context: Only called from attach() and/or detach() path contexts
453 */
454 void
tavor_intr_outmbox_list_fini(tavor_state_t * state)455 tavor_intr_outmbox_list_fini(tavor_state_t *state)
456 {
457 /* Free up the interrupt "Out" mailbox list */
458 tavor_impl_mboxlist_fini(state, &state->ts_out_intr_mblist);
459 }
460
461
462 /*
463 * tavor_impl_mbox_alloc()
464 * Context: Can be called from interrupt or base context.
465 */
466 static int
tavor_impl_mbox_alloc(tavor_state_t * state,tavor_mboxlist_t * mblist,tavor_mbox_t ** mb,uint_t mbox_wait)467 tavor_impl_mbox_alloc(tavor_state_t *state, tavor_mboxlist_t *mblist,
468 tavor_mbox_t **mb, uint_t mbox_wait)
469 {
470 tavor_mbox_t *mbox_ptr;
471 uint_t index, next, prev;
472 uint_t count, countmax;
473
474 /*
475 * If the mailbox list is empty, then wait (if appropriate in the
476 * current context). Otherwise, grab the next available mailbox.
477 */
478 if (mbox_wait == TAVOR_NOSLEEP) {
479 count = 0;
480 countmax = state->ts_cfg_profile->cp_cmd_poll_max;
481
482 mutex_enter(&mblist->mbl_lock);
483 mblist->mbl_pollers++;
484 while (mblist->mbl_entries_free == 0) {
485 mutex_exit(&mblist->mbl_lock);
486 /* Delay loop polling for an available mbox */
487 if (++count > countmax) {
488 return (TAVOR_CMD_INSUFF_RSRC);
489 }
490
491 /* Delay before polling for mailbox again */
492 drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);
493 mutex_enter(&mblist->mbl_lock);
494 }
495 mblist->mbl_pollers--;
496
497 /* TAVOR_SLEEP */
498 } else {
499 /*
500 * Grab lock here as we prepare to cv_wait if needed.
501 */
502 mutex_enter(&mblist->mbl_lock);
503 while (mblist->mbl_entries_free == 0) {
504 /*
505 * Wait (on cv) for a mailbox to become free. Note:
506 * Just as we do above in tavor_cmd_post(), we also
507 * have the "__lock_lint" here to workaround warlock.
508 * Warlock doesn't know that other parts of the Tavor
509 * may occasionally call this routine while holding
510 * their own locks, so it complains about this cv_wait.
511 * In reality, however, the rest of the driver never
512 * calls this routine with a lock held unless they pass
513 * TAVOR_CMD_NOSLEEP.
514 */
515 mblist->mbl_waiters++;
516 #ifndef __lock_lint
517 cv_wait(&mblist->mbl_cv, &mblist->mbl_lock);
518 #endif
519 }
520 }
521
522 /* Grab the next available mailbox from list */
523 mbox_ptr = mblist->mbl_mbox;
524 index = mblist->mbl_head_indx;
525 next = mbox_ptr[index].mb_next;
526 prev = mbox_ptr[index].mb_prev;
527
528 /* Remove it from the mailbox list */
529 mblist->mbl_mbox[next].mb_prev = prev;
530 mblist->mbl_mbox[prev].mb_next = next;
531 mblist->mbl_head_indx = next;
532
533 /* Update the "free" count and return the mailbox pointer */
534 mblist->mbl_entries_free--;
535 *mb = &mbox_ptr[index];
536
537 mutex_exit(&mblist->mbl_lock);
538
539 return (TAVOR_CMD_SUCCESS);
540 }
541
542
543 /*
544 * tavor_impl_mbox_free()
545 * Context: Can be called from interrupt or base context.
546 */
547 static void
tavor_impl_mbox_free(tavor_mboxlist_t * mblist,tavor_mbox_t ** mb)548 tavor_impl_mbox_free(tavor_mboxlist_t *mblist, tavor_mbox_t **mb)
549 {
550 uint_t mbox_indx;
551
552 mutex_enter(&mblist->mbl_lock);
553
554 /* Pull the "index" from mailbox entry */
555 mbox_indx = (*mb)->mb_indx;
556
557 /*
558 * If mailbox list is not empty, then insert the entry. Otherwise,
559 * this is the only entry. So update the pointers appropriately.
560 */
561 if (mblist->mbl_entries_free++ != 0) {
562 /* Update the current mailbox */
563 (*mb)->mb_next = mblist->mbl_head_indx;
564 (*mb)->mb_prev = mblist->mbl_tail_indx;
565
566 /* Update head and tail mailboxes */
567 mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev = mbox_indx;
568 mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next = mbox_indx;
569
570 /* Update tail index */
571 mblist->mbl_tail_indx = mbox_indx;
572
573 } else {
574 /* Update the current mailbox */
575 (*mb)->mb_next = mbox_indx;
576 (*mb)->mb_prev = mbox_indx;
577
578 /* Update head and tail indexes */
579 mblist->mbl_tail_indx = mbox_indx;
580 mblist->mbl_head_indx = mbox_indx;
581 }
582
583 /*
584 * Because we can have both waiters (SLEEP treads waiting for a
585 * cv_signal to continue processing) and pollers (NOSLEEP treads
586 * polling for a mailbox to become available), we try to share CPU time
587 * between them. We do this by signalling the waiters only every other
588 * call to mbox_free. This gives the pollers a chance to get some CPU
589 * time to do their command. If we signalled every time, the pollers
590 * would have a much harder time getting CPU time.
591 *
592 * If there are waiters and no pollers, then we signal always.
593 *
594 * Otherwise, if there are either no waiters, there may in fact be
595 * pollers, so we do not signal in that case.
596 */
597 if (mblist->mbl_pollers > 0 && mblist->mbl_waiters > 0) {
598 /* flip the signal value */
599 mblist->mbl_signal = (mblist->mbl_signal + 1) % 2;
600 } else if (mblist->mbl_waiters > 0) {
601 mblist->mbl_signal = 1;
602 } else {
603 mblist->mbl_signal = 0;
604 }
605
606 /*
607 * Depending on the conditions in the previous check, we signal only if
608 * we are supposed to.
609 */
610 if (mblist->mbl_signal) {
611 mblist->mbl_waiters--;
612 cv_signal(&mblist->mbl_cv);
613 }
614
615 /* Clear out the mailbox entry pointer */
616 *mb = NULL;
617
618 mutex_exit(&mblist->mbl_lock);
619 }
620
621
622 /*
623 * tavor_impl_mboxlist_init()
624 * Context: Only called from attach() path context
625 */
626 static int
tavor_impl_mboxlist_init(tavor_state_t * state,tavor_mboxlist_t * mblist,uint_t num_mbox,tavor_rsrc_type_t type)627 tavor_impl_mboxlist_init(tavor_state_t *state, tavor_mboxlist_t *mblist,
628 uint_t num_mbox, tavor_rsrc_type_t type)
629 {
630 tavor_rsrc_t *rsrc;
631 ddi_dma_cookie_t dma_cookie;
632 uint_t dma_cookiecnt, flag, sync;
633 int status, i;
634
635 /* Allocate the memory for the mailbox entries list */
636 mblist->mbl_list_sz = num_mbox;
637 mblist->mbl_mbox = kmem_zalloc(mblist->mbl_list_sz *
638 sizeof (tavor_mbox_t), KM_SLEEP);
639
640 /* Initialize the mailbox entries list */
641 mblist->mbl_head_indx = 0;
642 mblist->mbl_tail_indx = mblist->mbl_list_sz - 1;
643 mblist->mbl_entries_free = mblist->mbl_list_sz;
644 mblist->mbl_waiters = 0;
645 mblist->mbl_num_alloc = 0;
646
647 /* Set up the mailbox list's cv and mutex */
648 mutex_init(&mblist->mbl_lock, NULL, MUTEX_DRIVER,
649 DDI_INTR_PRI(state->ts_intrmsi_pri));
650 cv_init(&mblist->mbl_cv, NULL, CV_DRIVER, NULL);
651
652 /* Determine if syncs will be necessary */
653 sync = TAVOR_MBOX_IS_SYNC_REQ(state, type);
654
655 /* Determine whether to map DDI_DMA_STREAMING or DDI_DMA_CONSISTENT */
656 flag = state->ts_cfg_profile->cp_streaming_consistent;
657
658 /* Initialize the mailbox list entries */
659 for (i = 0; i < mblist->mbl_list_sz; i++) {
660 /* Allocate resources for the mailbox */
661 status = tavor_rsrc_alloc(state, type, 1, TAVOR_SLEEP,
662 &rsrc);
663 if (status != DDI_SUCCESS) {
664 /* Jump to cleanup and return error */
665 goto mboxlist_init_fail;
666 }
667
668 /* Save away the mailbox resource info */
669 mblist->mbl_mbox[i].mb_rsrcptr = rsrc;
670 mblist->mbl_mbox[i].mb_addr = rsrc->tr_addr;
671 mblist->mbl_mbox[i].mb_acchdl = rsrc->tr_acchdl;
672
673 /*
674 * Get a PCI mapped address for each mailbox. Note: this
675 * uses the ddi_dma_handle return from the resource
676 * allocation routine
677 */
678 status = ddi_dma_addr_bind_handle(rsrc->tr_dmahdl, NULL,
679 rsrc->tr_addr, rsrc->tr_len, (DDI_DMA_RDWR | flag),
680 DDI_DMA_SLEEP, NULL, &dma_cookie, &dma_cookiecnt);
681 if (status != DDI_SUCCESS) {
682 /* Jump to cleanup and return error */
683 tavor_rsrc_free(state, &rsrc);
684 goto mboxlist_init_fail;
685 }
686
687 /* Save away the mapped address for the mailbox */
688 mblist->mbl_mbox[i].mb_mapaddr = dma_cookie.dmac_laddress;
689
690 /* Set sync flag appropriately */
691 mblist->mbl_mbox[i].mb_sync = sync;
692
693 /* Make each entry point to the "next" and "prev" entries */
694 mblist->mbl_mbox[i].mb_next = i+1;
695 mblist->mbl_mbox[i].mb_prev = i-1;
696 mblist->mbl_mbox[i].mb_indx = i;
697 mblist->mbl_num_alloc = i + 1;
698 }
699
700 /* Make the "head" and "tail" entries point to each other */
701 mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev =
702 mblist->mbl_tail_indx;
703 mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next =
704 mblist->mbl_head_indx;
705
706 return (DDI_SUCCESS);
707
708 mboxlist_init_fail:
709 tavor_impl_mboxlist_fini(state, mblist);
710
711 return (DDI_FAILURE);
712 }
713
714
715 /*
716 * tavor_impl_mboxlist_fini()
717 * Context: Only called from attach() and/or detach() path contexts
718 */
719 static void
tavor_impl_mboxlist_fini(tavor_state_t * state,tavor_mboxlist_t * mblist)720 tavor_impl_mboxlist_fini(tavor_state_t *state, tavor_mboxlist_t *mblist)
721 {
722 tavor_rsrc_t *rsrc;
723 int i, status;
724
725 /* Release the resources for each of the mailbox list entries */
726 for (i = 0; i < mblist->mbl_num_alloc; i++) {
727 rsrc = mblist->mbl_mbox[i].mb_rsrcptr;
728
729 /*
730 * First, unbind the DMA memory for the mailbox
731 *
732 * Note: The only way ddi_dma_unbind_handle() currently
733 * can return an error is if the handle passed in is invalid.
734 * Since this should never happen, we choose to return void
735 * from this function! If this does return an error,
736 * however, then we print a warning message to the console.
737 */
738 status = ddi_dma_unbind_handle(rsrc->tr_dmahdl);
739 if (status != DDI_SUCCESS) {
740 TAVOR_WARNING(state, "failed to unbind DMA mapping");
741 return;
742 }
743
744 /* Next, free the mailbox resource */
745 tavor_rsrc_free(state, &rsrc);
746 }
747
748 /* Destroy the mailbox list mutex and cv */
749 mutex_destroy(&mblist->mbl_lock);
750 cv_destroy(&mblist->mbl_cv);
751
752 /* Free up the memory for tracking the mailbox list */
753 kmem_free(mblist->mbl_mbox, mblist->mbl_list_sz *
754 sizeof (tavor_mbox_t));
755 }
756
757
758 /*
759 * tavor_outstanding_cmd_alloc()
760 * Context: Can be called only from base context.
761 */
762 static int
tavor_outstanding_cmd_alloc(tavor_state_t * state,tavor_cmd_t ** cmd_ptr,uint_t cmd_wait)763 tavor_outstanding_cmd_alloc(tavor_state_t *state, tavor_cmd_t **cmd_ptr,
764 uint_t cmd_wait)
765 {
766 tavor_cmdlist_t *cmd_list;
767 uint_t next, prev, head;
768
769 cmd_list = &state->ts_cmd_list;
770 mutex_enter(&cmd_list->cml_lock);
771
772 /* Ensure that outstanding commands are supported */
773 ASSERT(cmd_list->cml_num_alloc != 0);
774
775 /*
776 * If the outstanding command list is empty, then wait (if
777 * appropriate in the current context). Otherwise, grab the
778 * next available command.
779 */
780 while (cmd_list->cml_entries_free == 0) {
781 /* No free commands */
782 if (cmd_wait == TAVOR_NOSLEEP) {
783 mutex_exit(&cmd_list->cml_lock);
784 return (TAVOR_CMD_INSUFF_RSRC);
785 }
786
787 /*
788 * Wait (on cv) for a command to become free. Note: Just
789 * as we do above in tavor_cmd_post(), we also have the
790 * "__lock_lint" here to workaround warlock. Warlock doesn't
791 * know that other parts of the Tavor may occasionally call
792 * this routine while holding their own locks, so it complains
793 * about this cv_wait. In reality, however, the rest of the
794 * driver never calls this routine with a lock held unless
795 * they pass TAVOR_CMD_NOSLEEP.
796 */
797 cmd_list->cml_waiters++;
798 #ifndef __lock_lint
799 cv_wait(&cmd_list->cml_cv, &cmd_list->cml_lock);
800 #endif
801 }
802
803 /* Grab the next available command from the list */
804 head = cmd_list->cml_head_indx;
805 *cmd_ptr = &cmd_list->cml_cmd[head];
806 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(**cmd_ptr))
807 next = (*cmd_ptr)->cmd_next;
808 prev = (*cmd_ptr)->cmd_prev;
809 (*cmd_ptr)->cmd_status = TAVOR_CMD_INVALID_STATUS;
810
811 /* Remove it from the command list */
812 cmd_list->cml_cmd[next].cmd_prev = prev;
813 cmd_list->cml_cmd[prev].cmd_next = next;
814 cmd_list->cml_head_indx = next;
815
816 /* Update the "free" count and return */
817 cmd_list->cml_entries_free--;
818
819 mutex_exit(&cmd_list->cml_lock);
820
821 return (TAVOR_CMD_SUCCESS);
822 }
823
824
825 /*
826 * tavor_outstanding_cmd_free()
827 * Context: Can be called only from base context.
828 */
829 static void
tavor_outstanding_cmd_free(tavor_state_t * state,tavor_cmd_t ** cmd_ptr)830 tavor_outstanding_cmd_free(tavor_state_t *state, tavor_cmd_t **cmd_ptr)
831 {
832 tavor_cmdlist_t *cmd_list;
833 uint_t cmd_indx;
834
835 cmd_list = &state->ts_cmd_list;
836 mutex_enter(&cmd_list->cml_lock);
837
838 /* Pull the "index" from command entry */
839 cmd_indx = (*cmd_ptr)->cmd_indx;
840
841 /*
842 * If outstanding command list is not empty, then insert the entry.
843 * Otherwise, this is the only entry. So update the pointers
844 * appropriately.
845 */
846 if (cmd_list->cml_entries_free++ != 0) {
847 /* Update the current command */
848 (*cmd_ptr)->cmd_next = cmd_list->cml_head_indx;
849 (*cmd_ptr)->cmd_prev = cmd_list->cml_tail_indx;
850
851 /* Update head and tail commands */
852 cmd_list->cml_cmd[cmd_list->cml_head_indx].cmd_prev = cmd_indx;
853 cmd_list->cml_cmd[cmd_list->cml_tail_indx].cmd_next = cmd_indx;
854
855 /* Update tail index */
856 cmd_list->cml_tail_indx = cmd_indx;
857
858 } else {
859 /* Update the current command */
860 (*cmd_ptr)->cmd_next = cmd_indx;
861 (*cmd_ptr)->cmd_prev = cmd_indx;
862
863 /* Update head and tail indexes */
864 cmd_list->cml_head_indx = cmd_indx;
865 cmd_list->cml_tail_indx = cmd_indx;
866 }
867
868 /* If there are threads waiting, signal one of them */
869 if (cmd_list->cml_waiters > 0) {
870 cmd_list->cml_waiters--;
871 cv_signal(&cmd_list->cml_cv);
872 }
873
874 /* Clear out the command entry pointer */
875 *cmd_ptr = NULL;
876
877 mutex_exit(&cmd_list->cml_lock);
878 }
879
880
881 /*
882 * tavor_write_hcr()
883 * Context: Can be called from interrupt or base context.
884 */
885 static int
tavor_write_hcr(tavor_state_t * state,tavor_cmd_post_t * cmdpost,uint16_t token)886 tavor_write_hcr(tavor_state_t *state, tavor_cmd_post_t *cmdpost,
887 uint16_t token)
888 {
889 tavor_hw_hcr_t *hcr;
890 uint_t status, count, countmax;
891 uint64_t hcrreg;
892
893 /*
894 * Grab the "HCR access" lock if the driver is not in
895 * fastreboot. In fastreboot, this function is called
896 * with the single thread but in high interrupt context
897 * (so that this mutex lock cannot be used).
898 */
899 #ifdef __lock_lint
900 mutex_enter(&state->ts_cmd_regs.hcr_lock);
901 #else
902 if (!TAVOR_IN_FASTREBOOT(state)) {
903 mutex_enter(&state->ts_cmd_regs.hcr_lock);
904 }
905 #endif
906
907 hcr = state->ts_cmd_regs.hcr;
908
909 /*
910 * First, check the "go" bit to see if the previous hcr usage is
911 * complete. As long as it is set then we must continue to poll.
912 */
913 count = 0;
914 countmax = state->ts_cfg_profile->cp_cmd_poll_max;
915 for (;;) {
916 hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->cmd);
917
918 /* If "go" bit is clear, then done */
919 if ((hcrreg & TAVOR_HCR_CMD_GO_MASK) == 0) {
920 break;
921 }
922 /* Delay before polling the "go" bit again */
923 drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);
924
925 /*
926 * If we poll more than the maximum number of times, then
927 * return a "timeout" error.
928 */
929 if (++count > countmax) {
930 #ifdef __lock_lint
931 mutex_exit(&state->ts_cmd_regs.hcr_lock);
932 #else
933 if (!TAVOR_IN_FASTREBOOT(state)) {
934 mutex_exit(&state->ts_cmd_regs.hcr_lock);
935 }
936 #endif
937 return (TAVOR_CMD_TIMEOUT);
938 }
939 }
940
941 /* Write "inparam" as a 64-bit quantity */
942 ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->in_param0,
943 cmdpost->cp_inparm);
944
945 /* Write "inmod" and 32-bits of "outparam" as 64-bit */
946 hcrreg = ((uint64_t)cmdpost->cp_inmod << 32);
947 hcrreg = hcrreg | (cmdpost->cp_outparm >> 32);
948 ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->input_modifier,
949 hcrreg);
950
951 /* Write the other 32-bits of "outparam" and "token" as 64-bit */
952 hcrreg = (cmdpost->cp_outparm << 32);
953 hcrreg = hcrreg | ((uint32_t)token << TAVOR_HCR_TOKEN_SHIFT);
954 ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->out_param1,
955 hcrreg);
956
957 /* Then setup the final hcrreg to hit doorbell (i.e. "go" bit) */
958 hcrreg = TAVOR_HCR_CMD_GO_MASK;
959 if (cmdpost->cp_flags == TAVOR_CMD_SLEEP_NOSPIN)
960 hcrreg = hcrreg | TAVOR_HCR_CMD_E_MASK;
961 hcrreg = hcrreg | (cmdpost->cp_opmod << TAVOR_HCR_CMD_OPMOD_SHFT);
962 hcrreg = hcrreg | (cmdpost->cp_opcode);
963
964 /* Write the doorbell to the HCR */
965 ddi_put32(state->ts_reg_cmdhdl, &hcr->cmd, hcrreg);
966
967 /*
968 * In the SPIN case we read the HCR and check the "go" bit. For the
969 * NOSPIN case we do not have to poll, we simply release the HCR lock
970 * and return.
971 */
972 if (cmdpost->cp_flags == TAVOR_CMD_NOSLEEP_SPIN) {
973 count = 0;
974 countmax = state->ts_cfg_profile->cp_cmd_poll_max;
975
976 for (;;) {
977 hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->cmd);
978
979 /* If "go" bit is clear, then done */
980 if ((hcrreg & TAVOR_HCR_CMD_GO_MASK) == 0) {
981 break;
982 }
983 /* Delay before polling the "go" bit again */
984 drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);
985
986 /*
987 * If we poll more than the maximum number of times,
988 * then return a "timeout" error.
989 */
990 if (++count > countmax) {
991 #ifdef __lock_lint
992 mutex_exit(&state-> ts_cmd_regs.hcr_lock);
993 #else
994 if (!TAVOR_IN_FASTREBOOT(state)) {
995 mutex_exit(&state->
996 ts_cmd_regs.hcr_lock);
997 }
998 #endif
999 return (TAVOR_CMD_TIMEOUT);
1000 }
1001 }
1002
1003 /* Pull out the "status" bits from the HCR */
1004 status = (hcrreg >> TAVOR_HCR_CMD_STATUS_SHFT);
1005
1006 /*
1007 * Read the "outparam" value. Note: we have to read "outparam"
1008 * as two separate 32-bit reads because the field in the HCR is
1009 * not 64-bit aligned.
1010 */
1011 hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->out_param0);
1012 cmdpost->cp_outparm = hcrreg << 32;
1013 hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->out_param1);
1014 cmdpost->cp_outparm |= hcrreg;
1015
1016 /* NOSPIN */
1017 } else {
1018 status = TAVOR_CMD_SUCCESS;
1019 }
1020
1021 /* Drop the "HCR access" lock */
1022 #ifdef __lock_lint
1023 mutex_exit(&state->ts_cmd_regs.hcr_lock);
1024 #else
1025 if (!TAVOR_IN_FASTREBOOT(state)) {
1026 mutex_exit(&state->ts_cmd_regs.hcr_lock);
1027 }
1028 #endif
1029
1030 return (status);
1031 }
1032
1033
1034 /*
1035 * tavor_outstanding_cmdlist_init()
1036 * Context: Only called from attach() path context
1037 */
1038 int
tavor_outstanding_cmdlist_init(tavor_state_t * state)1039 tavor_outstanding_cmdlist_init(tavor_state_t *state)
1040 {
1041 uint_t num_outstanding_cmds, head, tail;
1042 int i;
1043
1044 /*
1045 * Determine the number of the outstanding commands supported
1046 * by the Tavor device (obtained from the QUERY_FW command). Note:
1047 * Because we handle both SLEEP and NOSLEEP cases around the tavor HCR,
1048 * we know that when an interrupt comes in it will be next on the
1049 * command register, and will at most have to wait one commands time.
1050 * We do not have to reserve an outstanding command here for
1051 * interrupts.
1052 */
1053 num_outstanding_cmds = (1 << state->ts_fw.log_max_cmd);
1054
1055 /* Initialize the outstanding command list */
1056 state->ts_cmd_list.cml_list_sz = num_outstanding_cmds;
1057 state->ts_cmd_list.cml_head_indx = 0;
1058 state->ts_cmd_list.cml_tail_indx = state->ts_cmd_list.cml_list_sz - 1;
1059 state->ts_cmd_list.cml_entries_free = state->ts_cmd_list.cml_list_sz;
1060 state->ts_cmd_list.cml_waiters = 0;
1061 state->ts_cmd_list.cml_num_alloc = 0;
1062
1063 /* Allocate the memory for the outstanding command list */
1064 if (num_outstanding_cmds) {
1065 state->ts_cmd_list.cml_cmd =
1066 kmem_zalloc(state->ts_cmd_list.cml_list_sz *
1067 sizeof (tavor_cmd_t), KM_SLEEP);
1068 }
1069 mutex_init(&state->ts_cmd_list.cml_lock, NULL, MUTEX_DRIVER,
1070 DDI_INTR_PRI(state->ts_intrmsi_pri));
1071 cv_init(&state->ts_cmd_list.cml_cv, NULL, CV_DRIVER, NULL);
1072
1073 /* Initialize the individual outstanding command list entries */
1074 for (i = 0; i < state->ts_cmd_list.cml_list_sz; i++) {
1075 mutex_init(&state->ts_cmd_list.cml_cmd[i].cmd_comp_lock,
1076 NULL, MUTEX_DRIVER, DDI_INTR_PRI(state->ts_intrmsi_pri));
1077 cv_init(&state->ts_cmd_list.cml_cmd[i].cmd_comp_cv, NULL,
1078 CV_DRIVER, NULL);
1079
1080 state->ts_cmd_list.cml_cmd[i].cmd_next = i+1;
1081 state->ts_cmd_list.cml_cmd[i].cmd_prev = i-1;
1082 state->ts_cmd_list.cml_cmd[i].cmd_indx = i;
1083 state->ts_cmd_list.cml_num_alloc = i + 1;
1084 }
1085 if (num_outstanding_cmds) {
1086 head = state->ts_cmd_list.cml_head_indx;
1087 tail = state->ts_cmd_list.cml_tail_indx;
1088 state->ts_cmd_list.cml_cmd[head].cmd_prev =
1089 state->ts_cmd_list.cml_tail_indx;
1090 state->ts_cmd_list.cml_cmd[tail].cmd_next =
1091 state->ts_cmd_list.cml_head_indx;
1092 }
1093
1094 return (DDI_SUCCESS);
1095 }
1096
1097
1098 /*
1099 * tavor_outstanding_cmdlist_fini()
1100 * Context: Only called from attach() and/or detach() path contexts
1101 */
1102 void
tavor_outstanding_cmdlist_fini(tavor_state_t * state)1103 tavor_outstanding_cmdlist_fini(tavor_state_t *state)
1104 {
1105 int i;
1106
1107 /* Destroy the outstanding command list entries */
1108 for (i = 0; i < state->ts_cmd_list.cml_num_alloc; i++) {
1109 mutex_destroy(&state->ts_cmd_list.cml_cmd[i].cmd_comp_lock);
1110 cv_destroy(&state->ts_cmd_list.cml_cmd[i].cmd_comp_cv);
1111 }
1112
1113 /* Destroy the lock (and cv) and free up memory for list */
1114 mutex_destroy(&state->ts_cmd_list.cml_lock);
1115 cv_destroy(&state->ts_cmd_list.cml_cv);
1116 if (state->ts_cmd_list.cml_num_alloc) {
1117 kmem_free(state->ts_cmd_list.cml_cmd,
1118 state->ts_cmd_list.cml_list_sz * sizeof (tavor_cmd_t));
1119 }
1120
1121 }
1122
1123
1124 /*
1125 * tavor_mbox_sync()
1126 */
1127 static void
tavor_mbox_sync(tavor_mbox_t * mbox,uint_t offset,uint_t length,uint_t flag)1128 tavor_mbox_sync(tavor_mbox_t *mbox, uint_t offset, uint_t length,
1129 uint_t flag)
1130 {
1131 ddi_dma_handle_t dmahdl;
1132 int status;
1133
1134 /* Determine if mailbox needs to be synced or not */
1135 if (mbox->mb_sync == 0) {
1136 return;
1137 }
1138
1139 /* Get the DMA handle from mailbox */
1140 dmahdl = mbox->mb_rsrcptr->tr_dmahdl;
1141
1142 /* Calculate offset into mailbox */
1143 status = ddi_dma_sync(dmahdl, (off_t)offset, (size_t)length, flag);
1144 if (status != DDI_SUCCESS) {
1145 return;
1146 }
1147 }
1148
1149
1150 /*
1151 * tavor_sys_en_cmd_post()
1152 * Context: Can be called from interrupt or base context.
1153 * (Currently called only from attach() path context)
1154 */
1155 int
tavor_sys_en_cmd_post(tavor_state_t * state,uint_t flags,uint64_t * errorcode,uint_t sleepflag)1156 tavor_sys_en_cmd_post(tavor_state_t *state, uint_t flags,
1157 uint64_t *errorcode, uint_t sleepflag)
1158 {
1159 tavor_cmd_post_t cmd;
1160 int status;
1161
1162 /* Make sure we are called with the correct flag */
1163 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1164
1165 /* Setup and post the Tavor "SYS_EN" command */
1166 cmd.cp_inparm = 0;
1167 cmd.cp_outparm = 0;
1168 cmd.cp_inmod = 0;
1169 cmd.cp_opcode = SYS_EN;
1170 cmd.cp_opmod = flags;
1171 cmd.cp_flags = sleepflag;
1172 status = tavor_cmd_post(state, &cmd);
1173 if (status != TAVOR_CMD_SUCCESS) {
1174 /*
1175 * When the SYS_EN command fails, the "outparam" field may
1176 * contain more detailed information about what caused the
1177 * failure.
1178 */
1179 *errorcode = cmd.cp_outparm;
1180 }
1181
1182 return (status);
1183 }
1184
1185
1186 /*
1187 * tavor_sys_dis_cmd_post()
1188 * Context: Can be called from interrupt or base context.
1189 * (Currently called only from attach() and/or detach() path contexts)
1190 */
1191 int
tavor_sys_dis_cmd_post(tavor_state_t * state,uint_t sleepflag)1192 tavor_sys_dis_cmd_post(tavor_state_t *state, uint_t sleepflag)
1193 {
1194 tavor_cmd_post_t cmd;
1195 int status;
1196
1197 /* Make sure we are called with the correct flag */
1198 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1199
1200 /* Setup and post the Tavor "SYS_DIS" command */
1201 cmd.cp_inparm = 0;
1202 cmd.cp_outparm = 0;
1203 cmd.cp_inmod = 0;
1204 cmd.cp_opcode = SYS_DIS;
1205 cmd.cp_opmod = 0;
1206 cmd.cp_flags = sleepflag;
1207 status = tavor_cmd_post(state, &cmd);
1208
1209 return (status);
1210 }
1211
1212
1213 /*
1214 * tavor_init_hca_cmd_post()
1215 * Context: Can be called from interrupt or base context.
1216 * (Currently called only from attach() path context)
1217 */
1218 int
tavor_init_hca_cmd_post(tavor_state_t * state,tavor_hw_initqueryhca_t * inithca,uint_t sleepflag)1219 tavor_init_hca_cmd_post(tavor_state_t *state,
1220 tavor_hw_initqueryhca_t *inithca, uint_t sleepflag)
1221 {
1222 tavor_mbox_info_t mbox_info;
1223 tavor_cmd_post_t cmd;
1224 uint64_t data;
1225 uint_t size;
1226 int status, i;
1227
1228 /* Make sure we are called with the correct flag */
1229 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1230
1231 /* Get an "In" mailbox for the command */
1232 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
1233 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1234 if (status != TAVOR_CMD_SUCCESS) {
1235 return (status);
1236 }
1237
1238 /* Copy the Tavor "INIT_HCA" command into the mailbox */
1239 size = sizeof (tavor_hw_initqueryhca_t);
1240 for (i = 0; i < (size >> 3); i++) {
1241 data = ((uint64_t *)inithca)[i];
1242 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1243 ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
1244 }
1245
1246 /* Sync the mailbox for the device to read */
1247 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1248
1249 /* Setup and post the Tavor "INIT_HCA" command */
1250 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1251 cmd.cp_outparm = 0;
1252 cmd.cp_inmod = 0;
1253 cmd.cp_opcode = INIT_HCA;
1254 cmd.cp_opmod = 0;
1255 cmd.cp_flags = sleepflag;
1256 status = tavor_cmd_post(state, &cmd);
1257
1258 /* Free the mailbox */
1259 tavor_mbox_free(state, &mbox_info);
1260
1261 return (status);
1262 }
1263
1264
1265 /*
1266 * tavor_close_hca_cmd_post()
1267 * Context: Can be called from interrupt or base context.
1268 * (Currently called only from attach() and/or detach() path contexts)
1269 */
1270 int
tavor_close_hca_cmd_post(tavor_state_t * state,uint_t sleepflag)1271 tavor_close_hca_cmd_post(tavor_state_t *state, uint_t sleepflag)
1272 {
1273 tavor_cmd_post_t cmd;
1274 int status;
1275
1276 /* Make sure we are called with the correct flag */
1277 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1278
1279 /* Setup and post the Tavor "CLOSE_HCA" command */
1280 cmd.cp_inparm = 0;
1281 cmd.cp_outparm = 0;
1282 cmd.cp_inmod = 0;
1283 cmd.cp_opcode = CLOSE_HCA;
1284 cmd.cp_opmod = 0;
1285 cmd.cp_flags = sleepflag;
1286 status = tavor_cmd_post(state, &cmd);
1287
1288 return (status);
1289 }
1290
1291
1292 /*
1293 * tavor_init_ib_cmd_post()
1294 * Context: Can be called from interrupt or base context.
1295 * (Currently called only from attach() path context)
1296 */
1297 int
tavor_init_ib_cmd_post(tavor_state_t * state,tavor_hw_initib_t * initib,uint_t port,uint_t sleepflag)1298 tavor_init_ib_cmd_post(tavor_state_t *state, tavor_hw_initib_t *initib,
1299 uint_t port, uint_t sleepflag)
1300 {
1301 tavor_mbox_info_t mbox_info;
1302 tavor_cmd_post_t cmd;
1303 uint64_t data;
1304 uint_t size;
1305 int status, i;
1306
1307 /* Make sure we are called with the correct flag */
1308 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1309
1310 /* Get an "In" mailbox for the command */
1311 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
1312 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1313 if (status != TAVOR_CMD_SUCCESS) {
1314 return (status);
1315 }
1316
1317 /* Copy the Tavor "INIT_IB" command into the mailbox */
1318 size = sizeof (tavor_hw_initib_t);
1319 for (i = 0; i < (size >> 3); i++) {
1320 data = ((uint64_t *)initib)[i];
1321 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1322 ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
1323 }
1324
1325 /* Sync the mailbox for the device to read */
1326 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1327
1328 /* Setup and post the Tavor "INIT_IB" command */
1329 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1330 cmd.cp_outparm = 0;
1331 cmd.cp_inmod = port;
1332 cmd.cp_opcode = INIT_IB;
1333 cmd.cp_opmod = 0;
1334 cmd.cp_flags = sleepflag;
1335 status = tavor_cmd_post(state, &cmd);
1336
1337 /* Free the mailbox */
1338 tavor_mbox_free(state, &mbox_info);
1339
1340 return (status);
1341 }
1342
1343
1344 /*
1345 * tavor_close_ib_cmd_post()
1346 * Context: Can be called from interrupt or base context.
1347 * (Currently called only from attach() and/or detach() path contexts)
1348 */
1349 int
tavor_close_ib_cmd_post(tavor_state_t * state,uint_t port,uint_t sleepflag)1350 tavor_close_ib_cmd_post(tavor_state_t *state, uint_t port, uint_t sleepflag)
1351 {
1352 tavor_cmd_post_t cmd;
1353 int status;
1354
1355 /* Setup and post the Tavor "CLOSE_IB" command */
1356 cmd.cp_inparm = 0;
1357 cmd.cp_outparm = 0;
1358 cmd.cp_inmod = port;
1359 cmd.cp_opcode = CLOSE_IB;
1360 cmd.cp_opmod = 0;
1361 cmd.cp_flags = sleepflag;
1362 status = tavor_cmd_post(state, &cmd);
1363
1364 return (status);
1365 }
1366
1367
1368 /*
1369 * tavor_set_ib_cmd_post()
1370 * Context: Can be called from interrupt or base context.
1371 */
1372 int
tavor_set_ib_cmd_post(tavor_state_t * state,uint32_t capmask,uint_t port,uint_t reset_qkey,uint_t sleepflag)1373 tavor_set_ib_cmd_post(tavor_state_t *state, uint32_t capmask, uint_t port,
1374 uint_t reset_qkey, uint_t sleepflag)
1375 {
1376 tavor_mbox_info_t mbox_info;
1377 tavor_cmd_post_t cmd;
1378 int status;
1379
1380 /* Get an "In" mailbox for the command */
1381 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
1382 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1383 if (status != TAVOR_CMD_SUCCESS) {
1384 return (status);
1385 }
1386
1387 /* Copy the Tavor "SET_IB" command into mailbox */
1388 ddi_put32(mbox_info.mbi_in->mb_acchdl,
1389 ((uint32_t *)mbox_info.mbi_in->mb_addr + 0), reset_qkey);
1390 ddi_put32(mbox_info.mbi_in->mb_acchdl,
1391 ((uint32_t *)mbox_info.mbi_in->mb_addr + 1), capmask);
1392
1393 /* Sync the mailbox for the device to read */
1394 tavor_mbox_sync(mbox_info.mbi_in, 0, TAVOR_CMD_SETIB_SZ,
1395 DDI_DMA_SYNC_FORDEV);
1396
1397 /* Setup and post the Tavor "SET_IB" command */
1398 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1399 cmd.cp_outparm = 0;
1400 cmd.cp_inmod = port;
1401 cmd.cp_opcode = SET_IB;
1402 cmd.cp_opmod = 0;
1403 cmd.cp_flags = sleepflag;
1404 status = tavor_cmd_post(state, &cmd);
1405
1406 /* Free the mailbox */
1407 tavor_mbox_free(state, &mbox_info);
1408
1409 return (status);
1410 }
1411
1412
1413 /*
1414 * tavor_mod_stat_cfg_cmd_post()
1415 * Context: Can be called only from attach() path
1416 */
1417 int
tavor_mod_stat_cfg_cmd_post(tavor_state_t * state)1418 tavor_mod_stat_cfg_cmd_post(tavor_state_t *state)
1419 {
1420 tavor_mbox_info_t mbox_info;
1421 tavor_cmd_post_t cmd;
1422 tavor_hw_mod_stat_cfg_t *mod;
1423 uint64_t data;
1424 uint_t size;
1425 int status, i;
1426
1427 /*
1428 * "MOD_STAT_CFG" needs an INMBOX parameter, to specify what operations
1429 * to do. However, at the point in time that we call this command, the
1430 * DDR has not yet been initialized, and all INMBOX'es are located in
1431 * DDR. Because we want to call MOD_STAT_CFG before QUERY_DEVLIM is
1432 * called, and thus call it before DDR is setup, we simply use an
1433 * OUTMBOX memory location here as our INMBOX parameter.
1434 */
1435 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
1436 status = tavor_mbox_alloc(state, &mbox_info, TAVOR_NOSLEEP);
1437 if (status != TAVOR_CMD_SUCCESS) {
1438 return (status);
1439 }
1440
1441 /*
1442 * Allocate on the heap our 'mod_stat_cfg' structure. We want to
1443 * ideally move all of this on to the stack in the future, but this
1444 * works well for now.
1445 */
1446 mod = (tavor_hw_mod_stat_cfg_t *)kmem_zalloc(
1447 sizeof (tavor_hw_mod_stat_cfg_t), KM_SLEEP);
1448
1449 /* Setup "MOD_STAT_CFG" settings */
1450 mod->srq_m = 1;
1451 mod->srq = state->ts_cfg_profile->cp_srq_enable;
1452
1453 if (mod->srq) {
1454 mod->log_max_srq = state->ts_cfg_profile->cp_log_num_srq;
1455 } else {
1456 mod->log_max_srq = 0;
1457 }
1458
1459 /* Copy the "MOD_STAT_CFG" command into the "In" mailbox */
1460 size = sizeof (tavor_hw_mod_stat_cfg_t);
1461 for (i = 0; i < (size >> 3); i++) {
1462 data = ((uint64_t *)mod)[i];
1463 ddi_put64(mbox_info.mbi_out->mb_acchdl,
1464 ((uint64_t *)mbox_info.mbi_out->mb_addr + i), data);
1465 }
1466
1467 /* Sync the mailbox for the device to read */
1468 tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORDEV);
1469
1470 /* Setup and post the Tavor "MOD_STAT_CFG" command */
1471 cmd.cp_inparm = mbox_info.mbi_out->mb_mapaddr;
1472 cmd.cp_outparm = 0;
1473 cmd.cp_inmod = 0;
1474 cmd.cp_opcode = MOD_STAT_CFG;
1475 cmd.cp_opmod = 0;
1476 cmd.cp_flags = TAVOR_CMD_NOSLEEP_SPIN;
1477 status = tavor_cmd_post(state, &cmd);
1478
1479 /* Free "MOD_STAT_CFG" struct */
1480 kmem_free(mod, sizeof (tavor_hw_mod_stat_cfg_t));
1481
1482 /* Free the mailbox */
1483 tavor_mbox_free(state, &mbox_info);
1484
1485 return (status);
1486 }
1487
1488
1489 /*
1490 * tavor_mad_ifc_cmd_post()
1491 * Context: Can be called from interrupt or base context.
1492 */
1493 int
tavor_mad_ifc_cmd_post(tavor_state_t * state,uint_t port,uint_t sleepflag,uint32_t * mad,uint32_t * resp)1494 tavor_mad_ifc_cmd_post(tavor_state_t *state, uint_t port,
1495 uint_t sleepflag, uint32_t *mad, uint32_t *resp)
1496 {
1497 tavor_mbox_info_t mbox_info;
1498 tavor_cmd_post_t cmd;
1499 uint_t size;
1500 int status;
1501
1502 /* Get "In" and "Out" mailboxes for the command */
1503 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1504 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1505 if (status != TAVOR_CMD_SUCCESS) {
1506 return (status);
1507 }
1508
1509 /* Copy the request MAD into the "In" mailbox */
1510 size = TAVOR_CMD_MAD_IFC_SIZE;
1511 bcopy(mad, mbox_info.mbi_in->mb_addr, size);
1512
1513 /* Sync the mailbox for the device to read */
1514 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1515
1516 /* Setup the Tavor "MAD_IFC" command */
1517 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1518 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1519 cmd.cp_inmod = port;
1520 cmd.cp_opcode = MAD_IFC;
1521 cmd.cp_opmod = TAVOR_CMD_MKEY_CHECK; /* Enable MKey checking */
1522 cmd.cp_flags = sleepflag;
1523 status = tavor_cmd_post(state, &cmd);
1524 if (status != TAVOR_CMD_SUCCESS) {
1525 goto mad_ifc_fail;
1526 }
1527
1528 /* Sync the mailbox to read the results */
1529 tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
1530
1531 /* Copy the response MAD into "resp" */
1532 bcopy(mbox_info.mbi_out->mb_addr, resp, size);
1533
1534 mad_ifc_fail:
1535 /* Free the mailbox */
1536 tavor_mbox_free(state, &mbox_info);
1537
1538 return (status);
1539 }
1540
1541
1542 /*
1543 * tavor_getportinfo_cmd_post()
1544 * Context: Can be called from interrupt or base context.
1545 */
1546 int
tavor_getportinfo_cmd_post(tavor_state_t * state,uint_t port,uint_t sleepflag,sm_portinfo_t * portinfo)1547 tavor_getportinfo_cmd_post(tavor_state_t *state, uint_t port,
1548 uint_t sleepflag, sm_portinfo_t *portinfo)
1549 {
1550 tavor_mbox_info_t mbox_info;
1551 tavor_cmd_post_t cmd;
1552 uint32_t *mbox;
1553 uint_t size;
1554 int status, i;
1555
1556 /* Get "In" and "Out" mailboxes for the command */
1557 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1558 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1559 if (status != TAVOR_CMD_SUCCESS) {
1560 return (status);
1561 }
1562
1563 /* Build the GetPortInfo request MAD in the "In" mailbox */
1564 size = TAVOR_CMD_MAD_IFC_SIZE;
1565 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1566 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
1567 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
1568 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
1569 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
1570 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PORTINFO);
1571 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], port);
1572 for (i = 6; i < (size >> 2); i++) {
1573 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1574 }
1575
1576 /* Sync the mailbox for the device to read */
1577 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1578
1579 /* Setup the Tavor "MAD_IFC" command */
1580 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1581 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1582 cmd.cp_inmod = port;
1583 cmd.cp_opcode = MAD_IFC;
1584 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK; /* No MKey checking */
1585 cmd.cp_flags = sleepflag;
1586 status = tavor_cmd_post(state, &cmd);
1587 if (status != TAVOR_CMD_SUCCESS) {
1588 goto getportinfo_fail;
1589 }
1590
1591 /* Sync the mailbox to read the results */
1592 size = sizeof (sm_portinfo_t);
1593 tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
1594 size, DDI_DMA_SYNC_FORCPU);
1595
1596 /*
1597 * Copy GetPortInfo response MAD into "portinfo". Do any endian
1598 * swapping that may be necessary to flip any of the "portinfo"
1599 * fields
1600 */
1601 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*portinfo))
1602 bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1603 TAVOR_CMD_MADDATA_OFFSET), portinfo, size);
1604 TAVOR_GETPORTINFO_SWAP(portinfo);
1605 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*portinfo))
1606
1607 getportinfo_fail:
1608 /* Free the mailbox */
1609 tavor_mbox_free(state, &mbox_info);
1610
1611 return (status);
1612 }
1613
1614
1615 /*
1616 * tavor_getnodeinfo_cmd_post()
1617 * Context: Can be called from interrupt or base context.
1618 * (Currently called only from attach() and detach() path contexts)
1619 */
1620 int
tavor_getnodeinfo_cmd_post(tavor_state_t * state,uint_t sleepflag,sm_nodeinfo_t * nodeinfo)1621 tavor_getnodeinfo_cmd_post(tavor_state_t *state, uint_t sleepflag,
1622 sm_nodeinfo_t *nodeinfo)
1623 {
1624 tavor_mbox_info_t mbox_info;
1625 tavor_cmd_post_t cmd;
1626 uint32_t *mbox;
1627 uint_t size;
1628 int status, i;
1629
1630 /* Make sure we are called with the correct flag */
1631 ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);
1632
1633 /* Get "In" and "Out" mailboxes for the command */
1634 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1635 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1636 if (status != TAVOR_CMD_SUCCESS) {
1637 return (status);
1638 }
1639
1640 /* Build the GetNodeInfo request MAD into the "In" mailbox */
1641 size = TAVOR_CMD_MAD_IFC_SIZE;
1642 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1643 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
1644 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
1645 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
1646 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
1647 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_NODEINFO);
1648 for (i = 5; i < (size >> 2); i++) {
1649 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1650 }
1651
1652 /* Sync the mailbox for the device to read */
1653 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1654
1655 /* Setup the Tavor "MAD_IFC" command */
1656 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1657 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1658 cmd.cp_inmod = 1; /* Get NodeInfo from port #1 */
1659 cmd.cp_opcode = MAD_IFC;
1660 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK; /* No MKey checking */
1661 cmd.cp_flags = sleepflag;
1662 status = tavor_cmd_post(state, &cmd);
1663 if (status != TAVOR_CMD_SUCCESS) {
1664 goto getnodeinfo_fail;
1665 }
1666
1667 /* Sync the mailbox to read the results */
1668 size = sizeof (sm_nodeinfo_t);
1669 tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
1670 size, DDI_DMA_SYNC_FORCPU);
1671
1672 /*
1673 * Copy GetNodeInfo response MAD into "nodeinfo". Do any endian
1674 * swapping that may be necessary to flip any of the "nodeinfo"
1675 * fields
1676 */
1677 bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1678 TAVOR_CMD_MADDATA_OFFSET), nodeinfo, size);
1679 TAVOR_GETNODEINFO_SWAP(nodeinfo);
1680
1681 getnodeinfo_fail:
1682 /* Free the mailbox */
1683 tavor_mbox_free(state, &mbox_info);
1684
1685 return (status);
1686 }
1687
1688
1689 /*
1690 * tavor_getnodedesc_cmd_post()
1691 * Context: Can be called from interrupt or base context.
1692 * (Currently called only from attach() and detach() path contexts)
1693 */
1694 int
tavor_getnodedesc_cmd_post(tavor_state_t * state,uint_t sleepflag,sm_nodedesc_t * nodedesc)1695 tavor_getnodedesc_cmd_post(tavor_state_t *state, uint_t sleepflag,
1696 sm_nodedesc_t *nodedesc)
1697 {
1698 tavor_mbox_info_t mbox_info;
1699 tavor_cmd_post_t cmd;
1700 uint32_t *mbox;
1701 uint_t size;
1702 int status, i;
1703
1704 /* Get "In" and "Out" mailboxes for the command */
1705 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1706 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1707 if (status != TAVOR_CMD_SUCCESS) {
1708 return (status);
1709 }
1710
1711 /* Build the GetNodeDesc request MAD into the "In" mailbox */
1712 size = TAVOR_CMD_MAD_IFC_SIZE;
1713 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1714 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
1715 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
1716 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
1717 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
1718 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_NODEDESC);
1719 for (i = 5; i < (size >> 2); i++) {
1720 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1721 }
1722
1723 /* Sync the mailbox for the device to read */
1724 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1725
1726 /* Setup the Tavor "MAD_IFC" command */
1727 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1728 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1729 cmd.cp_inmod = 1; /* Get NodeDesc from port #1 */
1730 cmd.cp_opcode = MAD_IFC;
1731 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK; /* No MKey checking */
1732 cmd.cp_flags = sleepflag;
1733 status = tavor_cmd_post(state, &cmd);
1734 if (status != TAVOR_CMD_SUCCESS) {
1735 goto getnodedesc_fail;
1736 }
1737
1738 /* Sync the mailbox to read the results */
1739 size = sizeof (sm_nodedesc_t);
1740 tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
1741 size, DDI_DMA_SYNC_FORCPU);
1742
1743 /* Copy GetNodeDesc response MAD into "nodedesc" */
1744 bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1745 TAVOR_CMD_MADDATA_OFFSET), nodedesc, size);
1746
1747 getnodedesc_fail:
1748 /* Free the mailbox */
1749 tavor_mbox_free(state, &mbox_info);
1750
1751 return (status);
1752 }
1753
1754
1755 /*
1756 * tavor_getguidinfo_cmd_post()
1757 * Context: Can be called from interrupt or base context.
1758 */
1759 int
tavor_getguidinfo_cmd_post(tavor_state_t * state,uint_t port,uint_t guidblock,uint_t sleepflag,sm_guidinfo_t * guidinfo)1760 tavor_getguidinfo_cmd_post(tavor_state_t *state, uint_t port,
1761 uint_t guidblock, uint_t sleepflag, sm_guidinfo_t *guidinfo)
1762 {
1763 tavor_mbox_info_t mbox_info;
1764 tavor_cmd_post_t cmd;
1765 uint32_t *mbox;
1766 uint_t size;
1767 int status, i;
1768
1769 /* Get "In" and "Out" mailboxes for the command */
1770 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1771 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1772 if (status != TAVOR_CMD_SUCCESS) {
1773 return (status);
1774 }
1775
1776 /* Build the GetGUIDInfo request MAD into the "In" mailbox */
1777 size = TAVOR_CMD_MAD_IFC_SIZE;
1778 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1779 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
1780 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
1781 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
1782 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
1783 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_GUIDINFO);
1784 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], guidblock);
1785 for (i = 6; i < (size >> 2); i++) {
1786 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1787 }
1788
1789 /* Sync the mailbox for the device to read */
1790 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1791
1792 /* Setup the Tavor "MAD_IFC" command */
1793 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1794 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1795 cmd.cp_inmod = port;
1796 cmd.cp_opcode = MAD_IFC;
1797 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK; /* No MKey checking */
1798 cmd.cp_flags = sleepflag;
1799 status = tavor_cmd_post(state, &cmd);
1800 if (status != TAVOR_CMD_SUCCESS) {
1801 goto getguidinfo_fail;
1802 }
1803
1804 /* Sync the mailbox to read the results */
1805 size = sizeof (sm_guidinfo_t);
1806 tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
1807 size, DDI_DMA_SYNC_FORCPU);
1808
1809 /*
1810 * Copy GetGUIDInfo response MAD into "guidinfo". Do any endian
1811 * swapping that may be necessary to flip the "guidinfo" fields
1812 */
1813 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*guidinfo))
1814 bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1815 TAVOR_CMD_MADDATA_OFFSET), guidinfo, size);
1816 TAVOR_GETGUIDINFO_SWAP(guidinfo);
1817 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*guidinfo))
1818
1819 getguidinfo_fail:
1820 /* Free the mailbox */
1821 tavor_mbox_free(state, &mbox_info);
1822
1823 return (status);
1824 }
1825
1826
1827 /*
1828 * tavor_getpkeytable_cmd_post()
1829 * Context: Can be called from interrupt or base context.
1830 */
1831 int
tavor_getpkeytable_cmd_post(tavor_state_t * state,uint_t port,uint_t pkeyblock,uint_t sleepflag,sm_pkey_table_t * pkeytable)1832 tavor_getpkeytable_cmd_post(tavor_state_t *state, uint_t port,
1833 uint_t pkeyblock, uint_t sleepflag, sm_pkey_table_t *pkeytable)
1834 {
1835 tavor_mbox_info_t mbox_info;
1836 tavor_cmd_post_t cmd;
1837 uint32_t *mbox;
1838 uint_t size;
1839 int status, i;
1840
1841 /* Get "In" and "Out" mailboxes for the command */
1842 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
1843 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
1844 if (status != TAVOR_CMD_SUCCESS) {
1845 return (status);
1846 }
1847
1848 /* Build the GetPkeyTable request MAD into the "In" mailbox */
1849 size = TAVOR_CMD_MAD_IFC_SIZE;
1850 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1851 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
1852 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
1853 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
1854 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
1855 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PKEYTBLE);
1856 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], pkeyblock);
1857 for (i = 6; i < (size >> 2); i++) {
1858 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1859 }
1860
1861 /* Sync the mailbox for the device to read */
1862 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1863
1864 /* Setup the Tavor "MAD_IFC" command */
1865 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
1866 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
1867 cmd.cp_inmod = port;
1868 cmd.cp_opcode = MAD_IFC;
1869 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK; /* No MKey checking */
1870 cmd.cp_flags = sleepflag;
1871 status = tavor_cmd_post(state, &cmd);
1872 if (status != TAVOR_CMD_SUCCESS) {
1873 goto getpkeytable_fail;
1874 }
1875
1876 /* Sync the mailbox to read the results */
1877 size = sizeof (sm_pkey_table_t);
1878 tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
1879 size, DDI_DMA_SYNC_FORCPU);
1880
1881 /*
1882 * Copy GetPKeyTable response MAD into "pkeytable". Do any endian
1883 * swapping that may be necessary to flip the "pkeytable" fields
1884 */
1885 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pkeytable))
1886 bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1887 TAVOR_CMD_MADDATA_OFFSET), pkeytable, size);
1888 TAVOR_GETPKEYTABLE_SWAP(pkeytable);
1889 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*pkeytable))
1890
1891 getpkeytable_fail:
1892 /* Free the mailbox */
1893 tavor_mbox_free(state, &mbox_info);
1894
1895 return (status);
1896 }
1897
1898
1899 /*
1900 * tavor_write_mtt_cmd_post()
1901 * Context: Can be called from interrupt or base context.
1902 */
1903 int
tavor_write_mtt_cmd_post(tavor_state_t * state,tavor_mbox_info_t * mbox_info,uint_t num_mtt,uint_t sleepflag)1904 tavor_write_mtt_cmd_post(tavor_state_t *state, tavor_mbox_info_t *mbox_info,
1905 uint_t num_mtt, uint_t sleepflag)
1906 {
1907 tavor_cmd_post_t cmd;
1908 uint_t size;
1909 int status;
1910
1911 /*
1912 * The WRITE_MTT command is unlike the other commands we use, in that
1913 * we have intentionally separated the mailbox allocation step from
1914 * the rest of the command posting steps. At this point (when this
1915 * function is called) the "In" mailbox already contains all the MTT
1916 * entries to be copied into the Tavor tables (starting at offset
1917 * 0x10) _and_ the 64-bit address of the destination for the first
1918 * MTT entry in the MTT table.
1919 */
1920
1921 /* Sync the mailbox for the device to read */
1922 size = (num_mtt << TAVOR_MTT_SIZE_SHIFT) + TAVOR_CMD_WRITEMTT_RSVD_SZ;
1923 tavor_mbox_sync(mbox_info->mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1924
1925 /* Setup and post Tavor "WRITE_MTT" command */
1926 cmd.cp_inparm = mbox_info->mbi_in->mb_mapaddr;
1927 cmd.cp_outparm = 0;
1928 cmd.cp_inmod = num_mtt;
1929 cmd.cp_opcode = WRITE_MTT;
1930 cmd.cp_opmod = 0;
1931 cmd.cp_flags = sleepflag;
1932 status = tavor_cmd_post(state, &cmd);
1933
1934 return (status);
1935 }
1936
1937
1938 /*
1939 * tavor_sync_tpt_cmd_post()
1940 * Context: Can be called from interrupt or base context.
1941 */
1942 int
tavor_sync_tpt_cmd_post(tavor_state_t * state,uint_t sleepflag)1943 tavor_sync_tpt_cmd_post(tavor_state_t *state, uint_t sleepflag)
1944 {
1945 tavor_cmd_post_t cmd;
1946 int status;
1947
1948 /* Setup and post the Tavor "SYNC_TPT" command */
1949 cmd.cp_inparm = 0;
1950 cmd.cp_outparm = 0;
1951 cmd.cp_inmod = 0;
1952 cmd.cp_opcode = SYNC_TPT;
1953 cmd.cp_opmod = 0;
1954 cmd.cp_flags = sleepflag;
1955 status = tavor_cmd_post(state, &cmd);
1956
1957 return (status);
1958 }
1959
1960 /*
1961 * tavor_map_eq_cmd_post()
1962 * Context: Can be called from interrupt or base context.
1963 * (Currently called only from attach() and/or detach() path contexts)
1964 */
1965 int
tavor_map_eq_cmd_post(tavor_state_t * state,uint_t map,uint_t eqcindx,uint64_t eqmapmask,uint_t sleepflag)1966 tavor_map_eq_cmd_post(tavor_state_t *state, uint_t map, uint_t eqcindx,
1967 uint64_t eqmapmask, uint_t sleepflag)
1968 {
1969 tavor_cmd_post_t cmd;
1970 int status;
1971
1972 /* Setup and post Tavor "MAP_EQ" command */
1973 cmd.cp_inparm = eqmapmask;
1974 cmd.cp_outparm = 0;
1975 cmd.cp_inmod = eqcindx;
1976 if (map != TAVOR_CMD_MAP_EQ_EVT_MAP) {
1977 cmd.cp_inmod |= TAVOR_CMD_UNMAP_EQ_MASK;
1978 }
1979 cmd.cp_opcode = MAP_EQ;
1980 cmd.cp_opmod = 0;
1981 cmd.cp_flags = sleepflag;
1982 status = tavor_cmd_post(state, &cmd);
1983
1984 return (status);
1985 }
1986
1987
1988 /*
1989 * tavor_resize_cq_cmd_post()
1990 * Context: Can be called from interrupt or base context.
1991 */
1992 int
tavor_resize_cq_cmd_post(tavor_state_t * state,tavor_hw_cqc_t * cqc,uint_t cqcindx,uint32_t * prod_indx,uint_t sleepflag)1993 tavor_resize_cq_cmd_post(tavor_state_t *state, tavor_hw_cqc_t *cqc,
1994 uint_t cqcindx, uint32_t *prod_indx, uint_t sleepflag)
1995 {
1996 tavor_mbox_info_t mbox_info;
1997 tavor_cmd_post_t cmd;
1998 uint64_t data;
1999 uint_t size;
2000 int status, i;
2001
2002 /* Get an "In" mailbox for the command */
2003 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2004 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2005 if (status != TAVOR_CMD_SUCCESS) {
2006 return (status);
2007 }
2008
2009 /* Copy the Tavor "RESIZE_CQ" command into mailbox */
2010 size = sizeof (tavor_hw_cqc_t);
2011 for (i = 0; i < (size >> 3); i++) {
2012 data = ((uint64_t *)cqc)[i];
2013 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2014 ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
2015 }
2016
2017 /* Sync the mailbox for the device to read */
2018 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2019
2020 /* Setup and post Tavor "RESIZE_CQ" command */
2021 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
2022 cmd.cp_outparm = 0;
2023 cmd.cp_inmod = cqcindx;
2024 cmd.cp_opcode = RESIZE_CQ;
2025 cmd.cp_opmod = 0;
2026 cmd.cp_flags = sleepflag;
2027 status = tavor_cmd_post(state, &cmd);
2028
2029 /*
2030 * New "producer index" is returned in the upper 32 bits of
2031 * command "outparam"
2032 */
2033 *prod_indx = (cmd.cp_outparm >> 32);
2034
2035 /* Free the mailbox */
2036 tavor_mbox_free(state, &mbox_info);
2037
2038 return (status);
2039 }
2040
2041
2042 /*
2043 * tavor_cmn_qp_cmd_post()
2044 * Context: Can be called from interrupt or base context.
2045 *
2046 * This is the common function for posting all the various types of
2047 * QP state transition related Tavor commands. Since some of the
2048 * commands differ from the others in the number (and type) of arguments
2049 * that each require, this routine does checks based on opcode type
2050 * (explained in more detail below).
2051 *
2052 * Note: This common function should be used only with the following
2053 * opcodes: RTS2SQD_QP, TOERR_QP, TORST_QP, RST2INIT_QP, INIT2INIT_QP,
2054 * INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP, SQD2RTS_QP, and SQERR2RTS_QP.
2055 */
2056 int
tavor_cmn_qp_cmd_post(tavor_state_t * state,uint_t opcode,tavor_hw_qpc_t * qp,uint_t qpindx,uint32_t opmask,uint_t sleepflag)2057 tavor_cmn_qp_cmd_post(tavor_state_t *state, uint_t opcode,
2058 tavor_hw_qpc_t *qp, uint_t qpindx, uint32_t opmask,
2059 uint_t sleepflag)
2060 {
2061 tavor_mbox_info_t mbox_info;
2062 tavor_cmd_post_t cmd;
2063 uint64_t data, in_mapaddr, out_mapaddr;
2064 uint_t size, flags, opmod;
2065 int status, i;
2066
2067 /*
2068 * Use the specified opcode type to set the appropriate parameters.
2069 * Specifically, we need to set in_mapaddr, out_mapaddr, flags, and
2070 * opmod (as necessary). Setting these parameters may also require
2071 * us to allocate an "In" or "Out" mailbox depending on the command
2072 * type.
2073 */
2074 if (opcode == RTS2SQD_QP) {
2075 /*
2076 * Note: For RTS-to-SendQueueDrain state transitions we
2077 * always want to request the event generation from the
2078 * hardware. Though we may not notify the consumer of the
2079 * drained event, the decision to forward (or not) is made
2080 * later in the SQD event handler.
2081 */
2082 flags = TAVOR_CMD_REQ_SQD_EVENT;
2083
2084 /*
2085 * The RTS2SQD_QP command uses no "In" or "Out" mailboxes (and
2086 * has no special opcode modifiers).
2087 */
2088 in_mapaddr = 0;
2089 out_mapaddr = 0;
2090 opmod = 0;
2091
2092 } else if (opcode == TOERR_QP) {
2093 /*
2094 * The TOERR_QP command uses no "In" or "Out" mailboxes, has no
2095 * special opcode modifiers, and takes no special flags.
2096 */
2097 in_mapaddr = 0;
2098 out_mapaddr = 0;
2099 opmod = 0;
2100 flags = 0;
2101
2102 } else if (opcode == TORST_QP) {
2103 /*
2104 * The TORST_QP command could take an "Out" mailbox, but we do
2105 * not require it here. It also does not takes any special
2106 * flags. It does however, take a TAVOR_CMD_DIRECT_TO_RESET
2107 * opcode modifier, which indicates that the transition to
2108 * reset should happen without first moving the QP through the
2109 * Error state (and, hence, without generating any unnecessary
2110 * "flushed-in-error" completions).
2111 */
2112 in_mapaddr = 0;
2113 out_mapaddr = 0;
2114 opmod = TAVOR_CMD_DIRECT_TO_RESET | TAVOR_CMD_NO_OUTMBOX;
2115 flags = 0;
2116
2117 } else {
2118 /*
2119 * All the other QP state transition commands (RST2INIT_QP,
2120 * INIT2INIT_QP, INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP,
2121 * SQD2RTS_QP, and SQERR2RTS_QP) require an "In" mailbox.
2122 * None of these require any special flags or opcode modifiers.
2123 */
2124 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2125 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2126 if (status != TAVOR_CMD_SUCCESS) {
2127 return (status);
2128 }
2129 in_mapaddr = mbox_info.mbi_in->mb_mapaddr;
2130 out_mapaddr = 0;
2131 flags = 0;
2132 opmod = 0;
2133
2134 /* Copy the Tavor command into the "In" mailbox */
2135 size = sizeof (tavor_hw_qpc_t);
2136 for (i = 0; i < (size >> 3); i++) {
2137 data = ((uint64_t *)qp)[i];
2138 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2139 ((uint64_t *)mbox_info.mbi_in->mb_addr + i + 1),
2140 data);
2141 }
2142 ddi_put32(mbox_info.mbi_in->mb_acchdl,
2143 ((uint32_t *)mbox_info.mbi_in->mb_addr), opmask);
2144
2145 /*
2146 * Sync the mailbox for the device to read. We have to add
2147 * eight bytes here to account for "opt_param_mask" and
2148 * proper alignment.
2149 */
2150 tavor_mbox_sync(mbox_info.mbi_in, 0, size + 8,
2151 DDI_DMA_SYNC_FORDEV);
2152 }
2153
2154 /* Setup and post Tavor QP state transition command */
2155 cmd.cp_inparm = in_mapaddr;
2156 cmd.cp_outparm = out_mapaddr;
2157 cmd.cp_inmod = qpindx | flags;
2158 cmd.cp_opcode = opcode;
2159 cmd.cp_opmod = opmod;
2160 cmd.cp_flags = sleepflag;
2161 status = tavor_cmd_post(state, &cmd);
2162
2163 /*
2164 * If we allocated a mailbox (either an "In" or an "Out") above,
2165 * then free it now before returning.
2166 */
2167 if ((opcode != RTS2SQD_QP) && (opcode != TOERR_QP) &&
2168 (opcode != TORST_QP)) {
2169 /* Free the mailbox */
2170 tavor_mbox_free(state, &mbox_info);
2171 }
2172
2173 return (status);
2174 }
2175
2176
2177 /*
2178 * tavor_cmn_query_cmd_post()
2179 * Context: Can be called from interrupt or base context.
2180 *
2181 * This is the common function for posting all the various types of
2182 * Tavor query commands. All Tavor query commands require an "Out"
2183 * mailbox to be allocated for the resulting queried data.
2184 *
2185 * Note: This common function should be used only with the following
2186 * opcodes: QUERY_DEV_LIM, QUERY_FW, QUERY_DDR, QUERY_ADAPTER,
2187 * QUERY_HCA, QUERY_MPT, QUERY_EQ, QUERY_CQ, and QUERY_QP.
2188 */
2189 int
tavor_cmn_query_cmd_post(tavor_state_t * state,uint_t opcode,uint_t queryindx,void * query,uint_t size,uint_t sleepflag)2190 tavor_cmn_query_cmd_post(tavor_state_t *state, uint_t opcode,
2191 uint_t queryindx, void *query, uint_t size, uint_t sleepflag)
2192 {
2193 tavor_mbox_info_t mbox_info;
2194 tavor_cmd_post_t cmd;
2195 uint64_t data;
2196 uint_t offset;
2197 int status, i;
2198
2199 /* Get an "Out" mailbox for the command */
2200 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
2201 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2202 if (status != TAVOR_CMD_SUCCESS) {
2203 return (status);
2204 }
2205
2206 /* Setup and post the Tavor query command */
2207 cmd.cp_inparm = 0;
2208 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
2209 cmd.cp_inmod = queryindx;
2210 cmd.cp_opcode = opcode;
2211 cmd.cp_opmod = 0;
2212 cmd.cp_flags = sleepflag;
2213 status = tavor_cmd_post(state, &cmd);
2214 if (status != TAVOR_CMD_SUCCESS) {
2215 goto cmn_query_fail;
2216 }
2217
2218 /* Sync the mailbox to read the results */
2219 tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
2220
2221 /*
2222 * QUERY_QP is handled somewhat differently than the other query
2223 * commands. For QUERY_QP, the actual queried data is offset into
2224 * the mailbox (by one 64-bit word).
2225 */
2226 offset = (opcode == QUERY_QP) ? 1 : 0;
2227
2228 /* Copy query command results into "query" */
2229 for (i = 0; i < (size >> 3); i++) {
2230 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2231 ((uint64_t *)mbox_info.mbi_out->mb_addr + i + offset));
2232 ((uint64_t *)query)[i] = data;
2233 }
2234
2235 cmn_query_fail:
2236 /* Free the mailbox */
2237 tavor_mbox_free(state, &mbox_info);
2238
2239 return (status);
2240 }
2241
2242
2243 /*
2244 * tavor_cmn_ownership_cmd_post()
2245 * Context: Can be called from interrupt or base context.
2246 *
2247 * This is the common function for posting all the various types of
2248 * Tavor HW/SW resource ownership commands. Since some of the commands
2249 * differ from the others in the direction of ownership change (i.e.
2250 * from HW ownership to SW, or vice versa), they differ in the type of
2251 * mailbox and specific handling that each requires. This routine does
2252 * certain checks based on opcode type to determine the direction of
2253 * the transition and to correctly handle the request.
2254 *
2255 * Note: This common function should be used only with the following
2256 * opcodes: HW2SW_MPT, HW2SW_EQ, HW2SW_CQ, SW2HW_MPT, SW2HW_EQ, and
2257 * SW2HW_CQ
2258 */
2259 int
tavor_cmn_ownership_cmd_post(tavor_state_t * state,uint_t opcode,void * hwrsrc,uint_t size,uint_t hwrsrcindx,uint_t sleepflag)2260 tavor_cmn_ownership_cmd_post(tavor_state_t *state, uint_t opcode,
2261 void *hwrsrc, uint_t size, uint_t hwrsrcindx, uint_t sleepflag)
2262 {
2263 tavor_mbox_info_t mbox_info;
2264 tavor_cmd_post_t cmd;
2265 uint64_t data, in_mapaddr, out_mapaddr;
2266 uint_t direction, opmod;
2267 int status, i;
2268
2269 /*
2270 * Determine the direction of the ownership transfer based on the
2271 * provided opcode
2272 */
2273 if ((opcode == HW2SW_MPT) || (opcode == HW2SW_EQ) ||
2274 (opcode == HW2SW_CQ) || (opcode == HW2SW_SRQ)) {
2275 direction = TAVOR_CMD_RSRC_HW2SW;
2276
2277 } else if ((opcode == SW2HW_MPT) || (opcode == SW2HW_EQ) ||
2278 (opcode == SW2HW_CQ) || (opcode == SW2HW_SRQ)) {
2279 direction = TAVOR_CMD_RSRC_SW2HW;
2280
2281 } else {
2282 return (TAVOR_CMD_INVALID_STATUS);
2283 }
2284
2285 /*
2286 * If hwrsrc is NULL then we do not allocate a mailbox. This is used
2287 * in the case of memory deregister where the out mailbox is not
2288 * needed. In the case of re-register, we do use the hwrsrc.
2289 *
2290 * Otherwise, If ownership transfer is going from hardware to software,
2291 * then allocate an "Out" mailbox. This will be filled in later as a
2292 * result of the Tavor command.
2293 *
2294 * And if the ownership transfer is going from software to hardware,
2295 * then we need an "In" mailbox, and we need to fill it in and sync it
2296 * (if necessary). Then the mailbox can be passed to the Tavor
2297 * firmware.
2298 *
2299 * For the HW2SW (dereg) case, we only use an out mbox if hwrsrc is !=
2300 * NULL. This implies a re-reg, and the out mbox must be used. If
2301 * hwrsrc is == NULL, then we can save some time and resources by not
2302 * using an out mbox at all. We must set opmod to TAVOR_CMD_DO_OUTMBOX
2303 * and TAVOR_CMD_NO_OUTMBOX appropriately in this case.
2304 *
2305 * For the SW2HW (reg) case, no out mbox is possible. We set opmod to
2306 * 0 anyway, but this field is not used in this case.
2307 */
2308 if (direction == TAVOR_CMD_RSRC_HW2SW) {
2309 if (hwrsrc != NULL) {
2310 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
2311 status = tavor_mbox_alloc(state, &mbox_info,
2312 sleepflag);
2313 if (status != TAVOR_CMD_SUCCESS) {
2314 return (status);
2315 }
2316 in_mapaddr = 0;
2317 out_mapaddr = mbox_info.mbi_out->mb_mapaddr;
2318 opmod = TAVOR_CMD_DO_OUTMBOX;
2319 } else {
2320 in_mapaddr = 0;
2321 out_mapaddr = 0;
2322 opmod = TAVOR_CMD_NO_OUTMBOX;
2323 }
2324 } else { /* TAVOR_CMD_RSRC_SW2HW */
2325 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2326 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2327 if (status != TAVOR_CMD_SUCCESS) {
2328 return (status);
2329 }
2330
2331 /* Copy the SW2HW ownership command into mailbox */
2332 for (i = 0; i < (size >> 3); i++) {
2333 data = ((uint64_t *)hwrsrc)[i];
2334 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2335 ((uint64_t *)mbox_info.mbi_in->mb_addr + i),
2336 data);
2337 }
2338
2339 /* Sync the mailbox for the device to read */
2340 tavor_mbox_sync(mbox_info.mbi_in, 0, size,
2341 DDI_DMA_SYNC_FORDEV);
2342
2343 in_mapaddr = mbox_info.mbi_in->mb_mapaddr;
2344 out_mapaddr = 0;
2345 opmod = 0;
2346 }
2347
2348
2349 /* Setup and post the Tavor ownership command */
2350 cmd.cp_inparm = in_mapaddr;
2351 cmd.cp_outparm = out_mapaddr;
2352 cmd.cp_inmod = hwrsrcindx;
2353 cmd.cp_opcode = opcode;
2354 cmd.cp_opmod = opmod;
2355 cmd.cp_flags = sleepflag;
2356 status = tavor_cmd_post(state, &cmd);
2357 if (status != TAVOR_CMD_SUCCESS) {
2358 goto cmn_ownership_fail;
2359 }
2360
2361 /*
2362 * As mentioned above, for HW2SW ownership transfers we need to
2363 * sync (if necessary) and copy out the resulting data from the
2364 * "Out" mailbox" (assuming the above command was successful).
2365 */
2366 if (direction == TAVOR_CMD_RSRC_HW2SW && hwrsrc != NULL) {
2367 /* Sync the mailbox to read the results */
2368 tavor_mbox_sync(mbox_info.mbi_out, 0, size,
2369 DDI_DMA_SYNC_FORCPU);
2370
2371 /* Copy HW2SW ownership command results into "hwrsrc" */
2372 for (i = 0; i < (size >> 3); i++) {
2373 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2374 ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
2375 ((uint64_t *)hwrsrc)[i] = data;
2376 }
2377 }
2378
2379 cmn_ownership_fail:
2380 if (hwrsrc != NULL) {
2381 /* Free the mailbox */
2382 tavor_mbox_free(state, &mbox_info);
2383 }
2384
2385 return (status);
2386 }
2387
2388
2389 /*
2390 * tavor_conf_special_qp_cmd_post()
2391 * Context: Can be called from interrupt or base context.
2392 */
2393 int
tavor_conf_special_qp_cmd_post(tavor_state_t * state,uint_t qpindx,uint_t qptype,uint_t sleepflag)2394 tavor_conf_special_qp_cmd_post(tavor_state_t *state, uint_t qpindx,
2395 uint_t qptype, uint_t sleepflag)
2396 {
2397 tavor_cmd_post_t cmd;
2398 int status;
2399
2400 /* Setup and post Tavor "CONF_SPECIAL_QP" command */
2401 cmd.cp_inparm = 0;
2402 cmd.cp_outparm = 0;
2403 cmd.cp_inmod = qpindx;
2404 cmd.cp_opcode = CONF_SPECIAL_QP;
2405 cmd.cp_opmod = qptype;
2406 cmd.cp_flags = sleepflag;
2407 status = tavor_cmd_post(state, &cmd);
2408
2409 return (status);
2410 }
2411
2412
2413 /*
2414 * tavor_mgid_hash_cmd_post()
2415 * Context: Can be called from interrupt or base context.
2416 */
2417 int
tavor_mgid_hash_cmd_post(tavor_state_t * state,uint64_t mgid_h,uint64_t mgid_l,uint64_t * mgid_hash,uint_t sleepflag)2418 tavor_mgid_hash_cmd_post(tavor_state_t *state, uint64_t mgid_h,
2419 uint64_t mgid_l, uint64_t *mgid_hash, uint_t sleepflag)
2420 {
2421 tavor_mbox_info_t mbox_info;
2422 tavor_cmd_post_t cmd;
2423 int status;
2424
2425 /* Get an "In" mailbox for the command */
2426 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2427 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2428 if (status != TAVOR_CMD_SUCCESS) {
2429 return (status);
2430 }
2431
2432 /* Copy the Tavor "MGID_HASH" command into mailbox */
2433 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2434 ((uint64_t *)mbox_info.mbi_in->mb_addr + 0), mgid_h);
2435 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2436 ((uint64_t *)mbox_info.mbi_in->mb_addr + 1), mgid_l);
2437
2438 /* Sync the mailbox for the device to read */
2439 tavor_mbox_sync(mbox_info.mbi_in, 0, TAVOR_CMD_MGIDHASH_SZ,
2440 DDI_DMA_SYNC_FORDEV);
2441
2442 /* Setup and post the Tavor "MGID_HASH" command */
2443 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
2444 cmd.cp_outparm = 0;
2445 cmd.cp_inmod = 0;
2446 cmd.cp_opcode = MGID_HASH;
2447 cmd.cp_opmod = 0;
2448 cmd.cp_flags = sleepflag;
2449 status = tavor_cmd_post(state, &cmd);
2450
2451 /* MGID hash value is returned in command "outparam" */
2452 *mgid_hash = cmd.cp_outparm;
2453
2454 /* Free the mailbox */
2455 tavor_mbox_free(state, &mbox_info);
2456
2457 return (status);
2458 }
2459
2460
2461 /*
2462 * tavor_read_mgm_cmd_post()
2463 * Context: Can be called from interrupt or base context.
2464 *
2465 * Note: It is assumed that the "mcg" parameter is actually a pointer to a
2466 * "tavor_hw_mcg_t" struct and some number of "tavor_hw_mcg_qp_list_t"
2467 * structs. Combined size should be equal to result of TAVOR_MCGMEM_SZ()
2468 * macro.
2469 */
2470 int
tavor_read_mgm_cmd_post(tavor_state_t * state,tavor_hw_mcg_t * mcg,uint_t mcgindx,uint_t sleepflag)2471 tavor_read_mgm_cmd_post(tavor_state_t *state, tavor_hw_mcg_t *mcg,
2472 uint_t mcgindx, uint_t sleepflag)
2473 {
2474 tavor_mbox_info_t mbox_info;
2475 tavor_cmd_post_t cmd;
2476 uint64_t data;
2477 uint_t size, hdrsz, qplistsz;
2478 int status, i;
2479
2480 /* Get an "Out" mailbox for the results */
2481 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
2482 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2483 if (status != TAVOR_CMD_SUCCESS) {
2484 return (status);
2485 }
2486
2487 /* Setup and post Tavor "READ_MGM" command */
2488 cmd.cp_inparm = 0;
2489 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
2490 cmd.cp_inmod = mcgindx;
2491 cmd.cp_opcode = READ_MGM;
2492 cmd.cp_opmod = 0;
2493 cmd.cp_flags = sleepflag;
2494 status = tavor_cmd_post(state, &cmd);
2495 if (status != TAVOR_CMD_SUCCESS) {
2496 goto read_mgm_fail;
2497 }
2498
2499 /* Sync the mailbox to read the results */
2500 size = TAVOR_MCGMEM_SZ(state);
2501 tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
2502
2503 /* Copy the READ_MGM command results into "mcg" */
2504 hdrsz = sizeof (tavor_hw_mcg_t);
2505 for (i = 0; i < (hdrsz >> 3); i++) {
2506 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2507 ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
2508 ((uint64_t *)mcg)[i] = data;
2509 }
2510 qplistsz = size - hdrsz;
2511 for (i = 0; i < (qplistsz >> 2); i++) {
2512 data = ddi_get32(mbox_info.mbi_out->mb_acchdl,
2513 ((uint32_t *)mbox_info.mbi_out->mb_addr + i + 8));
2514 ((uint32_t *)mcg)[i + 8] = data;
2515 }
2516
2517 read_mgm_fail:
2518 /* Free the mailbox */
2519 tavor_mbox_free(state, &mbox_info);
2520
2521 return (status);
2522 }
2523
2524
2525 /*
2526 * tavor_write_mgm_cmd_post()
2527 * Context: Can be called from interrupt or base context.
2528 *
2529 * Note: It is assumed that the "mcg" parameter is actually a pointer to a
2530 * "tavor_hw_mcg_t" struct and some number of "tavor_hw_mcg_qp_list_t"
2531 * structs. Combined size should be equal to result of TAVOR_MCGMEM_SZ()
2532 * macro.
2533 */
2534 int
tavor_write_mgm_cmd_post(tavor_state_t * state,tavor_hw_mcg_t * mcg,uint_t mcgindx,uint_t sleepflag)2535 tavor_write_mgm_cmd_post(tavor_state_t *state, tavor_hw_mcg_t *mcg,
2536 uint_t mcgindx, uint_t sleepflag)
2537 {
2538 tavor_mbox_info_t mbox_info;
2539 tavor_cmd_post_t cmd;
2540 uint64_t data;
2541 uint_t size, hdrsz, qplistsz;
2542 int status, i;
2543
2544 /* Get an "In" mailbox for the command */
2545 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2546 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2547 if (status != TAVOR_CMD_SUCCESS) {
2548 return (status);
2549 }
2550
2551 /* Copy the Tavor "WRITE_MGM" command into mailbox */
2552 size = TAVOR_MCGMEM_SZ(state);
2553 hdrsz = sizeof (tavor_hw_mcg_t);
2554 for (i = 0; i < (hdrsz >> 3); i++) {
2555 data = ((uint64_t *)mcg)[i];
2556 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2557 ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
2558 }
2559 qplistsz = size - hdrsz;
2560 for (i = 0; i < (qplistsz >> 2); i++) {
2561 data = ((uint32_t *)mcg)[i + 8];
2562 ddi_put32(mbox_info.mbi_in->mb_acchdl,
2563 ((uint32_t *)mbox_info.mbi_in->mb_addr + i + 8), data);
2564 }
2565
2566 /* Sync the mailbox for the device to read */
2567 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2568
2569 /* Setup and post Tavor "WRITE_MGM" command */
2570 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
2571 cmd.cp_outparm = 0;
2572 cmd.cp_inmod = mcgindx;
2573 cmd.cp_opcode = WRITE_MGM;
2574 cmd.cp_opmod = 0;
2575 cmd.cp_flags = sleepflag;
2576 status = tavor_cmd_post(state, &cmd);
2577
2578 /* Free the mailbox */
2579 tavor_mbox_free(state, &mbox_info);
2580
2581 return (status);
2582
2583 }
2584
2585
2586 /*
2587 * tavor_modify_mpt_cmd_post()
2588 * Context: Can be called from interrupt or base context.
2589 */
2590 int
tavor_modify_mpt_cmd_post(tavor_state_t * state,tavor_hw_mpt_t * mpt,uint_t mptindx,uint_t flags,uint_t sleepflag)2591 tavor_modify_mpt_cmd_post(tavor_state_t *state, tavor_hw_mpt_t *mpt,
2592 uint_t mptindx, uint_t flags, uint_t sleepflag)
2593 {
2594 tavor_mbox_info_t mbox_info;
2595 tavor_cmd_post_t cmd;
2596 uint64_t data;
2597 uint_t size;
2598 int status, i;
2599
2600 /* Get an "In" mailbox for the command */
2601 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
2602 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2603 if (status != TAVOR_CMD_SUCCESS) {
2604 return (status);
2605 }
2606
2607 /* Copy the Tavor "MODIFY_MPT" command into mailbox */
2608 size = sizeof (tavor_hw_mpt_t);
2609 for (i = 0; i < (size >> 3); i++) {
2610 data = ((uint64_t *)mpt)[i];
2611 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2612 ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
2613 }
2614
2615 /* Sync the mailbox for the device to read */
2616 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2617
2618 /* Setup and post Tavor "MODIFY_MPT" command */
2619 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
2620 cmd.cp_outparm = 0;
2621 cmd.cp_inmod = mptindx;
2622 cmd.cp_opcode = MODIFY_MPT;
2623 cmd.cp_opmod = flags;
2624 cmd.cp_flags = sleepflag;
2625 status = tavor_cmd_post(state, &cmd);
2626
2627 /* Free the mailbox */
2628 tavor_mbox_free(state, &mbox_info);
2629
2630 return (status);
2631 }
2632
2633 /*
2634 * tavor_getpefcntr_cmd_post()
2635 * Context: Can be called from interrupt or base context.
2636 *
2637 * If reset is zero, read the performance counters of the specified port and
2638 * copy them into perfinfo.
2639 * If reset is non-zero reset the performance counters of the specified port.
2640 */
2641 int
tavor_getperfcntr_cmd_post(tavor_state_t * state,uint_t port,uint_t sleepflag,tavor_hw_sm_perfcntr_t * perfinfo,int reset)2642 tavor_getperfcntr_cmd_post(tavor_state_t *state, uint_t port,
2643 uint_t sleepflag, tavor_hw_sm_perfcntr_t *perfinfo, int reset)
2644 {
2645 tavor_mbox_info_t mbox_info;
2646 tavor_cmd_post_t cmd;
2647 uint64_t data;
2648 uint32_t *mbox;
2649 uint_t size;
2650 int status, i;
2651
2652 bzero((void *)&cmd, sizeof (tavor_cmd_post_t));
2653
2654 /* Get "In" and "Out" mailboxes for the command */
2655 mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
2656 status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
2657 if (status != TAVOR_CMD_SUCCESS) {
2658 return (status);
2659 }
2660
2661 /* Build request MAD in the "In" mailbox */
2662 size = TAVOR_CMD_MAD_IFC_SIZE;
2663 mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2664
2665 if (reset) {
2666 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
2667 TAVOR_CMD_PERF_SET);
2668 } else {
2669 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
2670 TAVOR_CMD_PERF_GET);
2671 }
2672 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
2673 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
2674 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
2675 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PERFCNTRS);
2676 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], TAVOR_CMD_PERFATTR);
2677
2678 if (reset) {
2679 /* reset counters for XmitData, RcvData, XmitPkts, RcvPkts */
2680 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16],
2681 ((port << 16) | 0xf000));
2682
2683 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[22], 0);
2684 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[23], 0);
2685 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[24], 0);
2686 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[25], 0);
2687 } else
2688 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16], (port << 16));
2689
2690 /* Sync the mailbox for the device to read */
2691 tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2692
2693 /* Setup the Hermon "MAD_IFC" command */
2694 cmd.cp_inparm = mbox_info.mbi_in->mb_mapaddr;
2695 cmd.cp_outparm = mbox_info.mbi_out->mb_mapaddr;
2696 cmd.cp_inmod = port;
2697 cmd.cp_opcode = MAD_IFC;
2698 /* No MKey and BKey checking */
2699 cmd.cp_opmod = TAVOR_CMD_MKEY_DONTCHECK | TAVOR_CMD_BKEY_DONTCHECK;
2700 cmd.cp_flags = TAVOR_CMD_NOSLEEP_SPIN; /* NO SLEEP */
2701 status = tavor_cmd_post(state, &cmd);
2702 if (status != TAVOR_CMD_SUCCESS) {
2703 goto getperfinfo_fail;
2704 }
2705
2706 /* Sync the mailbox to read the results */
2707 size = TAVOR_CMD_MAD_IFC_SIZE;
2708 tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
2709
2710 if (reset == 0) {
2711 size = sizeof (tavor_hw_sm_perfcntr_t); /* for the copy */
2712 /*
2713 * Copy Perfcounters into "perfinfo". We can discard the MAD
2714 * header and the 8 Quadword reserved area of the PERM mgmt
2715 * class MAD
2716 */
2717
2718 for (i = 0; i < size >> 3; i++) {
2719 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2720 ((uint64_t *)mbox_info.mbi_out->mb_addr + i + 8));
2721 ((uint64_t *)(void *)perfinfo)[i] = data;
2722 }
2723 }
2724
2725 getperfinfo_fail:
2726 /* Free the mailbox */
2727 tavor_mbox_free(state, &mbox_info);
2728 return (status);
2729 }
2730