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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * hermon_cmd.c
  28  *    Hermon Firmware Command Routines
  29  *
  30  *    Implements all the routines necessary for allocating, posting, and
  31  *    freeing commands for the Hermon firmware.  These routines manage a
  32  *    preallocated list of command mailboxes and provide interfaces to post
  33  *    each of the several dozen commands to the Hermon firmware.
  34  */
  35 
  36 #include <sys/types.h>
  37 #include <sys/conf.h>
  38 #include <sys/ddi.h>
  39 #include <sys/sunddi.h>
  40 #include <sys/modctl.h>
  41 #include <sys/bitmap.h>
  42 
  43 #include <sys/ib/adapters/hermon/hermon.h>
  44 
  45 static int hermon_impl_mbox_alloc(hermon_state_t *state,
  46     hermon_mboxlist_t *mblist, hermon_mbox_t **mb, uint_t mbox_wait);
  47 static void hermon_impl_mbox_free(hermon_mboxlist_t *mblist,
  48     hermon_mbox_t **mb);
  49 static int hermon_impl_mboxlist_init(hermon_state_t *state,
  50     hermon_mboxlist_t *mblist, uint_t num_mbox, hermon_rsrc_type_t type);
  51 static void hermon_impl_mboxlist_fini(hermon_state_t *state,
  52     hermon_mboxlist_t *mblist);
  53 static int hermon_outstanding_cmd_alloc(hermon_state_t *state,
  54     hermon_cmd_t **cmd_ptr, uint_t cmd_wait);
  55 static void hermon_outstanding_cmd_free(hermon_state_t *state,
  56     hermon_cmd_t **cmd_ptr);
  57 static int hermon_write_hcr(hermon_state_t *state, hermon_cmd_post_t *cmdpost,
  58     uint16_t token, int *hwerr);
  59 static void hermon_mbox_sync(hermon_mbox_t *mbox, uint_t offset,
  60     uint_t length, uint_t flag);
  61 static void hermon_cmd_check_status(hermon_state_t *state, int status);
  62 
  63 /*
  64  * hermon_cmd_post()
  65  *    Context: Can be called from interrupt or base context.
  66  *
  67  *    The "cp_flags" field in cmdpost
  68  *    is used to determine whether to wait for an available
  69  *    outstanding command (if necessary) or to return error.
  70  */
  71 int
  72 hermon_cmd_post(hermon_state_t *state, hermon_cmd_post_t *cmdpost)
  73 {
  74         hermon_cmd_t    *cmdptr;
  75         int             status, retry_cnt, retry_cnt2, hw_err;
  76         uint16_t        token;
  77 
  78         /* Determine if we are going to spin until completion */
  79         if (cmdpost->cp_flags == HERMON_CMD_NOSLEEP_SPIN) {
  80 
  81                 /* Write the command to the HCR */
  82                 retry_cnt = HCA_PIO_RETRY_CNT;
  83                 do {
  84                         status = hermon_write_hcr(state, cmdpost, 0, &hw_err);
  85                 } while (status == HERMON_CMD_INTERNAL_ERR && retry_cnt-- > 0);
  86 
  87                 /* Check if there is an error status in hermon_write_hcr() */
  88                 if (status != HERMON_CMD_SUCCESS) {
  89                         /*
  90                          * If there is a HW error, call hermon_cmd_retry_ok()
  91                          * to check the side-effect of the operation retry.
  92                          */
  93                         if ((retry_cnt == HCA_PIO_RETRY_CNT &&
  94                             hw_err == HCA_PIO_OK) ||
  95                             !hermon_cmd_retry_ok(cmdpost, status)) {
  96                                 hermon_cmd_check_status(state, status);
  97                                 return (status);
  98                         }
  99                 /* Check if there is a transient internal error */
 100                 } else if (retry_cnt != HCA_PIO_RETRY_CNT) {
 101                         hermon_fm_ereport(state, HCA_IBA_ERR,
 102                             HCA_ERR_TRANSIENT);
 103                 }
 104 
 105         } else {  /* "HERMON_CMD_SLEEP_NOSPIN" */
 106                 ASSERT(HERMON_SLEEPFLAG_FOR_CONTEXT() != HERMON_NOSLEEP);
 107 
 108                 /* NOTE: Expect threads to be waiting in here */
 109                 status = hermon_outstanding_cmd_alloc(state, &cmdptr,
 110                     cmdpost->cp_flags);
 111                 if (status != HERMON_CMD_SUCCESS) {
 112                         return (status);
 113                 }
 114 
 115                 retry_cnt = HCA_PIO_RETRY_CNT;
 116 retry:
 117                 /*
 118                  * Set status to "HERMON_CMD_INVALID_STATUS".  It is
 119                  * appropriate to do this here without the "cmd_comp_lock"
 120                  * because this register is overloaded.  Later it will be
 121                  * used to indicate - through a change from this invalid
 122                  * value to some other value - that the condition variable
 123                  * has been signaled.  Once it has, status will then contain
 124                  * the _real_ completion status
 125                  */
 126                 cmdptr->cmd_status = HERMON_CMD_INVALID_STATUS;
 127 
 128                 /* Write the command to the HCR */
 129                 token = (uint16_t)cmdptr->cmd_indx;
 130                 retry_cnt2 = HCA_PIO_RETRY_CNT;
 131                 do {
 132                         status = hermon_write_hcr(state, cmdpost, token,
 133                             &hw_err);
 134                 } while (status == HERMON_CMD_INTERNAL_ERR && retry_cnt2-- > 0);
 135 
 136                 /* Check if there is an error status in hermon_write_hcr() */
 137                 if (status != HERMON_CMD_SUCCESS) {
 138                         /*
 139                          * If there is a HW error, call hermon_cmd_retry_ok()
 140                          * to check the side-effect of the operation retry.
 141                          */
 142                         if ((retry_cnt == HCA_PIO_RETRY_CNT &&
 143                             hw_err == HCA_PIO_OK) ||
 144                             !hermon_cmd_retry_ok(cmdpost, status)) {
 145                                 hermon_cmd_check_status(state, status);
 146                                 hermon_outstanding_cmd_free(state, &cmdptr);
 147                                 return (status);
 148                         }
 149                 /* Check if there is a transient internal error */
 150                 } else if (retry_cnt2 != HCA_PIO_RETRY_CNT) {
 151                         hermon_fm_ereport(state, HCA_IBA_ERR,
 152                             HCA_ERR_TRANSIENT);
 153                 }
 154 
 155                 /*
 156                  * cv_wait() on the "command_complete" condition variable.
 157                  */
 158                 mutex_enter(&cmdptr->cmd_comp_lock);
 159                 while (cmdptr->cmd_status == HERMON_CMD_INVALID_STATUS) {
 160                         /* NOTE: EXPECT SEVERAL THREADS TO BE WAITING HERE */
 161                         cv_wait(&cmdptr->cmd_comp_cv, &cmdptr->cmd_comp_lock);
 162                 }
 163                 mutex_exit(&cmdptr->cmd_comp_lock);
 164 
 165                 /*
 166                  * Wake up after command completes (cv_signal).  Read status
 167                  * from the command (success, fail, etc.).  It is appropriate
 168                  * here (as above) to read the status field without the
 169                  * "cmd_comp_lock" because it is no longer being used to
 170                  * indicate whether the condition variable has been signaled
 171                  * (i.e. at this point we are certain that it already has).
 172                  */
 173                 status = cmdptr->cmd_status;
 174 
 175                 /* retry the operation if an internal error occurs */
 176                 if (status == HERMON_CMD_INTERNAL_ERR && retry_cnt-- > 0)
 177                         goto retry;
 178 
 179                 /* Save the "outparam" values into the cmdpost struct */
 180                 cmdpost->cp_outparm = cmdptr->cmd_outparm;
 181 
 182                 /*
 183                  * Add the command back to the "outstanding commands list".
 184                  * Signal the "cmd_list" condition variable, if necessary.
 185                  */
 186                 hermon_outstanding_cmd_free(state, &cmdptr);
 187 
 188                 /* Check if there is an error status in hermon_write_hcr() */
 189                 if (status != HERMON_CMD_SUCCESS) {
 190                         /*
 191                          * If there is a HW error, call hermon_cmd_retry_ok()
 192                          * to check the side-effect of the operation retry.
 193                          */
 194                         if ((retry_cnt == HCA_PIO_RETRY_CNT &&
 195                             hw_err == HCA_PIO_OK) ||
 196                             !hermon_cmd_retry_ok(cmdpost, status)) {
 197                                 hermon_cmd_check_status(state, status);
 198                                 cmn_err(CE_NOTE, "hermon%d: post cmd failed "
 199                                     "opcode (0x%x) status (0x%x)\n",
 200                                     state->hs_instance, cmdpost->cp_opcode,
 201                                     status);
 202                                 return (status);
 203                         }
 204                 /* Check if there is a transient internal error */
 205                 } else if (retry_cnt != HCA_PIO_RETRY_CNT) {
 206                         hermon_fm_ereport(state, HCA_IBA_ERR,
 207                             HCA_ERR_TRANSIENT);
 208                 }
 209         }
 210 
 211         return (HERMON_CMD_SUCCESS);
 212 }
 213 
 214 /*
 215  * hermon_cmd_check_status()
 216  *      Context:  Can be called from interrupt or base
 217  *
 218  * checks the status returned from write_hcr and does the right
 219  * notice to the console, if any
 220  */
 221 static void
 222 hermon_cmd_check_status(hermon_state_t *state, int status)
 223 {
 224         switch (status) {
 225                 case HERMON_CMD_TIMEOUT_TOGGLE:
 226                         HERMON_FMANOTE(state, HERMON_FMA_TOTOG);
 227                         hermon_fm_ereport(state, HCA_IBA_ERR,
 228                             HCA_ERR_NON_FATAL);
 229                         break;
 230 
 231                 case HERMON_CMD_TIMEOUT_GOBIT:
 232                         HERMON_FMANOTE(state, HERMON_FMA_GOBIT);
 233                         hermon_fm_ereport(state, HCA_IBA_ERR,
 234                             HCA_ERR_NON_FATAL);
 235                         break;
 236 
 237                 case HERMON_CMD_INSUFF_RSRC:
 238                         HERMON_FMANOTE(state, HERMON_FMA_RSRC);
 239                         break;
 240 
 241                 case HERMON_CMD_INVALID_STATUS:
 242                         HERMON_FMANOTE(state, HERMON_FMA_CMDINV);
 243                         hermon_fm_ereport(state, HCA_IBA_ERR,
 244                             HCA_ERR_NON_FATAL);
 245                         break;
 246 
 247                 case HERMON_CMD_INTERNAL_ERR:
 248                         HERMON_FMANOTE(state, HERMON_FMA_HCRINT);
 249                         hermon_fm_ereport(state, HCA_IBA_ERR,
 250                             HCA_ERR_NON_FATAL);
 251                         break;
 252 
 253                 case HERMON_CMD_BAD_NVMEM:
 254                         /*
 255                          * No need of an ereport here since this case
 256                          * is treated as a degradation later.
 257                          */
 258                         HERMON_FMANOTE(state, HERMON_FMA_NVMEM);
 259                         break;
 260 
 261                 default:
 262                         break;
 263         }
 264 }
 265 
 266 /*
 267  * hermon_mbox_alloc()
 268  *    Context: Can be called from interrupt or base context.
 269  *
 270  *    The "mbox_wait" parameter is used to determine whether to
 271  *    wait for a mailbox to become available or not.
 272  */
 273 int
 274 hermon_mbox_alloc(hermon_state_t *state, hermon_mbox_info_t *mbox_info,
 275     uint_t mbox_wait)
 276 {
 277         int             status;
 278         uint_t          sleep_context;
 279 
 280         sleep_context = HERMON_SLEEPFLAG_FOR_CONTEXT();
 281 
 282         /* Allocate an "In" mailbox */
 283         if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_INMBOX) {
 284                 /* Determine correct mboxlist based on calling context */
 285                 if (sleep_context == HERMON_NOSLEEP) {
 286                         status = hermon_impl_mbox_alloc(state,
 287                             &state->hs_in_intr_mblist,
 288                             &mbox_info->mbi_in, mbox_wait);
 289 
 290                         ASSERT(status == HERMON_CMD_SUCCESS);
 291                 } else {
 292                         /* NOTE: Expect threads to be waiting in here */
 293                         status = hermon_impl_mbox_alloc(state,
 294                             &state->hs_in_mblist, &mbox_info->mbi_in,
 295                             mbox_wait);
 296                         if (status != HERMON_CMD_SUCCESS) {
 297                                 return (status);
 298                         }
 299                 }
 300 
 301         }
 302 
 303         /* Allocate an "Out" mailbox */
 304         if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_OUTMBOX) {
 305                 /* Determine correct mboxlist based on calling context */
 306                 if (sleep_context == HERMON_NOSLEEP) {
 307                         status = hermon_impl_mbox_alloc(state,
 308                             &state->hs_out_intr_mblist,
 309                             &mbox_info->mbi_out, mbox_wait);
 310 
 311                         ASSERT(status == HERMON_CMD_SUCCESS);
 312                 } else {
 313                         /* NOTE: Expect threads to be waiting in here */
 314                         status = hermon_impl_mbox_alloc(state,
 315                             &state->hs_out_mblist, &mbox_info->mbi_out,
 316                             mbox_wait);
 317                         if (status != HERMON_CMD_SUCCESS) {
 318                                 /* If we allocated an "In" mailbox, free it */
 319                                 if (mbox_info->mbi_alloc_flags &
 320                                     HERMON_ALLOC_INMBOX) {
 321                                         hermon_impl_mbox_free(
 322                                             &state->hs_in_mblist,
 323                                             &mbox_info->mbi_in);
 324                                 }
 325                                 return (status);
 326                         }
 327                 }
 328         }
 329 
 330         /* Store appropriate context in mbox_info */
 331         mbox_info->mbi_sleep_context = sleep_context;
 332 
 333         return (HERMON_CMD_SUCCESS);
 334 }
 335 
 336 
 337 /*
 338  * hermon_mbox_free()
 339  *    Context: Can be called from interrupt or base context.
 340  */
 341 void
 342 hermon_mbox_free(hermon_state_t *state, hermon_mbox_info_t *mbox_info)
 343 {
 344         /*
 345          * The mailbox has to be freed in the same context from which it was
 346          * allocated.  The context is stored in the mbox_info at
 347          * hermon_mbox_alloc() time.  We check the stored context against the
 348          * current context here.
 349          */
 350         ASSERT(mbox_info->mbi_sleep_context == HERMON_SLEEPFLAG_FOR_CONTEXT());
 351 
 352         /* Determine correct mboxlist based on calling context */
 353         if (mbox_info->mbi_sleep_context == HERMON_NOSLEEP) {
 354                 /* Free the intr "In" mailbox */
 355                 if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_INMBOX) {
 356                         hermon_impl_mbox_free(&state->hs_in_intr_mblist,
 357                             &mbox_info->mbi_in);
 358                 }
 359 
 360                 /* Free the intr "Out" mailbox */
 361                 if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_OUTMBOX) {
 362                         hermon_impl_mbox_free(&state->hs_out_intr_mblist,
 363                             &mbox_info->mbi_out);
 364                 }
 365         } else {
 366                 /* Free the "In" mailbox */
 367                 if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_INMBOX) {
 368                         hermon_impl_mbox_free(&state->hs_in_mblist,
 369                             &mbox_info->mbi_in);
 370                 }
 371 
 372                 /* Free the "Out" mailbox */
 373                 if (mbox_info->mbi_alloc_flags & HERMON_ALLOC_OUTMBOX) {
 374                         hermon_impl_mbox_free(&state->hs_out_mblist,
 375                             &mbox_info->mbi_out);
 376                 }
 377         }
 378 }
 379 
 380 
 381 
 382 /*
 383  * hermon_cmd_complete_handler()
 384  *    Context: Called only from interrupt context.
 385  */
 386 /* ARGSUSED */
 387 int
 388 hermon_cmd_complete_handler(hermon_state_t *state, hermon_eqhdl_t eq,
 389     hermon_hw_eqe_t *eqe)
 390 {
 391         hermon_cmd_t            *cmdp;
 392 
 393         /*
 394          * Find the outstanding command pointer based on value returned
 395          * in "token"
 396          */
 397         cmdp = &state->hs_cmd_list.cml_cmd[HERMON_EQE_CMDTOKEN_GET(eq, eqe)];
 398 
 399         /* Signal the waiting thread */
 400         mutex_enter(&cmdp->cmd_comp_lock);
 401         cmdp->cmd_outparm = ((uint64_t)HERMON_EQE_CMDOUTP0_GET(eq, eqe) << 32) |
 402             HERMON_EQE_CMDOUTP1_GET(eq, eqe);
 403         cmdp->cmd_status = HERMON_EQE_CMDSTATUS_GET(eq, eqe);
 404 
 405         cv_signal(&cmdp->cmd_comp_cv);
 406         mutex_exit(&cmdp->cmd_comp_lock);
 407 
 408         return (DDI_SUCCESS);
 409 }
 410 
 411 
 412 /*
 413  * hermon_inmbox_list_init()
 414  *    Context: Only called from attach() path context
 415  */
 416 int
 417 hermon_inmbox_list_init(hermon_state_t *state)
 418 {
 419         int             status;
 420         uint_t          num_inmbox;
 421 
 422         /* Initialize the "In" mailbox list */
 423         num_inmbox  =  (1 << state->hs_cfg_profile->cp_log_num_inmbox);
 424         status = hermon_impl_mboxlist_init(state, &state->hs_in_mblist,
 425             num_inmbox, HERMON_IN_MBOX);
 426         if (status != DDI_SUCCESS) {
 427                 return (DDI_FAILURE);
 428         }
 429 
 430         return (DDI_SUCCESS);
 431 }
 432 
 433 
 434 /*
 435  * hermon_intr_inmbox_list_init()
 436  *    Context: Only called from attach() path context
 437  */
 438 int
 439 hermon_intr_inmbox_list_init(hermon_state_t *state)
 440 {
 441         int             status;
 442         uint_t          num_inmbox;
 443 
 444         /* Initialize the interrupt "In" mailbox list */
 445         num_inmbox  =  (1 << state->hs_cfg_profile->cp_log_num_intr_inmbox);
 446         status = hermon_impl_mboxlist_init(state, &state->hs_in_intr_mblist,
 447             num_inmbox, HERMON_INTR_IN_MBOX);
 448         if (status != DDI_SUCCESS) {
 449                 return (DDI_FAILURE);
 450         }
 451 
 452         return (DDI_SUCCESS);
 453 }
 454 
 455 
 456 /*
 457  * hermon_outmbox_list_init()
 458  *    Context: Only called from attach() path context
 459  */
 460 int
 461 hermon_outmbox_list_init(hermon_state_t *state)
 462 {
 463         int             status;
 464         uint_t          num_outmbox;
 465 
 466         /* Initialize the "Out" mailbox list */
 467         num_outmbox  =  (1 << state->hs_cfg_profile->cp_log_num_outmbox);
 468         status = hermon_impl_mboxlist_init(state, &state->hs_out_mblist,
 469             num_outmbox, HERMON_OUT_MBOX);
 470         if (status != DDI_SUCCESS) {
 471                 return (DDI_FAILURE);
 472         }
 473 
 474         return (DDI_SUCCESS);
 475 }
 476 
 477 
 478 /*
 479  * hermon_intr_outmbox_list_init()
 480  *    Context: Only called from attach() path context
 481  */
 482 int
 483 hermon_intr_outmbox_list_init(hermon_state_t *state)
 484 {
 485         int             status;
 486         uint_t          num_outmbox;
 487 
 488         /* Initialize the interrupts "Out" mailbox list */
 489         num_outmbox  =  (1 << state->hs_cfg_profile->cp_log_num_intr_outmbox);
 490         status = hermon_impl_mboxlist_init(state, &state->hs_out_intr_mblist,
 491             num_outmbox, HERMON_INTR_OUT_MBOX);
 492         if (status != DDI_SUCCESS) {
 493                 return (DDI_FAILURE);
 494         }
 495 
 496         return (DDI_SUCCESS);
 497 }
 498 
 499 
 500 /*
 501  * hermon_inmbox_list_fini()
 502  *    Context: Only called from attach() and/or detach() path contexts
 503  */
 504 void
 505 hermon_inmbox_list_fini(hermon_state_t *state)
 506 {
 507         /* Free up the "In" mailbox list */
 508         hermon_impl_mboxlist_fini(state, &state->hs_in_mblist);
 509 }
 510 
 511 
 512 /*
 513  * hermon_intr_inmbox_list_fini()
 514  *    Context: Only called from attach() and/or detach() path contexts
 515  */
 516 void
 517 hermon_intr_inmbox_list_fini(hermon_state_t *state)
 518 {
 519         /* Free up the interupts "In" mailbox list */
 520         hermon_impl_mboxlist_fini(state, &state->hs_in_intr_mblist);
 521 }
 522 
 523 
 524 /*
 525  * hermon_outmbox_list_fini()
 526  *    Context: Only called from attach() and/or detach() path contexts
 527  */
 528 void
 529 hermon_outmbox_list_fini(hermon_state_t *state)
 530 {
 531         /* Free up the "Out" mailbox list */
 532         hermon_impl_mboxlist_fini(state, &state->hs_out_mblist);
 533 }
 534 
 535 
 536 /*
 537  * hermon_intr_outmbox_list_fini()
 538  *    Context: Only called from attach() and/or detach() path contexts
 539  */
 540 void
 541 hermon_intr_outmbox_list_fini(hermon_state_t *state)
 542 {
 543         /* Free up the interrupt "Out" mailbox list */
 544         hermon_impl_mboxlist_fini(state, &state->hs_out_intr_mblist);
 545 }
 546 
 547 
 548 /*
 549  * hermon_impl_mbox_alloc()
 550  *    Context: Can be called from interrupt or base context.
 551  */
 552 static int
 553 hermon_impl_mbox_alloc(hermon_state_t *state, hermon_mboxlist_t *mblist,
 554     hermon_mbox_t **mb, uint_t mbox_wait)
 555 {
 556         hermon_mbox_t   *mbox_ptr;
 557         uint_t          index, next, prev;
 558         uint_t          count, countmax;
 559 
 560         /*
 561          * If the mailbox list is empty, then wait (if appropriate in the
 562          * current context).  Otherwise, grab the next available mailbox.
 563          */
 564         if (mbox_wait == HERMON_NOSLEEP) {
 565                 count    = 0;
 566                 countmax = state->hs_cfg_profile->cp_cmd_poll_max;
 567 
 568                 mutex_enter(&mblist->mbl_lock);
 569                 mblist->mbl_pollers++;
 570                 while (mblist->mbl_entries_free == 0) {
 571                         mutex_exit(&mblist->mbl_lock);
 572                         /* Delay loop polling for an available mbox */
 573                         if (++count > countmax) {
 574                                 return (HERMON_CMD_INSUFF_RSRC);
 575                         }
 576 
 577                         /* Delay before polling for mailbox again */
 578                         drv_usecwait(state->hs_cfg_profile->cp_cmd_poll_delay);
 579                         mutex_enter(&mblist->mbl_lock);
 580                 }
 581                 mblist->mbl_pollers--;
 582 
 583         /* HERMON_SLEEP */
 584         } else {
 585                 /*
 586                  * Grab lock here as we prepare to cv_wait if needed.
 587                  */
 588                 mutex_enter(&mblist->mbl_lock);
 589                 while (mblist->mbl_entries_free == 0) {
 590                         /*
 591                          * Wait (on cv) for a mailbox to become free.
 592                          */
 593                         mblist->mbl_waiters++;
 594                         cv_wait(&mblist->mbl_cv, &mblist->mbl_lock);
 595                 }
 596         }
 597 
 598         /* Grab the next available mailbox from list */
 599         mbox_ptr = mblist->mbl_mbox;
 600         index    = mblist->mbl_head_indx;
 601         next     = mbox_ptr[index].mb_next;
 602         prev     = mbox_ptr[index].mb_prev;
 603 
 604         /* Remove it from the mailbox list */
 605         mblist->mbl_mbox[next].mb_prev       = prev;
 606         mblist->mbl_mbox[prev].mb_next       = next;
 607         mblist->mbl_head_indx                = next;
 608 
 609         /* Update the "free" count and return the mailbox pointer */
 610         mblist->mbl_entries_free--;
 611         *mb = &mbox_ptr[index];
 612 
 613         mutex_exit(&mblist->mbl_lock);
 614 
 615         return (HERMON_CMD_SUCCESS);
 616 }
 617 
 618 
 619 /*
 620  * hermon_impl_mbox_free()
 621  *    Context: Can be called from interrupt or base context.
 622  */
 623 static void
 624 hermon_impl_mbox_free(hermon_mboxlist_t *mblist, hermon_mbox_t **mb)
 625 {
 626         uint_t          mbox_indx;
 627 
 628         mutex_enter(&mblist->mbl_lock);
 629 
 630         /* Pull the "index" from mailbox entry */
 631         mbox_indx = (*mb)->mb_indx;
 632 
 633         /*
 634          * If mailbox list is not empty, then insert the entry.  Otherwise,
 635          * this is the only entry.  So update the pointers appropriately.
 636          */
 637         if (mblist->mbl_entries_free++ != 0) {
 638                 /* Update the current mailbox */
 639                 (*mb)->mb_next = mblist->mbl_head_indx;
 640                 (*mb)->mb_prev = mblist->mbl_tail_indx;
 641 
 642                 /* Update head and tail mailboxes */
 643                 mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev = mbox_indx;
 644                 mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next = mbox_indx;
 645 
 646                 /* Update tail index */
 647                 mblist->mbl_tail_indx = mbox_indx;
 648 
 649         } else {
 650                 /* Update the current mailbox */
 651                 (*mb)->mb_next = mbox_indx;
 652                 (*mb)->mb_prev = mbox_indx;
 653 
 654                 /* Update head and tail indexes */
 655                 mblist->mbl_tail_indx = mbox_indx;
 656                 mblist->mbl_head_indx = mbox_indx;
 657         }
 658 
 659         /*
 660          * Because we can have both waiters (SLEEP treads waiting for a
 661          * cv_signal to continue processing) and pollers (NOSLEEP treads
 662          * polling for a mailbox to become available), we try to share CPU time
 663          * between them.  We do this by signalling the waiters only every other
 664          * call to mbox_free.  This gives the pollers a chance to get some CPU
 665          * time to do their command.  If we signalled every time, the pollers
 666          * would have a much harder time getting CPU time.
 667          *
 668          * If there are waiters and no pollers, then we signal always.
 669          *
 670          * Otherwise, if there are either no waiters, there may in fact be
 671          * pollers, so we do not signal in that case.
 672          */
 673         if (mblist->mbl_pollers > 0 && mblist->mbl_waiters > 0) {
 674                 /* flip the signal value */
 675                 mblist->mbl_signal = (mblist->mbl_signal + 1) % 2;
 676         } else if (mblist->mbl_waiters > 0) {
 677                 mblist->mbl_signal = 1;
 678         } else {
 679                 mblist->mbl_signal = 0;
 680         }
 681 
 682         /*
 683          * Depending on the conditions in the previous check, we signal only if
 684          * we are supposed to.
 685          */
 686         if (mblist->mbl_signal) {
 687                 mblist->mbl_waiters--;
 688                 cv_signal(&mblist->mbl_cv);
 689         }
 690 
 691         /* Clear out the mailbox entry pointer */
 692         *mb = NULL;
 693 
 694         mutex_exit(&mblist->mbl_lock);
 695 }
 696 
 697 
 698 /*
 699  * hermon_impl_mboxlist_init()
 700  *    Context: Only called from attach() path context
 701  */
 702 static int
 703 hermon_impl_mboxlist_init(hermon_state_t *state, hermon_mboxlist_t *mblist,
 704     uint_t num_mbox, hermon_rsrc_type_t type)
 705 {
 706         hermon_rsrc_t           *rsrc;
 707         ddi_dma_cookie_t        dma_cookie;
 708         uint_t                  dma_cookiecnt;
 709         int                     status, i;
 710 
 711         /* Allocate the memory for the mailbox entries list */
 712         mblist->mbl_list_sz = num_mbox;
 713         mblist->mbl_mbox = kmem_zalloc(mblist->mbl_list_sz *
 714             sizeof (hermon_mbox_t), KM_SLEEP);
 715 
 716         /* Initialize the mailbox entries list */
 717         mblist->mbl_head_indx         = 0;
 718         mblist->mbl_tail_indx         = mblist->mbl_list_sz - 1;
 719         mblist->mbl_entries_free = mblist->mbl_list_sz;
 720         mblist->mbl_waiters   = 0;
 721         mblist->mbl_num_alloc         = 0;
 722 
 723         /* Set up the mailbox list's cv and mutex */
 724         mutex_init(&mblist->mbl_lock, NULL, MUTEX_DRIVER,
 725             DDI_INTR_PRI(state->hs_intrmsi_pri));
 726         cv_init(&mblist->mbl_cv, NULL, CV_DRIVER, NULL);
 727 
 728         /* Initialize the mailbox list entries */
 729         for (i = 0; i < mblist->mbl_list_sz; i++) {
 730                 /* Allocate resources for the mailbox */
 731                 status = hermon_rsrc_alloc(state, type, 1, HERMON_SLEEP,
 732                     &rsrc);
 733                 if (status != DDI_SUCCESS) {
 734                         /* Jump to cleanup and return error */
 735                         goto mboxlist_init_fail;
 736                 }
 737 
 738                 /* Save away the mailbox resource info */
 739                 mblist->mbl_mbox[i].mb_rsrcptr       = rsrc;
 740                 mblist->mbl_mbox[i].mb_addr  = rsrc->hr_addr;
 741                 mblist->mbl_mbox[i].mb_acchdl        = rsrc->hr_acchdl;
 742 
 743                 /*
 744                  * Get a PCI mapped address for each mailbox.  Note: this
 745                  * uses the ddi_dma_handle return from the resource
 746                  * allocation routine
 747                  */
 748                 status = ddi_dma_addr_bind_handle(rsrc->hr_dmahdl, NULL,
 749                     rsrc->hr_addr, rsrc->hr_len,
 750                     (DDI_DMA_RDWR | DDI_DMA_CONSISTENT),
 751                     DDI_DMA_SLEEP, NULL, &dma_cookie, &dma_cookiecnt);
 752                 if (status != DDI_SUCCESS) {
 753                         /* Jump to cleanup and return error */
 754                         hermon_rsrc_free(state, &rsrc);
 755                         goto mboxlist_init_fail;
 756                 }
 757 
 758                 /* Save away the mapped address for the mailbox */
 759                 mblist->mbl_mbox[i].mb_mapaddr       = dma_cookie.dmac_laddress;
 760 
 761                 /* Make each entry point to the "next" and "prev" entries */
 762                 mblist->mbl_mbox[i].mb_next  = i+1;
 763                 mblist->mbl_mbox[i].mb_prev  = i-1;
 764                 mblist->mbl_mbox[i].mb_indx  = i;
 765                 mblist->mbl_num_alloc                = i + 1;
 766         }
 767 
 768         /* Make the "head" and "tail" entries point to each other */
 769         mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev =
 770             mblist->mbl_tail_indx;
 771         mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next =
 772             mblist->mbl_head_indx;
 773 
 774         return (DDI_SUCCESS);
 775 
 776 mboxlist_init_fail:
 777         hermon_impl_mboxlist_fini(state, mblist);
 778 
 779         return (DDI_FAILURE);
 780 }
 781 
 782 
 783 /*
 784  * hermon_impl_mboxlist_fini()
 785  *    Context: Only called from attach() and/or detach() path contexts
 786  */
 787 static void
 788 hermon_impl_mboxlist_fini(hermon_state_t *state, hermon_mboxlist_t *mblist)
 789 {
 790         hermon_rsrc_t   *rsrc;
 791         int             i, status;
 792 
 793         /* Release the resources for each of the mailbox list entries */
 794         for (i = 0; i < mblist->mbl_num_alloc; i++) {
 795                 rsrc = mblist->mbl_mbox[i].mb_rsrcptr;
 796 
 797                 /*
 798                  * First, unbind the DMA memory for the mailbox
 799                  *
 800                  * Note: The only way ddi_dma_unbind_handle() currently
 801                  * can return an error is if the handle passed in is invalid.
 802                  * Since this should never happen, we choose to return void
 803                  * from this function!  If this does return an error,
 804                  * however, then we print a warning message to the console.
 805                  */
 806                 status = ddi_dma_unbind_handle(rsrc->hr_dmahdl);
 807                 if (status != DDI_SUCCESS) {
 808                         HERMON_WARNING(state, "failed to unbind DMA mapping");
 809                         return;
 810                 }
 811 
 812                 /* Next, free the mailbox resource */
 813                 hermon_rsrc_free(state, &rsrc);
 814         }
 815 
 816         /* Destroy the mailbox list mutex and cv */
 817         mutex_destroy(&mblist->mbl_lock);
 818         cv_destroy(&mblist->mbl_cv);
 819 
 820         /* Free up the memory for tracking the mailbox list */
 821         kmem_free(mblist->mbl_mbox, mblist->mbl_list_sz *
 822             sizeof (hermon_mbox_t));
 823 }
 824 
 825 
 826 /*
 827  * hermon_outstanding_cmd_alloc()
 828  *    Context: Can be called only from base context.
 829  */
 830 static int
 831 hermon_outstanding_cmd_alloc(hermon_state_t *state, hermon_cmd_t **cmd_ptr,
 832     uint_t cmd_wait)
 833 {
 834         hermon_cmdlist_t        *cmd_list;
 835         uint_t          next, prev, head;
 836 
 837         cmd_list = &state->hs_cmd_list;
 838         mutex_enter(&cmd_list->cml_lock);
 839 
 840         /* Ensure that outstanding commands are supported */
 841         ASSERT(cmd_list->cml_num_alloc != 0);
 842 
 843         /*
 844          * If the outstanding command list is empty, then wait (if
 845          * appropriate in the current context).  Otherwise, grab the
 846          * next available command.
 847          */
 848         while (cmd_list->cml_entries_free == 0) {
 849                 /* No free commands */
 850                 if (cmd_wait == HERMON_NOSLEEP) {
 851                         mutex_exit(&cmd_list->cml_lock);
 852                         return (HERMON_CMD_INSUFF_RSRC);
 853                 }
 854 
 855                 /*
 856                  * Wait (on cv) for a command to become free.
 857                  */
 858                 cmd_list->cml_waiters++;
 859                 cv_wait(&cmd_list->cml_cv, &cmd_list->cml_lock);
 860         }
 861 
 862         /* Grab the next available command from the list */
 863         head = cmd_list->cml_head_indx;
 864         *cmd_ptr = &cmd_list->cml_cmd[head];
 865         next = (*cmd_ptr)->cmd_next;
 866         prev = (*cmd_ptr)->cmd_prev;
 867         (*cmd_ptr)->cmd_status = HERMON_CMD_INVALID_STATUS;
 868 
 869         /* Remove it from the command list */
 870         cmd_list->cml_cmd[next].cmd_prev = prev;
 871         cmd_list->cml_cmd[prev].cmd_next = next;
 872         cmd_list->cml_head_indx               = next;
 873 
 874         /* Update the "free" count and return */
 875         cmd_list->cml_entries_free--;
 876 
 877         mutex_exit(&cmd_list->cml_lock);
 878 
 879         return (HERMON_CMD_SUCCESS);
 880 }
 881 
 882 
 883 /*
 884  * hermon_outstanding_cmd_free()
 885  *    Context: Can be called only from base context.
 886  */
 887 static void
 888 hermon_outstanding_cmd_free(hermon_state_t *state, hermon_cmd_t **cmd_ptr)
 889 {
 890         hermon_cmdlist_t        *cmd_list;
 891         uint_t          cmd_indx;
 892 
 893         cmd_list = &state->hs_cmd_list;
 894         mutex_enter(&cmd_list->cml_lock);
 895 
 896         /* Pull the "index" from command entry */
 897         cmd_indx = (*cmd_ptr)->cmd_indx;
 898 
 899         /*
 900          * If outstanding command list is not empty, then insert the entry.
 901          * Otherwise, this is the only entry.  So update the pointers
 902          * appropriately.
 903          */
 904         if (cmd_list->cml_entries_free++ != 0) {
 905                 /* Update the current command */
 906                 (*cmd_ptr)->cmd_next = cmd_list->cml_head_indx;
 907                 (*cmd_ptr)->cmd_prev = cmd_list->cml_tail_indx;
 908 
 909                 /* Update head and tail commands */
 910                 cmd_list->cml_cmd[cmd_list->cml_head_indx].cmd_prev = cmd_indx;
 911                 cmd_list->cml_cmd[cmd_list->cml_tail_indx].cmd_next = cmd_indx;
 912 
 913                 /* Update tail index */
 914                 cmd_list->cml_tail_indx = cmd_indx;
 915 
 916         } else {
 917                 /* Update the current command */
 918                 (*cmd_ptr)->cmd_next = cmd_indx;
 919                 (*cmd_ptr)->cmd_prev = cmd_indx;
 920 
 921                 /* Update head and tail indexes */
 922                 cmd_list->cml_head_indx = cmd_indx;
 923                 cmd_list->cml_tail_indx = cmd_indx;
 924         }
 925 
 926         /* If there are threads waiting, signal one of them */
 927         if (cmd_list->cml_waiters > 0) {
 928                 cmd_list->cml_waiters--;
 929                 cv_signal(&cmd_list->cml_cv);
 930         }
 931 
 932         /* Clear out the command entry pointer */
 933         *cmd_ptr = NULL;
 934 
 935         mutex_exit(&cmd_list->cml_lock);
 936 }
 937 
 938 
 939 /*
 940  * hermon_write_hcr()
 941  *    Context: Can be called from interrupt or base context.
 942  */
 943 static int
 944 hermon_write_hcr(hermon_state_t *state, hermon_cmd_post_t *cmdpost,
 945     uint16_t token, int *hw_err)
 946 {
 947         hermon_hw_hcr_t *hcr;
 948         uint_t          status, count, countmax;
 949         uint64_t        hcrreg;
 950         uint64_t        togmask;
 951         ddi_acc_handle_t cmdhdl = hermon_get_cmdhdl(state);
 952         boolean_t       hw_error = B_FALSE;
 953 
 954         /* initialize the FMA retry loop */
 955         hermon_pio_init(fm_loop_cnt, fm_status, fm_test);
 956 
 957         /*
 958          * Grab the "HCR access" lock if the driver is not in
 959          * fastreboot. In fastreboot, this function is called
 960          * with the single thread but in high interrupt context
 961          * (so that this mutex lock cannot be used).
 962          */
 963         if (!HERMON_IN_FASTREBOOT(state)) {
 964                 mutex_enter(&state->hs_cmd_regs.hcr_lock);
 965         }
 966         hcr = state->hs_cmd_regs.hcr;
 967 
 968         /*
 969          * First, check the "go" bit to see if any previous hcr usage is
 970          * complete.  As long as it is set then we must continue to poll.
 971          */
 972 
 973         countmax = state->hs_cfg_profile->cp_cmd_poll_max;
 974         togmask = (state->hs_cmd_toggle & 0x01) << HERMON_HCR_CMD_T_SHFT;
 975 
 976         /* the FMA retry loop starts. */
 977         hermon_pio_start(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
 978             fm_test);
 979 
 980         count    = 0;
 981         for (;;) {
 982                 hcrreg = ddi_get32(cmdhdl, &hcr->cmd);
 983 
 984                 /* If "go" bit is clear and toggle reset, then done */
 985                 if (((hcrreg & HERMON_HCR_CMD_GO_MASK) == 0) &&
 986                     ((hcrreg & HERMON_HCR_CMD_T_MASK)  == togmask)) {
 987                         break;
 988                 }
 989                 /* Delay before polling the "go" bit again */
 990                 drv_usecwait(state->hs_cfg_profile->cp_cmd_poll_delay);
 991 
 992                 /*
 993                  * If we poll more than the maximum number of times, then
 994                  * return a "timeout" error.
 995                  */
 996                 if (++count > countmax) {
 997                         if (!HERMON_IN_FASTREBOOT(state)) {
 998                                 mutex_exit(&state->hs_cmd_regs.hcr_lock);
 999                         }
1000                         cmn_err(CE_NOTE, "write_hcr: cannot start cmd");
1001                         return (HERMON_CMD_TIMEOUT_GOBIT);
1002                 }
1003         }
1004 
1005         /* the FMA retry loop ends. */
1006         hermon_pio_end(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1007             fm_test);
1008 
1009         /* check if there is a transient error */
1010         if (fm_loop_cnt != HCA_PIO_RETRY_CNT) {
1011                 hw_error = B_TRUE;
1012         }
1013 
1014         /* succeeded, so update the cmd counter for this cmd's completion */
1015         state->hs_cmd_toggle++;
1016         togmask = (state->hs_cmd_toggle & 0x01) << HERMON_HCR_CMD_T_SHFT;
1017 
1018         /* the FMA retry loop starts. */
1019         hermon_pio_start(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1020             fm_test);
1021 
1022         /* Write "inparam" as a 64-bit quantity */
1023         ddi_put64(cmdhdl, (uint64_t *)(void *)&hcr->in_param0,
1024             cmdpost->cp_inparm);
1025 
1026         /* Write "inmod" and 32-bits of "outparam" as 64-bit */
1027         hcrreg = ((uint64_t)cmdpost->cp_inmod << 32);
1028         hcrreg = hcrreg | (cmdpost->cp_outparm >> 32);
1029 
1030         ddi_put64(cmdhdl, (uint64_t *)(void *)&hcr->input_modifier, hcrreg);
1031 
1032         /* Write the other 32-bits of "outparam" and "token" as 64-bit */
1033         hcrreg = (cmdpost->cp_outparm << 32);
1034         hcrreg = hcrreg | ((uint32_t)token << HERMON_HCR_TOKEN_SHIFT);
1035 
1036         ddi_put64(cmdhdl, (uint64_t *)(void *)&hcr->out_param1, hcrreg);
1037 
1038         /* Then setup the final hcrreg to hit doorbell (i.e. "go" bit) */
1039         hcrreg = HERMON_HCR_CMD_GO_MASK;
1040         /* Then set the toggle bit for this command */
1041         hcrreg |= (state->hs_cmd_toggle & 0x01) << HERMON_HCR_CMD_T_SHFT;
1042         if (cmdpost->cp_flags == HERMON_CMD_SLEEP_NOSPIN) {
1043                 hcrreg = hcrreg | HERMON_HCR_CMD_E_MASK;
1044         }
1045         hcrreg = hcrreg | (cmdpost->cp_opmod << HERMON_HCR_CMD_OPMOD_SHFT);
1046         hcrreg = hcrreg | (cmdpost->cp_opcode);
1047 
1048         /* Write the doorbell to the HCR */
1049         ddi_put32(cmdhdl, &hcr->cmd, hcrreg);
1050 
1051         /* the FMA retry loop ends. */
1052         hermon_pio_end(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1053             fm_test);
1054 
1055         /* check if there is a transient error */
1056         if (fm_loop_cnt != HCA_PIO_RETRY_CNT) {
1057                 hw_error = B_TRUE;
1058         }
1059 
1060         /*
1061          * In the SPIN case we read the HCR and check the "go" bit.  For the
1062          * NOSPIN case we do not have to poll, we simply release the HCR lock
1063          * and return.
1064          */
1065         if (cmdpost->cp_flags == HERMON_CMD_NOSLEEP_SPIN) {
1066 
1067                 countmax = (state->hs_cfg_profile->cp_cmd_poll_max << 4);
1068 
1069                 /* the FMA retry loop starts. */
1070                 hermon_pio_start(state, cmdhdl, pio_error, fm_loop_cnt,
1071                     fm_status, fm_test);
1072 
1073                 count    = 0;
1074                 for (;;) {
1075                         hcrreg = ddi_get32(cmdhdl, &hcr->cmd);
1076 
1077                         /* If "go" bit is clear and toggle reset, then done */
1078                         if (((hcrreg & HERMON_HCR_CMD_GO_MASK) == 0) &&
1079                             ((hcrreg & HERMON_HCR_CMD_T_MASK)  == togmask)) {
1080                                 break;
1081                         }
1082                         /* Delay before polling the "go" bit again */
1083                         drv_usecwait(state->hs_cfg_profile->cp_cmd_poll_delay);
1084 
1085                         /*
1086                          * If we poll more than the maximum number of times,
1087                          * then return a "timeout" error.
1088                          */
1089                         if (++count > countmax) {
1090                                 if (!HERMON_IN_FASTREBOOT(state)) {
1091                                         mutex_exit(&state->
1092                                             hs_cmd_regs.hcr_lock);
1093                                 }
1094                                 cmn_err(CE_NOTE,
1095                                     "write_hcr: cannot complete cmd");
1096                                 return (HERMON_CMD_TIMEOUT_GOBIT);
1097                         }
1098                 }
1099 
1100                 /* Pull out the "status" bits from the HCR */
1101                 status = (hcrreg >> HERMON_HCR_CMD_STATUS_SHFT);
1102 
1103                 /*
1104                  * Read the "outparam" value.  Note: we have to read "outparam"
1105                  * as two separate 32-bit reads because the field in the HCR is
1106                  * not 64-bit aligned.
1107                  */
1108                 hcrreg = ddi_get32(cmdhdl, &hcr->out_param0);
1109                 cmdpost->cp_outparm = hcrreg << 32;
1110                 hcrreg = ddi_get32(cmdhdl, &hcr->out_param1);
1111                 cmdpost->cp_outparm |= hcrreg;
1112 
1113                 /* the FMA retry loop ends. */
1114                 hermon_pio_end(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1115                     fm_test);
1116 
1117                 /* check if there is a transient error */
1118                 if (fm_loop_cnt != HCA_PIO_RETRY_CNT) {
1119                         hw_error = B_TRUE;
1120                 }
1121 
1122         /* END SPIN */
1123         } else {                /* NOSPIN */
1124                 status = HERMON_CMD_SUCCESS;
1125         }
1126 
1127         /* Drop the "HCR access" lock */
1128         if (!HERMON_IN_FASTREBOOT(state)) {
1129                 mutex_exit(&state->hs_cmd_regs.hcr_lock);
1130         }
1131         if (hw_error == B_TRUE) {
1132                 *hw_err = HCA_PIO_TRANSIENT;
1133         } else {
1134                 *hw_err = HCA_PIO_OK;
1135         }
1136 #ifdef FMA_TEST
1137         if (hermon_test_num == -3) {
1138                 status = HERMON_CMD_INTERNAL_ERR;
1139         }
1140 #endif
1141         return (status);
1142 
1143 pio_error:
1144         if (!HERMON_IN_FASTREBOOT(state)) {
1145                 mutex_exit(&state->hs_cmd_regs.hcr_lock);
1146         }
1147         hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_NON_FATAL);
1148         *hw_err = HCA_PIO_PERSISTENT;
1149         return (HERMON_CMD_INVALID_STATUS);
1150 }
1151 
1152 
1153 /*
1154  * hermon_outstanding_cmdlist_init()
1155  *    Context: Only called from attach() path context
1156  */
1157 int
1158 hermon_outstanding_cmdlist_init(hermon_state_t *state)
1159 {
1160         uint_t          num_outstanding_cmds, head, tail;
1161         int             i;
1162 
1163         /*
1164          * Determine the number of the outstanding commands supported
1165          * by the Hermon device (obtained from the QUERY_FW command).  Note:
1166          * Because we handle both SLEEP and NOSLEEP cases around the hermon HCR,
1167          * we know that when an interrupt comes in it will be next on the
1168          * command register, and will at most have to wait one commands time.
1169          * We do not have to reserve an outstanding command here for
1170          * interrupts.
1171          */
1172         num_outstanding_cmds = (1 << state->hs_fw.log_max_cmd);
1173 
1174         /* Initialize the outstanding command list */
1175         state->hs_cmd_list.cml_list_sz        = num_outstanding_cmds;
1176         state->hs_cmd_list.cml_head_indx = 0;
1177         state->hs_cmd_list.cml_tail_indx = state->hs_cmd_list.cml_list_sz - 1;
1178         state->hs_cmd_list.cml_entries_free = state->hs_cmd_list.cml_list_sz;
1179         state->hs_cmd_list.cml_waiters        = 0;
1180         state->hs_cmd_list.cml_num_alloc = 0;
1181 
1182         /* Allocate the memory for the outstanding command list */
1183         if (num_outstanding_cmds) {
1184                 state->hs_cmd_list.cml_cmd =
1185                     kmem_zalloc(state->hs_cmd_list.cml_list_sz *
1186                     sizeof (hermon_cmd_t), KM_SLEEP);
1187         }
1188         mutex_init(&state->hs_cmd_list.cml_lock, NULL, MUTEX_DRIVER,
1189             DDI_INTR_PRI(state->hs_intrmsi_pri));
1190         cv_init(&state->hs_cmd_list.cml_cv, NULL, CV_DRIVER, NULL);
1191 
1192         /* Initialize the individual outstanding command list entries */
1193         for (i = 0; i < state->hs_cmd_list.cml_list_sz; i++) {
1194                 mutex_init(&state->hs_cmd_list.cml_cmd[i].cmd_comp_lock,
1195                     NULL, MUTEX_DRIVER, DDI_INTR_PRI(state->hs_intrmsi_pri));
1196                 cv_init(&state->hs_cmd_list.cml_cmd[i].cmd_comp_cv, NULL,
1197                     CV_DRIVER, NULL);
1198 
1199                 state->hs_cmd_list.cml_cmd[i].cmd_next       = i+1;
1200                 state->hs_cmd_list.cml_cmd[i].cmd_prev       = i-1;
1201                 state->hs_cmd_list.cml_cmd[i].cmd_indx       = i;
1202                 state->hs_cmd_list.cml_num_alloc     = i + 1;
1203         }
1204         if (num_outstanding_cmds) {
1205                 head = state->hs_cmd_list.cml_head_indx;
1206                 tail = state->hs_cmd_list.cml_tail_indx;
1207                 state->hs_cmd_list.cml_cmd[head].cmd_prev =
1208                     state->hs_cmd_list.cml_tail_indx;
1209                 state->hs_cmd_list.cml_cmd[tail].cmd_next =
1210                     state->hs_cmd_list.cml_head_indx;
1211         }
1212 
1213         return (DDI_SUCCESS);
1214 }
1215 
1216 
1217 /*
1218  * hermon_outstanding_cmdlist_fini()
1219  *    Context: Only called from attach() and/or detach() path contexts
1220  */
1221 void
1222 hermon_outstanding_cmdlist_fini(hermon_state_t *state)
1223 {
1224         int             i;
1225 
1226         /* Destroy the outstanding command list entries */
1227         for (i = 0; i < state->hs_cmd_list.cml_num_alloc; i++) {
1228                 mutex_destroy(&state->hs_cmd_list.cml_cmd[i].cmd_comp_lock);
1229                 cv_destroy(&state->hs_cmd_list.cml_cmd[i].cmd_comp_cv);
1230         }
1231 
1232         /* Destroy the lock (and cv) and free up memory for list */
1233         mutex_destroy(&state->hs_cmd_list.cml_lock);
1234         cv_destroy(&state->hs_cmd_list.cml_cv);
1235         if (state->hs_cmd_list.cml_num_alloc) {
1236                 kmem_free(state->hs_cmd_list.cml_cmd,
1237                     state->hs_cmd_list.cml_list_sz * sizeof (hermon_cmd_t));
1238         }
1239 }
1240 
1241 
1242 /*
1243  * hermon_mbox_sync()
1244  */
1245 static void
1246 hermon_mbox_sync(hermon_mbox_t *mbox, uint_t offset, uint_t length,
1247     uint_t flag)
1248 {
1249         ddi_dma_handle_t        dmahdl;
1250         int                     status;
1251 
1252         /* Get the DMA handle from mailbox */
1253         dmahdl = mbox->mb_rsrcptr->hr_dmahdl;
1254 
1255         /* Calculate offset into mailbox */
1256         status = ddi_dma_sync(dmahdl, (off_t)offset, (size_t)length, flag);
1257         if (status != DDI_SUCCESS) {
1258                 return;
1259         }
1260 }
1261 
1262 
1263 /*
1264  * hermon_init_hca_cmd_post()
1265  *    Context: Can be called from interrupt or base context.
1266  *    (Currently called only from attach() path context)
1267  */
1268 int
1269 hermon_init_hca_cmd_post(hermon_state_t *state,
1270     hermon_hw_initqueryhca_t *inithca, uint_t sleepflag)
1271 {
1272         hermon_mbox_info_t      mbox_info;
1273         hermon_cmd_post_t       cmd;
1274         uint64_t                data;
1275         uint_t                  size;
1276         int                     status, i;
1277 
1278         /* Make sure we are called with the correct flag */
1279         ASSERT(sleepflag == HERMON_CMD_NOSLEEP_SPIN);
1280 
1281         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1282 
1283         /* Get an "In" mailbox for the command */
1284         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
1285         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
1286         if (status != HERMON_CMD_SUCCESS) {
1287                 return (status);
1288         }
1289 
1290         /* Copy the Hermon "INIT_HCA" command into the mailbox */
1291         size = sizeof (hermon_hw_initqueryhca_t);
1292         for (i = 0; i < (size >> 3); i++) {
1293                 data = ((uint64_t *)inithca)[i];
1294                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1295                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
1296         }
1297 
1298         /* Sync the mailbox for the device to read */
1299         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1300 
1301         /* Setup and post the Hermon "INIT_HCA" command */
1302         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1303         cmd.cp_outparm  = 0;
1304         cmd.cp_inmod    = 0;
1305         cmd.cp_opcode   = INIT_HCA;
1306         cmd.cp_opmod    = 0;
1307         cmd.cp_flags    = sleepflag;
1308         status = hermon_cmd_post(state, &cmd);
1309 
1310         /* Free the mailbox */
1311         hermon_mbox_free(state, &mbox_info);
1312         return (status);
1313 }
1314 
1315 
1316 /*
1317  * hermon_close_hca_cmd_post()
1318  *    Context: Can be called from interrupt or base context.
1319  *    (Currently called only from attach() and/or detach() path contexts)
1320  */
1321 int
1322 hermon_close_hca_cmd_post(hermon_state_t *state, uint_t sleepflag)
1323 {
1324         hermon_cmd_post_t       cmd;
1325         int                     status;
1326 
1327         /* Make sure we are called with the correct flag */
1328         ASSERT(sleepflag == HERMON_CMD_NOSLEEP_SPIN);
1329 
1330         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1331 
1332 
1333         /* Setup and post the Hermon "CLOSE_HCA" command */
1334         cmd.cp_inparm   = 0;
1335         cmd.cp_outparm  = 0;
1336         cmd.cp_inmod    = 0;
1337         cmd.cp_opcode   = CLOSE_HCA;
1338         cmd.cp_opmod    = 0;
1339         cmd.cp_flags    = sleepflag;
1340         status = hermon_cmd_post(state, &cmd);
1341         return (status);
1342 }
1343 
1344 
1345 /*
1346  * hermon_set_port_cmd_post()
1347  *    Context: Can be called from interrupt or base context.
1348  *    (Currently called only from attach() path context)
1349  */
1350 int
1351 hermon_set_port_cmd_post(hermon_state_t *state, hermon_hw_set_port_t *initport,
1352     uint_t port, uint_t sleepflag)
1353 {
1354         hermon_mbox_info_t      mbox_info;
1355         hermon_cmd_post_t       cmd;
1356         uint64_t                data;
1357         uint_t                  size;
1358         int                     status, i;
1359 
1360         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1361 
1362         /* Get an "In" mailbox for the command */
1363         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
1364         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
1365         if (status != HERMON_CMD_SUCCESS) {
1366                 return (status);
1367         }
1368 
1369         /* Copy the Hermon "INIT_PORT" command into the mailbox */
1370         size = sizeof (hermon_hw_set_port_t);
1371         for (i = 0; i < (size >> 3); i++) {
1372                 data = ((uint64_t *)initport)[i];
1373                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1374                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
1375         }
1376 
1377         /* Sync the mailbox for the device to read */
1378         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1379 
1380         /* Setup and post the Hermon "SET_PORT" command */
1381         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1382         cmd.cp_outparm  = 0;
1383         cmd.cp_inmod    = port;
1384         cmd.cp_opcode   = SET_PORT;
1385         cmd.cp_opmod    = 0;
1386         cmd.cp_flags    = sleepflag;
1387         status = hermon_cmd_post(state, &cmd);
1388 
1389         /* Free the mailbox */
1390         hermon_mbox_free(state, &mbox_info);
1391         return (status);
1392 }
1393 
1394 
1395 /*
1396  * hermon_init_port_cmd_post()
1397  *    Context: Can be called from interrupt or base context.
1398  *    (Currently called only from attach() and/or detach() path contexts)
1399  */
1400 int
1401 hermon_init_port_cmd_post(hermon_state_t *state, uint_t port, uint_t sleepflag)
1402 {
1403         hermon_cmd_post_t       cmd;
1404         int                     status;
1405 
1406         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1407 
1408         /* Setup and post the Hermon "INIT_PORT" command */
1409         cmd.cp_inparm   = 0;
1410         cmd.cp_outparm  = 0;
1411         cmd.cp_inmod    = port;
1412         cmd.cp_opcode   = INIT_PORT;
1413         cmd.cp_opmod    = 0;
1414         cmd.cp_flags    = sleepflag;
1415         status = hermon_cmd_post(state, &cmd);
1416 
1417         return (status);
1418 }
1419 
1420 
1421 /*
1422  * hermon_close_port_cmd_post()
1423  *    Context: Can be called from interrupt or base context.
1424  *    (Currently called only from attach() and/or detach() path contexts)
1425  */
1426 int
1427 hermon_close_port_cmd_post(hermon_state_t *state, uint_t port, uint_t sleepflag)
1428 {
1429         hermon_cmd_post_t       cmd;
1430         int                     status;
1431 
1432         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1433 
1434         /* Setup and post the Hermon "CLOSE_PORT" command */
1435         cmd.cp_inparm   = 0;
1436         cmd.cp_outparm  = 0;
1437         cmd.cp_inmod    = port;
1438         cmd.cp_opcode   = CLOSE_PORT;
1439         cmd.cp_opmod    = 0;
1440         cmd.cp_flags    = sleepflag;
1441         status = hermon_cmd_post(state, &cmd);
1442         return (status);
1443 }
1444 
1445 
1446 /*
1447  * hermon_mod_stat_cfg_cmd_post()
1448  *    Context: Can be called only from attach() path
1449  *
1450  * This routine was initially implemented to enable SRQ. That's no longer needed
1451  * in hermon, and the code is conditionally compiled OUT, but left here because
1452  * there are other static configuration parameters we might one day want to set
1453  */
1454 #ifdef HERMON_NO_MOD_STAT_CFG
1455 int
1456 hermon_mod_stat_cfg_cmd_post(hermon_state_t *state)
1457 {
1458         hermon_mbox_info_t      mbox_info;
1459         hermon_cmd_post_t       cmd;
1460         hermon_hw_mod_stat_cfg_t        *mod;
1461         hermon_hw_msg_in_mod_t  inmod;
1462         uint64_t                data;
1463         uint_t                  size;
1464         int                     status, i;
1465 
1466         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1467 
1468         /*
1469          * "MOD_STAT_CFG" needs an INMBOX parameter, to specify what operations
1470          * to do.  However, at the point in time that we call this command, the
1471          * DDR has not yet been initialized, and all INMBOX'es are located in
1472          * DDR.  Because we want to call MOD_STAT_CFG before QUERY_DEVLIM is
1473          * called, and thus call it before DDR is setup, we simply use an
1474          * OUTMBOX memory location here as our INMBOX parameter.
1475          */
1476         mbox_info.mbi_alloc_flags = HERMON_ALLOC_OUTMBOX;
1477         status = hermon_mbox_alloc(state, &mbox_info, HERMON_NOSLEEP);
1478         if (status != HERMON_CMD_SUCCESS) {
1479                 return (status);
1480         }
1481 
1482         /*
1483          * Allocate on the heap our 'mod_stat_cfg' structure.  We want to
1484          * ideally move all of this on to the stack in the future, but this
1485          * works well for now.
1486          */
1487         mod = (hermon_hw_mod_stat_cfg_t *)kmem_zalloc(
1488             sizeof (hermon_hw_mod_stat_cfg_t), KM_SLEEP);
1489 
1490         /* Setup "MOD_STAT_CFG" settings */
1491         mod->srq_m   = 1;
1492         mod->srq     = state->hs_cfg_profile->cp_srq_enable;
1493 
1494         if (mod->srq) {
1495                 /*  use DEV_LIMS num srq */
1496                 mod->log_max_srq = state->hs_cfg_profile->cp_log_num_srq;
1497         } else {
1498                 mod->log_max_srq = 0;
1499         }
1500 
1501         /* Copy the "MOD_STAT_CFG" command into the "In" mailbox */
1502         size = sizeof (hermon_hw_mod_stat_cfg_t);
1503         for (i = 0; i < (size >> 3); i++) {
1504                 data = ((uint64_t *)mod)[i];
1505                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1506                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
1507         }
1508 
1509         /* Sync the mailbox for the device to read */
1510         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1511 
1512         /* Setup and post the Hermon "MOD_STAT_CFG" command */
1513         cmd.cp_inparm   = mbox_info.mbi_out->mb_mapaddr;
1514         cmd.cp_outparm  = 0;
1515         cmd.cp_inmod    = 0;
1516         cmd.cp_opcode   = MOD_STAT_CFG;
1517         cmd.cp_opmod    = HERMON_MOD_STAT_CFG_PTR;
1518         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1519         status = hermon_cmd_post(state, &cmd);
1520 
1521         /* Free "MOD_STAT_CFG" struct */
1522         kmem_free(mod, sizeof (hermon_hw_mod_stat_cfg_t));
1523 
1524         /* Free the mailbox */
1525         hermon_mbox_free(state, &mbox_info);
1526         return (status);
1527 }
1528 #endif
1529 
1530 
1531 /*
1532  * hermon_map_cmd_post()
1533  *    Context: Can be called only from user or kernel context
1534  *
1535  * Generic routine to map FW, ICMA, and ICM.
1536  */
1537 int
1538 hermon_map_cmd_post(hermon_state_t *state, hermon_dma_info_t *dma,
1539     uint16_t opcode, ddi_dma_cookie_t cookie, uint_t ccount)
1540 {
1541         hermon_mbox_info_t      mbox_info;
1542         hermon_cmd_post_t       cmd;
1543         hermon_hw_vpm_t         vpm;
1544         uint64_t                data;
1545         uint64_t                paddr, vaddr;
1546         uint_t                  size;
1547         int                     status, i, j, k = 0;
1548         int                     max_mailbox_size;
1549         int                     cookie_num_icm_pages;
1550         int                     num_vpm_entries;
1551         int                     log2_npages;
1552         int                     npages;
1553 
1554         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1555 
1556         /* Allocate an IN mailbox */
1557         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
1558         status = hermon_mbox_alloc(state, &mbox_info, HERMON_SLEEP);
1559         if (status != HERMON_CMD_SUCCESS) {
1560                 return (status);
1561         }
1562 
1563         /* Initialize cmd parameters */
1564         cmd.cp_outparm  = 0;
1565         cmd.cp_opcode   = opcode;
1566         cmd.cp_opmod    = 0;
1567         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1568 
1569         /*
1570          * Allocate a list of VPM (Virtual Physical Mapping) structures.
1571          * A VPM encodes a power-of-2 number of DMA pages that have been
1572          * allocated and are passed in the dma_info. We need to break up
1573          * the DMA cookies that are in the dma_info into power-of-2 page
1574          * mappings. We also need to keep track of the number of VPMs we
1575          * have total, as it is used as the inmod for this command.
1576          */
1577 
1578         /* Start with the ICM address passed and the first cookie */
1579         vaddr  = dma->icmaddr;
1580 
1581         /* Initialize the VPM count and the VPM struct */
1582         num_vpm_entries = 0;
1583         size = sizeof (hermon_hw_vpm_t);
1584         bzero(&vpm, size);
1585 
1586         /*
1587          * Establish a max mailbox size (in VPM entries). If we reach this,
1588          * we must post a MAP command, reinitialzie num_vpm_entries, and
1589          * continue.
1590          */
1591         max_mailbox_size = HERMON_MBOX_SIZE / size;
1592 
1593         /*
1594          * First, walk through the DMA cookies and build VPMs from them.
1595          */
1596         while (ccount-- > 0) {
1597 
1598                 /* Determine the number of ICM pages in this cookie. */
1599                 cookie_num_icm_pages = cookie.dmac_size / HERMON_PAGESIZE;
1600 
1601                 /* Initialize this set of VPM's starting physical address. */
1602                 paddr = cookie.dmac_laddress;
1603 
1604                 /*
1605                  * Now build a set of VPMs for this cookie's memory, breaking
1606                  * up the cookies into multiple VPMs if necessary to achieve
1607                  * the required power-of-2 number of pages per VPM. Once each
1608                  * VPM is constructed, write it out to the mailbox memory.
1609                  */
1610                 for (i = cookie_num_icm_pages; i > 0; i -= npages) {
1611                         log2_npages = highbit(i) - 1;
1612                         npages      = (1 << log2_npages);
1613                         /* Ensure this chunk is aligned on it's own size */
1614                         while (((npages * HERMON_PAGESIZE - 1) & paddr) != 0) {
1615                                 log2_npages--;
1616                                 npages = (1 << log2_npages);
1617                         }
1618                         vpm.log2sz    = log2_npages;
1619 
1620                         vpm.paddr_l = (uint32_t)(paddr >> 12);
1621                         vpm.paddr_h = (uint32_t)(paddr >> 32);
1622                         /* Increment the paddr for the next VPM */
1623                         paddr += npages * HERMON_PAGESIZE;
1624 
1625                         if (opcode == MAP_ICM) {
1626                                 vpm.vaddr_l = (uint32_t)(vaddr >> 12);
1627                                 vpm.vaddr_h = (uint32_t)(vaddr >> 32);
1628                                 /* Increment the ICM address for the next VPM */
1629                                 vaddr += npages * HERMON_PAGESIZE;
1630                         }
1631 
1632                         /*
1633                          * Copy this VPM into the "In" mailbox. Note we're
1634                          * using 'k' as the offset from mb_addr for this cmd.
1635                          */
1636                         for (j = 0; j < (size >> 3); j++, k++) {
1637                                 data = ((uint64_t *)(void *)&vpm)[j];
1638                                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
1639                                     ((uint64_t *)mbox_info.mbi_in->mb_addr + k),
1640                                     data);
1641                         }
1642 
1643                         /*
1644                          * Increment the number of VPM entries and check
1645                          * against max mailbox size. If we have reached
1646                          * the maximum mailbox size, post the map cmd.
1647                          */
1648                         if (++num_vpm_entries == max_mailbox_size) {
1649 
1650                                 /* Sync the mailbox for the device to read */
1651                                 hermon_mbox_sync(mbox_info.mbi_in, 0, (size *
1652                                     num_vpm_entries), DDI_DMA_SYNC_FORDEV);
1653 
1654                                 /* Setup and post the command */
1655                                 cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1656                                 cmd.cp_inmod    = num_vpm_entries;
1657                                 status = hermon_cmd_post(state, &cmd);
1658                                 if (status != HERMON_CMD_SUCCESS) {
1659                                         cmn_err(CE_NOTE, "hermon%d: %s cmd "
1660                                             "failed (0x%x)", state->hs_instance,
1661                                             opcode == MAP_FA ? "MAP_FA" :
1662                                             opcode == MAP_ICM ? "MAP_ICM" :
1663                                             opcode == MAP_ICM_AUX ? "MAP_ICMA" :
1664                                             "UNKNOWN", status);
1665                                         goto map_fail;
1666                                 }
1667 
1668                                 /*
1669                                  * Reinitialize num_vpm_entries, and the
1670                                  * mb_addr offset
1671                                  */
1672                                 num_vpm_entries = k = 0;
1673                         }
1674                 }
1675 
1676                 /* If count remains, move onto the next cookie */
1677                 if (ccount != 0) {
1678                         ddi_dma_nextcookie(dma->dma_hdl, &cookie);
1679                 }
1680         }
1681 
1682         if (num_vpm_entries) {
1683 
1684                 /* Sync the mailbox for the device to read */
1685                 hermon_mbox_sync(mbox_info.mbi_in, 0, (size * num_vpm_entries),
1686                     DDI_DMA_SYNC_FORDEV);
1687 
1688                 /* Setup and post the command */
1689                 cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1690                 cmd.cp_inmod    = num_vpm_entries;
1691                 status = hermon_cmd_post(state, &cmd);
1692                 if (status != HERMON_CMD_SUCCESS) {
1693                         cmn_err(CE_NOTE, "hermon%d: %s cmd "
1694                             "failed (0x%x)", state->hs_instance,
1695                             opcode == MAP_FA ? "MAP_FA" :
1696                             opcode == MAP_ICM ? "MAP_ICM" :
1697                             opcode == MAP_ICM_AUX ? "MAP_ICMA" :
1698                             "UNKNOWN", status);
1699                         goto map_fail;
1700                 }
1701         }
1702 
1703 map_fail:
1704         /* Free the mailbox */
1705         hermon_mbox_free(state, &mbox_info);
1706         return (status);
1707 }
1708 
1709 
1710 /*
1711  * hermon_unmap_fa_cmd_post()
1712  *    Context: Can be called only from attach() path
1713  */
1714 int
1715 hermon_unmap_fa_cmd_post(hermon_state_t *state)
1716 {
1717         hermon_cmd_post_t       cmd;
1718         int                     status;
1719 
1720         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1721 
1722         /* Setup and post the Hermon "UNMAP_FA" command */
1723         cmd.cp_inparm   = 0;
1724         cmd.cp_outparm  = 0;
1725         cmd.cp_inmod    = 0;
1726         cmd.cp_opcode   = UNMAP_FA;
1727         cmd.cp_opmod    = 0;
1728         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1729         status = hermon_cmd_post(state, &cmd);
1730 
1731         return (status);
1732 }
1733 
1734 
1735 /*
1736  * hermon_run_fw_cmd_post()
1737  *    Context: Can be called only from attach() path
1738  */
1739 int
1740 hermon_run_fw_cmd_post(hermon_state_t *state)
1741 {
1742         hermon_cmd_post_t       cmd;
1743         int                     status;
1744 
1745         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1746 
1747         /* Setup and post the Hermon "RUN_FW" command */
1748         cmd.cp_inparm   = 0;
1749         cmd.cp_outparm  = 0;
1750         cmd.cp_inmod    = 0;
1751         cmd.cp_opcode   = RUN_FW;
1752         cmd.cp_opmod    = 0;
1753         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1754 
1755         status = hermon_cmd_post(state, &cmd);
1756 #ifdef FMA_TEST
1757         if (hermon_test_num == -2) {
1758                 status = HERMON_CMD_BAD_NVMEM;
1759                 /*
1760                  * No need of an ereport here since this case
1761                  * is treated as a degradation later.
1762                  */
1763                 HERMON_FMANOTE(state, HERMON_FMA_BADNVMEM);
1764         }
1765 #endif
1766         return (status);
1767 }
1768 
1769 
1770 /*
1771  * hermon_set_icm_size_cmd_post()
1772  *    Context: Can be called only from attach() path
1773  */
1774 int
1775 hermon_set_icm_size_cmd_post(hermon_state_t *state)
1776 {
1777         hermon_cmd_post_t       cmd;
1778         int                     status;
1779 
1780         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1781 
1782         /* Setup and post the Hermon "SET_ICM_SIZE" command */
1783         cmd.cp_inparm   = (uint64_t)state->hs_icm_sz;
1784         cmd.cp_outparm  = 0;
1785         cmd.cp_inmod    = 0;
1786         cmd.cp_opcode   = SET_ICM_SIZE;
1787         cmd.cp_opmod    = 0;
1788         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1789         status = hermon_cmd_post(state, &cmd);
1790 
1791         /*
1792          * Aux ICM size in 4K pages returned in output param
1793          * convert it to bytes
1794          */
1795         state->hs_icma_sz = (uint64_t)(cmd.cp_outparm << HERMON_PAGESHIFT);
1796         return (status);
1797 }
1798 
1799 
1800 /*
1801  * hermon_unmap_icm_aux_cmd_post()
1802  *    Context: Can be called only from attach() path
1803  */
1804 int
1805 hermon_unmap_icm_aux_cmd_post(hermon_state_t *state)
1806 {
1807         hermon_cmd_post_t       cmd;
1808         int                     status;
1809 
1810         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1811 
1812         /* Setup and post the Hermon "UNMAP_ICM_AUX" command */
1813         cmd.cp_inparm   = 0;
1814         cmd.cp_outparm  = 0;
1815         cmd.cp_inmod    = 0;
1816         cmd.cp_opcode   = UNMAP_ICM_AUX;
1817         cmd.cp_opmod    = 0;
1818         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1819         status = hermon_cmd_post(state, &cmd);
1820         return (status);
1821 }
1822 
1823 
1824 /*
1825  * hermon_unmap_icm_cmd_post()
1826  *    Context: Can be called from base or attach context
1827  */
1828 int
1829 hermon_unmap_icm_cmd_post(hermon_state_t *state, hermon_dma_info_t *dma_info)
1830 {
1831         hermon_cmd_post_t       cmd;
1832         uint64_t                addr;
1833         uint32_t                npages;
1834         int                     status;
1835 
1836         /*
1837          * Setup and post the Hermon "UNMAP_ICM" command. If a
1838          * hermon_dma_info_t was passed, we want to unmap a set
1839          * of pages. Otherwise, unmap all of ICM.
1840          */
1841         if (dma_info != NULL) {
1842                 addr   = dma_info->icmaddr;
1843                 npages = dma_info->length / HERMON_PAGESIZE;
1844         } else {
1845                 addr   = 0;
1846                 npages = state->hs_icm_sz / HERMON_PAGESIZE;
1847         }
1848 
1849         /* Setup and post the Hermon "UNMAP_ICM" command */
1850         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1851         cmd.cp_inparm   = addr;
1852         cmd.cp_outparm  = 0;
1853         cmd.cp_inmod    = npages;
1854         cmd.cp_opcode   = UNMAP_ICM;
1855         cmd.cp_opmod    = 0;
1856         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
1857         status = hermon_cmd_post(state, &cmd);
1858         return (status);
1859 }
1860 
1861 
1862 /*
1863  * hermon_mad_ifc_cmd_post()
1864  *    Context: Can be called from interrupt or base context.
1865  */
1866 int
1867 hermon_mad_ifc_cmd_post(hermon_state_t *state, uint_t port,
1868     uint_t sleepflag, uint32_t *mad, uint32_t *resp)
1869 {
1870         hermon_mbox_info_t      mbox_info;
1871         hermon_cmd_post_t       cmd;
1872         uint_t                  size;
1873         int                     status;
1874 
1875         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1876 
1877         /* Get "In" and "Out" mailboxes for the command */
1878         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
1879         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
1880         if (status != HERMON_CMD_SUCCESS) {
1881                 return (status);
1882         }
1883 
1884         /* Copy the request MAD into the "In" mailbox */
1885         size = HERMON_CMD_MAD_IFC_SIZE;
1886         bcopy(mad, mbox_info.mbi_in->mb_addr, size);
1887 
1888         /* Sync the mailbox for the device to read */
1889         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1890 
1891         /* Setup the Hermon "MAD_IFC" command */
1892         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1893         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
1894         cmd.cp_inmod    = port;
1895         cmd.cp_opcode   = MAD_IFC;
1896         cmd.cp_opmod    = HERMON_CMD_MKEY_CHECK;  /* Enable MKey checking */
1897         cmd.cp_flags    = sleepflag;
1898         status = hermon_cmd_post(state, &cmd);
1899         if (status != HERMON_CMD_SUCCESS) {
1900                 goto mad_ifc_fail;
1901         }
1902 
1903         /* Sync the mailbox to read the results */
1904         hermon_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
1905 
1906         /* Copy the response MAD into "resp" */
1907         bcopy(mbox_info.mbi_out->mb_addr, resp, size);
1908 
1909 mad_ifc_fail:
1910         /* Free the mailbox */
1911         hermon_mbox_free(state, &mbox_info);
1912         return (status);
1913 }
1914 
1915 
1916 /*
1917  * hermon_getportinfo_cmd_post()
1918  *    Context: Can be called from interrupt or base context.
1919  */
1920 int
1921 hermon_getportinfo_cmd_post(hermon_state_t *state, uint_t port,
1922     uint_t sleepflag, sm_portinfo_t *portinfo)
1923 {
1924         hermon_mbox_info_t      mbox_info;
1925         hermon_cmd_post_t       cmd;
1926         uint32_t                *mbox;
1927         uint_t                  size;
1928         int                     status, i;
1929 
1930         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
1931 
1932         /* Get "In" and "Out" mailboxes for the command */
1933         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
1934         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
1935         if (status != HERMON_CMD_SUCCESS) {
1936                 return (status);
1937         }
1938 
1939         /* Build the GetPortInfo request MAD in the "In" mailbox */
1940         size = HERMON_CMD_MAD_IFC_SIZE;
1941         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
1942         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_MADHDR0);
1943         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
1944         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
1945         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
1946         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_PORTINFO);
1947         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], port);
1948         for (i = 6; i < (size >> 2); i++) {
1949                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
1950         }
1951 
1952         /* Sync the mailbox for the device to read */
1953         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
1954 
1955         /* Setup the Hermon "MAD_IFC" command */
1956         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
1957         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
1958         cmd.cp_inmod    = port;
1959         cmd.cp_opcode   = MAD_IFC;
1960         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK;  /* No MKey checking */
1961         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN; /* NO SLEEP */
1962         status = hermon_cmd_post(state, &cmd);
1963         if (status != HERMON_CMD_SUCCESS) {
1964                 goto getportinfo_fail;
1965         }
1966 
1967         /* Sync the mailbox to read the results */
1968         size = sizeof (sm_portinfo_t);
1969         hermon_mbox_sync(mbox_info.mbi_out, HERMON_CMD_MADDATA_OFFSET,
1970             size, DDI_DMA_SYNC_FORCPU);
1971 
1972         /*
1973          * Copy GetPortInfo response MAD into "portinfo".  Do any endian
1974          * swapping that may be necessary to flip any of the "portinfo"
1975          * fields
1976          */
1977         bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
1978             HERMON_CMD_MADDATA_OFFSET), portinfo, size);
1979         HERMON_GETPORTINFO_SWAP(portinfo);
1980 
1981 getportinfo_fail:
1982         /* Free the mailbox */
1983         hermon_mbox_free(state, &mbox_info);
1984         return (status);
1985 }
1986 
1987 /*
1988  * hermon_is_ext_port_counters_supported()
1989  *
1990  * Determine weather extended port counters are supported or not by sending
1991  * ClassPortInfo perf mgmt class MAD.
1992  */
1993 int
1994 hermon_is_ext_port_counters_supported(hermon_state_t *state, uint_t port,
1995     uint_t sleepflag, int *ext_width_supported)
1996 {
1997         hermon_mbox_info_t      mbox_info;
1998         hermon_cmd_post_t       cmd;
1999         uint64_t                data;
2000         uint32_t                *mbox;
2001         int                     status;
2002 
2003         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2004 
2005         /* Get "In" and "Out" mailboxes for the command */
2006         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2007         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2008         if (status != HERMON_CMD_SUCCESS) {
2009                 return (status);
2010         }
2011 
2012         /* Build the ClassPortInfo request MAD in the "In" mailbox */
2013         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2014 
2015         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_PERF_GET);
2016         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2017         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2018         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2019         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4],
2020             HERMON_CMD_CLASSPORTINFO);
2021         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], HERMON_CMD_PERFATTR);
2022 
2023         /* Sync the mailbox for the device to read */
2024         hermon_mbox_sync(mbox_info.mbi_in, 0, HERMON_CMD_MAD_IFC_SIZE,
2025             DDI_DMA_SYNC_FORDEV);
2026 
2027         /* Setup the Hermon "MAD_IFC" command */
2028         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2029         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2030         cmd.cp_inmod    = port;
2031         cmd.cp_opcode   = MAD_IFC;
2032         /* No MKey and BKey checking */
2033         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK | HERMON_CMD_BKEY_DONTCHECK;
2034         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN; /* NO SLEEP */
2035         status = hermon_cmd_post(state, &cmd);
2036         if (status != HERMON_CMD_SUCCESS) {
2037                 goto fail;
2038         }
2039 
2040         /* Sync the mailbox to read the results */
2041         hermon_mbox_sync(mbox_info.mbi_out, 0, HERMON_CMD_MAD_IFC_SIZE,
2042             DDI_DMA_SYNC_FORCPU);
2043 
2044         /*
2045          * We can discard the MAD header and the reserved area of the
2046          * perf mgmt class MAD
2047          */
2048         data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2049             ((uint64_t *)mbox_info.mbi_out->mb_addr + 8));
2050         *ext_width_supported = (data & (HERMON_IS_EXT_WIDTH_SUPPORTED |
2051             HERMON_IS_EXT_WIDTH_SUPPORTED_NOIETF)) ? 1 : 0;
2052 
2053 fail:
2054         /* Free the mailbox */
2055         hermon_mbox_free(state, &mbox_info);
2056         return (status);
2057 }
2058 
2059 /*
2060  * hermon_getextpefcntr_cmd_post()
2061  *
2062  * Read the extended performance counters of the specified port and
2063  * copy them into perfinfo.
2064  */
2065 int
2066 hermon_getextperfcntr_cmd_post(hermon_state_t *state, uint_t port,
2067     uint_t sleepflag, hermon_hw_sm_extperfcntr_t *perfinfo)
2068 {
2069         hermon_mbox_info_t      mbox_info;
2070         hermon_cmd_post_t       cmd;
2071         uint64_t                data;
2072         uint32_t                *mbox;
2073         int                     status, i;
2074 
2075         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2076 
2077         /* Get "In" and "Out" mailboxes for the command */
2078         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2079         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2080         if (status != HERMON_CMD_SUCCESS) {
2081                 return (status);
2082         }
2083 
2084         /* Build PortCountersExtended request MAD in the "In" mailbox */
2085         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2086 
2087         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_PERF_GET);
2088         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2089         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2090         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2091         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4],
2092             HERMON_CMD_EXTPERFCNTRS);
2093         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], HERMON_CMD_PERFATTR);
2094         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16], (port << 16));
2095 
2096         /* Sync the mailbox for the device to read */
2097         hermon_mbox_sync(mbox_info.mbi_in, 0, HERMON_CMD_MAD_IFC_SIZE,
2098             DDI_DMA_SYNC_FORDEV);
2099 
2100         /* Setup the Hermon "MAD_IFC" command */
2101         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2102         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2103         cmd.cp_inmod    = port;
2104         cmd.cp_opcode   = MAD_IFC;
2105         /* No MKey and BKey checking */
2106         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK | HERMON_CMD_BKEY_DONTCHECK;
2107         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN; /* NO SLEEP */
2108         status = hermon_cmd_post(state, &cmd);
2109         if (status != HERMON_CMD_SUCCESS) {
2110                 goto fail;
2111         }
2112 
2113         /* Sync the mailbox to read the results */
2114         hermon_mbox_sync(mbox_info.mbi_out, 0, HERMON_CMD_MAD_IFC_SIZE,
2115             DDI_DMA_SYNC_FORCPU);
2116 
2117         /*
2118          * Copy Perfcounters into "perfinfo". We can discard the MAD
2119          * header and the reserved area of the perf mgmt class MAD.
2120          */
2121         for (i = 0; i < (sizeof (hermon_hw_sm_extperfcntr_t) >> 3); i++) {
2122                 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2123                     ((uint64_t *)mbox_info.mbi_out->mb_addr + i + 8));
2124                 ((uint64_t *)(void *)perfinfo)[i] = data;
2125         }
2126 
2127 fail:
2128         /* Free the mailbox */
2129         hermon_mbox_free(state, &mbox_info);
2130         return (status);
2131 }
2132 
2133 /*
2134  * hermon_getpefcntr_cmd_post()
2135  *    Context: Can be called from interrupt or base context.
2136  *
2137  * If reset is zero, read the performance counters of the specified port and
2138  * copy them into perfinfo.
2139  * If reset is non-zero reset the performance counters of the specified port.
2140  */
2141 int
2142 hermon_getperfcntr_cmd_post(hermon_state_t *state, uint_t port,
2143     uint_t sleepflag, hermon_hw_sm_perfcntr_t *perfinfo, int reset)
2144 {
2145         hermon_mbox_info_t      mbox_info;
2146         hermon_cmd_post_t       cmd;
2147         uint64_t                data;
2148         uint32_t                *mbox;
2149         uint_t                  size;
2150         int                     status, i;
2151 
2152         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2153 
2154         /* Get "In" and "Out" mailboxes for the command */
2155         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2156         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2157         if (status != HERMON_CMD_SUCCESS) {
2158                 return (status);
2159         }
2160 
2161         /* Build the GetPortInfo request MAD in the "In" mailbox */
2162         size = HERMON_CMD_MAD_IFC_SIZE;
2163         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2164 
2165         if (reset) {
2166                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
2167                     HERMON_CMD_PERF_SET);
2168         } else {
2169                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
2170                     HERMON_CMD_PERF_GET);
2171         }
2172         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2173         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2174         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2175         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_PERFCNTRS);
2176         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], HERMON_CMD_PERFATTR);
2177 
2178         if (reset) {
2179                 /* reset counters for XmitData, RcvData, XmitPkts, RcvPkts */
2180                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16],
2181                     ((port << 16) | 0xf000));
2182 
2183                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[22], 0);
2184                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[23], 0);
2185                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[24], 0);
2186                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[25], 0);
2187         } else
2188                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16], (port << 16));
2189 
2190         /* Sync the mailbox for the device to read */
2191         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2192 
2193         /* Setup the Hermon "MAD_IFC" command */
2194         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2195         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2196         cmd.cp_inmod    = port;
2197         cmd.cp_opcode   = MAD_IFC;
2198         /* No MKey and BKey checking */
2199         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK | HERMON_CMD_BKEY_DONTCHECK;
2200         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN; /* NO SLEEP */
2201         status = hermon_cmd_post(state, &cmd);
2202         if (status != HERMON_CMD_SUCCESS) {
2203                 goto getperfinfo_fail;
2204         }
2205 
2206         /* Sync the mailbox to read the results */
2207         size = HERMON_CMD_MAD_IFC_SIZE;
2208         hermon_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
2209 
2210         if (reset == 0) {
2211                 size = sizeof (hermon_hw_sm_perfcntr_t); /* for the copy */
2212                 /*
2213                  * Copy Perfcounters into "perfinfo".  We can discard the MAD
2214                  * header and the 8 Quadword reserved area of the PERM mgmt
2215                  * class MAD
2216                  */
2217 
2218                 for (i = 0; i < size >> 3; i++) {
2219                         data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2220                             ((uint64_t *)mbox_info.mbi_out->mb_addr + i + 8));
2221                         ((uint64_t *)(void *)perfinfo)[i] = data;
2222                 }
2223         }
2224 
2225 getperfinfo_fail:
2226         /* Free the mailbox */
2227         hermon_mbox_free(state, &mbox_info);
2228         return (status);
2229 }
2230 
2231 
2232 
2233 /*
2234  * hermon_getnodeinfo_cmd_post()
2235  *    Context: Can be called from interrupt or base context.
2236  *    (Currently called only from attach() and detach() path contexts)
2237  */
2238 int
2239 hermon_getnodeinfo_cmd_post(hermon_state_t *state, uint_t sleepflag,
2240     sm_nodeinfo_t *nodeinfo)
2241 {
2242         hermon_mbox_info_t      mbox_info;
2243         hermon_cmd_post_t       cmd;
2244         uint32_t                *mbox;
2245         uint_t                  size;
2246         int                     status, i;
2247 
2248         /* Make sure we are called with the correct flag */
2249         ASSERT(sleepflag == HERMON_CMD_NOSLEEP_SPIN);
2250 
2251         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2252 
2253         /* Get "In" and "Out" mailboxes for the command */
2254         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2255         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2256         if (status != HERMON_CMD_SUCCESS) {
2257                 return (status);
2258         }
2259 
2260         /* Build the GetNodeInfo request MAD into the "In" mailbox */
2261         size = HERMON_CMD_MAD_IFC_SIZE;
2262         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2263         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_MADHDR0);
2264         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2265         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2266         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2267         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_NODEINFO);
2268         for (i = 5; i < (size >> 2); i++) {
2269                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
2270         }
2271 
2272         /* Sync the mailbox for the device to read */
2273         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2274 
2275         /* Setup the Hermon "MAD_IFC" command */
2276         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2277         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2278         cmd.cp_inmod    = 1;  /* Get NodeInfo from port #1 */
2279         cmd.cp_opcode   = MAD_IFC;
2280         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK;  /* No MKey checking */
2281         cmd.cp_flags    = sleepflag;
2282         status = hermon_cmd_post(state, &cmd);
2283         if (status != HERMON_CMD_SUCCESS) {
2284                 goto getnodeinfo_fail;
2285         }
2286 
2287         /* Sync the mailbox to read the results */
2288         size = sizeof (sm_nodeinfo_t);
2289         hermon_mbox_sync(mbox_info.mbi_out, HERMON_CMD_MADDATA_OFFSET,
2290             size, DDI_DMA_SYNC_FORCPU);
2291 
2292         /*
2293          * Copy GetNodeInfo response MAD into "nodeinfo".  Do any endian
2294          * swapping that may be necessary to flip any of the "nodeinfo"
2295          * fields
2296          */
2297         bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
2298             HERMON_CMD_MADDATA_OFFSET), nodeinfo, size);
2299         HERMON_GETNODEINFO_SWAP(nodeinfo);
2300 
2301 getnodeinfo_fail:
2302         /* Free the mailbox */
2303         hermon_mbox_free(state, &mbox_info);
2304         return (status);
2305 }
2306 
2307 
2308 /*
2309  * hermon_getnodedesc_cmd_post()
2310  *    Context: Can be called from interrupt or base context.
2311  *    (Currently called only from attach() and detach() path contexts)
2312  */
2313 int
2314 hermon_getnodedesc_cmd_post(hermon_state_t *state, uint_t sleepflag,
2315     sm_nodedesc_t *nodedesc)
2316 {
2317         hermon_mbox_info_t      mbox_info;
2318         hermon_cmd_post_t       cmd;
2319         uint32_t                *mbox;
2320         uint_t                  size;
2321         int                     status, i;
2322 
2323         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2324 
2325         /* Get "In" and "Out" mailboxes for the command */
2326         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2327         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2328         if (status != HERMON_CMD_SUCCESS) {
2329                 return (status);
2330         }
2331 
2332         /* Build the GetNodeDesc request MAD into the "In" mailbox */
2333         size = HERMON_CMD_MAD_IFC_SIZE;
2334         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2335         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_MADHDR0);
2336         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2337         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2338         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2339         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_NODEDESC);
2340         for (i = 5; i < (size >> 2); i++) {
2341                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
2342         }
2343 
2344         /* Sync the mailbox for the device to read */
2345         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2346 
2347         /* Setup the Hermon "MAD_IFC" command */
2348         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2349         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2350         cmd.cp_inmod    = 1;  /* Get NodeDesc from port #1 */
2351         cmd.cp_opcode   = MAD_IFC;
2352         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK;  /* No MKey checking */
2353         cmd.cp_flags    = sleepflag;
2354         status = hermon_cmd_post(state, &cmd);
2355         if (status != HERMON_CMD_SUCCESS) {
2356                 goto getnodedesc_fail;
2357         }
2358 
2359         /* Sync the mailbox to read the results */
2360         size = sizeof (sm_nodedesc_t);
2361         hermon_mbox_sync(mbox_info.mbi_out, HERMON_CMD_MADDATA_OFFSET,
2362             size, DDI_DMA_SYNC_FORCPU);
2363 
2364         /* Copy GetNodeDesc response MAD into "nodedesc" */
2365         bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
2366             HERMON_CMD_MADDATA_OFFSET), nodedesc, size);
2367 
2368 getnodedesc_fail:
2369         /* Free the mailbox */
2370         hermon_mbox_free(state, &mbox_info);
2371         return (status);
2372 }
2373 
2374 
2375 /*
2376  * hermon_getguidinfo_cmd_post()
2377  *    Context: Can be called from interrupt or base context.
2378  */
2379 int
2380 hermon_getguidinfo_cmd_post(hermon_state_t *state, uint_t port,
2381     uint_t guidblock, uint_t sleepflag, sm_guidinfo_t *guidinfo)
2382 {
2383         hermon_mbox_info_t      mbox_info;
2384         hermon_cmd_post_t       cmd;
2385         uint32_t                *mbox;
2386         uint_t                  size;
2387         int                     status, i;
2388 
2389         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2390 
2391         /* Get "In" and "Out" mailboxes for the command */
2392         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2393         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2394         if (status != HERMON_CMD_SUCCESS) {
2395                 return (status);
2396         }
2397 
2398         /* Build the GetGUIDInfo request MAD into the "In" mailbox */
2399         size = HERMON_CMD_MAD_IFC_SIZE;
2400         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2401         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_MADHDR0);
2402         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2403         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2404         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2405         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_GUIDINFO);
2406         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], guidblock);
2407         for (i = 6; i < (size >> 2); i++) {
2408                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
2409         }
2410 
2411         /* Sync the mailbox for the device to read */
2412         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2413 
2414         /* Setup the Hermon "MAD_IFC" command */
2415         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2416         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2417         cmd.cp_inmod    = port;
2418         cmd.cp_opcode   = MAD_IFC;
2419         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK;  /* No MKey checking */
2420         cmd.cp_flags    = sleepflag;
2421         status = hermon_cmd_post(state, &cmd);
2422         if (status != HERMON_CMD_SUCCESS) {
2423                 goto getguidinfo_fail;
2424         }
2425 
2426         /* Sync the mailbox to read the results */
2427         size = sizeof (sm_guidinfo_t);
2428         hermon_mbox_sync(mbox_info.mbi_out, HERMON_CMD_MADDATA_OFFSET,
2429             size, DDI_DMA_SYNC_FORCPU);
2430 
2431         /*
2432          * Copy GetGUIDInfo response MAD into "guidinfo".  Do any endian
2433          * swapping that may be necessary to flip the "guidinfo" fields
2434          */
2435         bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
2436             HERMON_CMD_MADDATA_OFFSET), guidinfo, size);
2437         HERMON_GETGUIDINFO_SWAP(guidinfo);
2438 
2439 getguidinfo_fail:
2440         /* Free the mailbox */
2441         hermon_mbox_free(state, &mbox_info);
2442         return (status);
2443 }
2444 
2445 
2446 /*
2447  * hermon_getpkeytable_cmd_post()
2448  *    Context: Can be called from interrupt or base context.
2449  */
2450 int
2451 hermon_getpkeytable_cmd_post(hermon_state_t *state, uint_t port,
2452     uint_t pkeyblock, uint_t sleepflag, sm_pkey_table_t *pkeytable)
2453 {
2454         hermon_mbox_info_t      mbox_info;
2455         hermon_cmd_post_t       cmd;
2456         uint32_t                *mbox;
2457         uint_t                  size;
2458         int                     status, i;
2459 
2460         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2461 
2462         /* Get "In" and "Out" mailboxes for the command */
2463         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX | HERMON_ALLOC_OUTMBOX;
2464         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2465         if (status != HERMON_CMD_SUCCESS) {
2466                 return (status);
2467         }
2468 
2469         /* Build the GetPkeyTable request MAD into the "In" mailbox */
2470         size = HERMON_CMD_MAD_IFC_SIZE;
2471         mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
2472         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], HERMON_CMD_MADHDR0);
2473         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], HERMON_CMD_MADHDR1);
2474         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], HERMON_CMD_MADHDR2);
2475         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], HERMON_CMD_MADHDR3);
2476         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], HERMON_CMD_PKEYTBLE);
2477         ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], pkeyblock);
2478         for (i = 6; i < (size >> 2); i++) {
2479                 ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
2480         }
2481 
2482         /* Sync the mailbox for the device to read */
2483         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2484 
2485         /* Setup the Hermon "MAD_IFC" command */
2486         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2487         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2488         cmd.cp_inmod    = port;
2489         cmd.cp_opcode   = MAD_IFC;
2490         cmd.cp_opmod    = HERMON_CMD_MKEY_DONTCHECK;  /* No MKey checking */
2491         cmd.cp_flags    = sleepflag;
2492         status = hermon_cmd_post(state, &cmd);
2493         if (status != HERMON_CMD_SUCCESS) {
2494                 goto getpkeytable_fail;
2495         }
2496 
2497         /* Sync the mailbox to read the results */
2498         size = sizeof (sm_pkey_table_t);
2499         hermon_mbox_sync(mbox_info.mbi_out, HERMON_CMD_MADDATA_OFFSET,
2500             size, DDI_DMA_SYNC_FORCPU);
2501 
2502         /*
2503          * Copy GetPKeyTable response MAD into "pkeytable".  Do any endian
2504          * swapping that may be necessary to flip the "pkeytable" fields
2505          */
2506         bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
2507             HERMON_CMD_MADDATA_OFFSET), pkeytable, size);
2508         HERMON_GETPKEYTABLE_SWAP(pkeytable);
2509 
2510 getpkeytable_fail:
2511         /* Free the mailbox */
2512         hermon_mbox_free(state, &mbox_info);
2513         return (status);
2514 }
2515 
2516 
2517 /*
2518  * hermon_write_mtt_cmd_post()
2519  *    Context: Can be called from interrupt or base context.
2520  */
2521 int
2522 hermon_write_mtt_cmd_post(hermon_state_t *state, hermon_rsrc_t *mtt,
2523     uint64_t start_addr, uint_t nummtt, uint_t sleepflag)
2524 {
2525         hermon_mbox_info_t      mbox_info;
2526         hermon_cmd_post_t       cmd;
2527         uint64_t                data;
2528         uint_t                  size;
2529         int                     status;
2530         int                     i;
2531 
2532         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2533 
2534         /* Get an "In" mailbox for the command */
2535         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
2536         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2537         if (status != HERMON_CMD_SUCCESS) {
2538                 return (status);
2539         }
2540 
2541         /*
2542          * The WRITE_MTT command input parameter contains the 64-bit addr of
2543          * the first target MTT, followed by 64 bits reserved, followed by an
2544          * array of MTT entries.
2545          *
2546          */
2547         ddi_put64(mbox_info.mbi_in->mb_acchdl,
2548             ((uint64_t *)mbox_info.mbi_in->mb_addr),
2549             start_addr);
2550 
2551         ddi_put64(mbox_info.mbi_in->mb_acchdl,
2552             ((uint64_t *)mbox_info.mbi_in->mb_addr + 1), 0x0);
2553 
2554         for (i = 0; i < nummtt; i++) {
2555                 data = ((uint64_t *)mtt->hr_addr)[i];
2556                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2557                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i + 2), data);
2558         }
2559 
2560         /* Sync the mailbox for the device to read */
2561         size = (nummtt << HERMON_MTT_SIZE_SHIFT) + HERMON_CMD_WRITEMTT_RSVD_SZ;
2562         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2563 
2564         /* Setup and post Hermon "WRITE_MTT" command */
2565         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2566         cmd.cp_outparm  = 0;
2567         cmd.cp_inmod    = nummtt;
2568         cmd.cp_opcode   = WRITE_MTT;
2569         cmd.cp_opmod    = 0;
2570         cmd.cp_flags    = sleepflag;
2571         status = hermon_cmd_post(state, &cmd);
2572         if (status != HERMON_CMD_SUCCESS) {
2573                 cmn_err(CE_CONT, "WRITE_MTT failed (0x%x)\n", status);
2574         }
2575 
2576         /* Free the mailbox */
2577         hermon_mbox_free(state, &mbox_info);
2578         return (status);
2579 }
2580 
2581 
2582 /*
2583  * hermon_sync_tpt_cmd_post()
2584  *    Context: Can be called from interrupt or base context.
2585  */
2586 int
2587 hermon_sync_tpt_cmd_post(hermon_state_t *state, uint_t sleepflag)
2588 {
2589         hermon_cmd_post_t       cmd;
2590         int                     status;
2591 
2592         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2593 
2594         /* Setup and post the Hermon "SYNC_TPT" command */
2595         cmd.cp_inparm   = 0;
2596         cmd.cp_outparm  = 0;
2597         cmd.cp_inmod    = 0;
2598         cmd.cp_opcode   = SYNC_TPT;
2599         cmd.cp_opmod    = 0;
2600         cmd.cp_flags    = sleepflag;
2601         status = hermon_cmd_post(state, &cmd);
2602 
2603         return (status);
2604 }
2605 
2606 /*
2607  * hermon_map_eq_cmd_post()
2608  *    Context: Can be called from interrupt or base context.
2609  *    (Currently called only from attach() and/or detach() path contexts)
2610  */
2611 int
2612 hermon_map_eq_cmd_post(hermon_state_t *state, uint_t map, uint_t eqcindx,
2613     uint64_t eqmapmask, uint_t sleepflag)
2614 {
2615         hermon_cmd_post_t       cmd;
2616         int                     status;
2617 
2618         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2619 
2620         /* Setup and post Hermon "MAP_EQ" command */
2621         cmd.cp_inparm   = eqmapmask;
2622         cmd.cp_outparm  = 0;
2623         cmd.cp_inmod    = eqcindx;
2624         if (map != HERMON_CMD_MAP_EQ_EVT_MAP) {
2625                 cmd.cp_inmod |= HERMON_CMD_UNMAP_EQ_MASK;
2626         }
2627         cmd.cp_opcode   = MAP_EQ;
2628         cmd.cp_opmod    = 0;
2629         cmd.cp_flags    = sleepflag;
2630         status = hermon_cmd_post(state, &cmd);
2631         return (status);
2632 }
2633 
2634 
2635 /*
2636  * hermon_resize_cq_cmd_post()
2637  *    Context: Can be called from interrupt or base context.
2638  */
2639 int
2640 hermon_resize_cq_cmd_post(hermon_state_t *state, hermon_hw_cqc_t *cqc,
2641     uint_t cqcindx, uint32_t *prod_indx, uint_t sleepflag)
2642 {
2643         hermon_mbox_info_t      mbox_info;
2644         hermon_cmd_post_t       cmd;
2645         uint64_t                data;
2646         uint_t                  size;
2647         int                     status, i;
2648 
2649         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2650 
2651         /* Get an "In" mailbox for the command */
2652         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
2653         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2654         if (status != HERMON_CMD_SUCCESS) {
2655                 return (status);
2656         }
2657 
2658         /* Copy the Hermon "MODIFY_CQ" command into mailbox */
2659         size = sizeof (hermon_hw_cqc_t);
2660         for (i = 0; i < (size >> 3); i++) {
2661                 data = ((uint64_t *)(void *)cqc)[i];
2662                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2663                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
2664         }
2665 
2666         /* Sync the mailbox for the device to read */
2667         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2668 
2669         /* Setup and post Hermon "MODIFY_CQ" command */
2670         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2671         cmd.cp_outparm  = 0;    /* resize cq */
2672         cmd.cp_inmod    = cqcindx;
2673         cmd.cp_opcode   = MODIFY_CQ;
2674         cmd.cp_opmod    = RESIZE_CQ;
2675         cmd.cp_flags    = sleepflag;
2676         status = hermon_cmd_post(state, &cmd);
2677 
2678         /*
2679          * New "producer index" is returned in the upper 32 bits of
2680          * command "outparam"
2681          */
2682         *prod_indx = (cmd.cp_outparm >> 32);
2683 
2684         /* Free the mailbox */
2685         hermon_mbox_free(state, &mbox_info);
2686         return (status);
2687 }
2688 
2689 
2690 /*
2691  * hermon_modify_cq_cmd_post()
2692  *    Context: Can be called from interrupt or base context.
2693  */
2694 int
2695 hermon_modify_cq_cmd_post(hermon_state_t *state, hermon_hw_cqc_t *cqc,
2696     uint_t cqcindx, uint_t opmod, uint_t sleepflag)
2697 {
2698         hermon_mbox_info_t      mbox_info;
2699         hermon_cmd_post_t       cmd;
2700         uint64_t                data;
2701         uint_t                  size;
2702         int                     status, i;
2703 
2704         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2705 
2706         /* Get an "In" mailbox for the command */
2707         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
2708         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2709         if (status != HERMON_CMD_SUCCESS) {
2710                 return (status);
2711         }
2712 
2713         /* Copy the Hermon "MODIFY_CQ" command into mailbox */
2714         size = sizeof (hermon_hw_cqc_t);
2715         for (i = 0; i < (size >> 3); i++) {
2716                 data = ((uint64_t *)(void *)cqc)[i];
2717                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
2718                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
2719         }
2720 
2721         /* Sync the mailbox for the device to read */
2722         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
2723 
2724         /* Setup and post Hermon "MODIFY_CQ" command */
2725         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
2726         cmd.cp_outparm  = 0;
2727         cmd.cp_inmod    = cqcindx;
2728         cmd.cp_opcode   = MODIFY_CQ;
2729         cmd.cp_opmod    = (uint16_t)opmod;
2730         cmd.cp_flags    = sleepflag;
2731         status = hermon_cmd_post(state, &cmd);
2732 
2733         /* Free the mailbox */
2734         hermon_mbox_free(state, &mbox_info);
2735         return (status);
2736 }
2737 
2738 
2739 /*
2740  * hermon_cmn_qp_cmd_post()
2741  *    Context: Can be called from interrupt or base context.
2742  *
2743  *    This is the common function for posting all the various types of
2744  *    QP state transition related Hermon commands.  Since some of the
2745  *    commands differ from the others in the number (and type) of arguments
2746  *    that each require, this routine does checks based on opcode type
2747  *    (explained in more detail below).
2748  *
2749  * Note: This common function should be used only with the following
2750  *    opcodes: RTS2SQD_QP, TOERR_QP, TORST_QP, RST2INIT_QP, INIT2INIT_QP,
2751  *    INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP, SQD2RTS_QP, and SQERR2RTS_QP.
2752  */
2753 int
2754 hermon_cmn_qp_cmd_post(hermon_state_t *state, uint_t opcode,
2755     hermon_hw_qpc_t *qp, uint_t qpindx, uint32_t opmask,
2756     uint_t sleepflag)
2757 {
2758         hermon_mbox_info_t      mbox_info;
2759         hermon_cmd_post_t       cmd;
2760         uint64_t                data, in_mapaddr, out_mapaddr;
2761         uint_t                  size, flags, opmod;
2762         int                     status, i;
2763 
2764         /*
2765          * Use the specified opcode type to set the appropriate parameters.
2766          * Specifically, we need to set in_mapaddr, out_mapaddr, flags, and
2767          * opmod (as necessary).  Setting these parameters may also require
2768          * us to allocate an "In" or "Out" mailbox depending on the command
2769          * type.
2770          */
2771 
2772         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2773 
2774         if (opcode == RTS2SQD_QP) {
2775                 /*
2776                  * Note: For RTS-to-SendQueueDrain state transitions we
2777                  * always want to request the event generation from the
2778                  * hardware.  Though we may not notify the consumer of the
2779                  * drained event, the decision to forward (or not) is made
2780                  * later in the SQD event handler.
2781                  */
2782                 flags = HERMON_CMD_REQ_SQD_EVENT;
2783 
2784                 /*
2785                  * The RTS2SQD_QP command uses no "In" or "Out" mailboxes (and
2786                  * has no special opcode modifiers).
2787                  */
2788                 in_mapaddr  = 0;
2789                 out_mapaddr = 0;
2790                 opmod = 0;
2791 
2792         } else if (opcode == TOERR_QP) {
2793                 /*
2794                  * The TOERR_QP command uses no "In" or "Out" mailboxes, has no
2795                  * special opcode modifiers, and takes no special flags.
2796                  */
2797                 in_mapaddr  = 0;
2798                 out_mapaddr = 0;
2799                 opmod = 0;
2800                 flags = 0;
2801 
2802         } else if (opcode == TORST_QP) {
2803                 /*
2804                  * The TORST_QP command could take an "Out" mailbox, but we do
2805                  * not require it here.  It also does not takes any special
2806                  * flags.  It does however, take a HERMON_CMD_DIRECT_TO_RESET
2807                  * opcode modifier, which indicates that the transition to
2808                  * reset should happen without first moving the QP through the
2809                  * Error state (and, hence, without generating any unnecessary
2810                  * "flushed-in-error" completions).
2811                  */
2812                 in_mapaddr  = 0;
2813                 out_mapaddr = 0;
2814                 opmod = HERMON_CMD_DIRECT_TO_RESET | HERMON_CMD_NO_OUTMBOX;
2815                 flags = 0;
2816 
2817         } else {
2818                 /*
2819                  * All the other QP state transition commands (RST2INIT_QP,
2820                  * INIT2INIT_QP, INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP,
2821                  * SQD2RTS_QP, and SQERR2RTS_QP) require an "In" mailbox.
2822                  * None of these require any special flags or opcode modifiers.
2823                  */
2824                 mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
2825                 status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2826                 if (status != HERMON_CMD_SUCCESS) {
2827                         return (status);
2828                 }
2829                 in_mapaddr  = mbox_info.mbi_in->mb_mapaddr;
2830                 out_mapaddr = 0;
2831                 flags = 0;
2832                 opmod = 0;
2833 
2834                 /* Copy the Hermon command into the "In" mailbox */
2835                 size = sizeof (hermon_hw_qpc_t);
2836                 for (i = 0; i < (size >> 3); i++) {
2837                         data = ((uint64_t *)(void *)qp)[i];
2838                         ddi_put64(mbox_info.mbi_in->mb_acchdl,
2839                             ((uint64_t *)mbox_info.mbi_in->mb_addr + i + 1),
2840                             data);
2841                 }
2842                 ddi_put32(mbox_info.mbi_in->mb_acchdl,
2843                     ((uint32_t *)mbox_info.mbi_in->mb_addr), opmask);
2844 
2845                 /*
2846                  * Sync the mailbox for the device to read.  We have to add
2847                  * eight bytes here to account for "opt_param_mask" and
2848                  * proper alignment.
2849                  */
2850                 hermon_mbox_sync(mbox_info.mbi_in, 0, size + 8,
2851                     DDI_DMA_SYNC_FORDEV);
2852         }
2853 
2854         /* Setup and post Hermon QP state transition command */
2855         cmd.cp_inparm   = in_mapaddr;
2856         cmd.cp_outparm  = out_mapaddr;
2857         cmd.cp_inmod    = qpindx | flags;
2858         cmd.cp_opcode   = (uint16_t)opcode;
2859         cmd.cp_opmod    = (uint16_t)opmod;
2860         cmd.cp_flags    = sleepflag;
2861         status = hermon_cmd_post(state, &cmd);
2862 
2863         /*
2864          * If we allocated a mailbox (either an "In" or an "Out") above,
2865          * then free it now before returning.
2866          */
2867         if ((opcode != RTS2SQD_QP) && (opcode != TOERR_QP) &&
2868             (opcode != TORST_QP)) {
2869                 /* Free the mailbox */
2870                 hermon_mbox_free(state, &mbox_info);
2871         }
2872         return (status);
2873 }
2874 
2875 
2876 /*
2877  * hermon_cmn_query_cmd_post()
2878  *    Context: Can be called from interrupt or base context.
2879  *
2880  *    This is the common function for posting all the various types of
2881  *    Hermon query commands.  All Hermon query commands require an "Out"
2882  *    mailbox to be allocated for the resulting queried data.
2883  *
2884  * Note: This common function should be used only with the following
2885  *    opcodes: QUERY_DEV_LIM, QUERY_FW, QUERY_DDR, QUERY_ADAPTER, QUERY_PORT
2886  *     QUERY_HCA, QUERY_MPT, QUERY_EQ, QUERY_CQ, and QUERY_QP.
2887  *      With support of FCoIB, this also supports QUERY_FC.
2888  */
2889 int
2890 hermon_cmn_query_cmd_post(hermon_state_t *state, uint_t opcode, uint_t opmod,
2891     uint_t queryindx, void *query, uint_t size, uint_t sleepflag)
2892 {
2893         hermon_mbox_info_t      mbox_info;
2894         hermon_cmd_post_t       cmd;
2895         uint64_t                data;
2896         uint_t                  offset;
2897         int                     status, i;
2898 
2899         bzero(&cmd, sizeof (hermon_cmd_post_t));
2900 
2901         /* Get an "Out" mailbox for the command */
2902         mbox_info.mbi_alloc_flags = HERMON_ALLOC_OUTMBOX;
2903         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
2904         if (status != HERMON_CMD_SUCCESS) {
2905                 return (status);
2906         }
2907 
2908         /* Setup and post the Hermon query command */
2909         cmd.cp_inparm   = 0;
2910         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
2911         cmd.cp_inmod    = queryindx;
2912         cmd.cp_opcode   = (uint16_t)opcode;
2913         cmd.cp_opmod    = (uint16_t)opmod;
2914         cmd.cp_flags    = sleepflag;
2915         status = hermon_cmd_post(state, &cmd);
2916         if (status != HERMON_CMD_SUCCESS) {
2917                 goto cmn_query_fail;
2918         }
2919 
2920         /* Sync the mailbox to read the results */
2921         hermon_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
2922 
2923         /*
2924          * QUERY_QP is handled somewhat differently than the other query
2925          * commands.  For QUERY_QP, the actual queried data is offset into
2926          * the mailbox (by one 64-bit word).
2927          */
2928         offset = (opcode == QUERY_QP) ? 1 : 0;
2929 
2930         /* Copy query command results into "query" */
2931         for (i = 0; i < (size >> 3); i++) {
2932                 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
2933                     ((uint64_t *)mbox_info.mbi_out->mb_addr + i + offset));
2934                 ((uint64_t *)query)[i] = data;
2935         }
2936 
2937 cmn_query_fail:
2938         /* Free the mailbox */
2939         hermon_mbox_free(state, &mbox_info);
2940         return (status);
2941 }
2942 
2943 
2944 /*
2945  * hermon_cmn_ownership_cmd_post()
2946  *    Context: Can be called from interrupt or base context.
2947  *
2948  *    This is the common function for posting all the various types of
2949  *    Hermon HW/SW resource ownership commands.  Since some of the commands
2950  *    differ from the others in the direction of ownership change (i.e.
2951  *    from HW ownership to SW, or vice versa), they differ in the type of
2952  *    mailbox and specific handling that each requires.  This routine does
2953  *    certain checks based on opcode type to determine the direction of
2954  *    the transition and to correctly handle the request.
2955  *
2956  * Note: This common function should be used only with the following
2957  *    opcodes: HW2SW_MPT, HW2SW_EQ, HW2SW_CQ, SW2HW_MPT, SW2HW_EQ, and
2958  *    SW2HW_CQ
2959  */
2960 int
2961 hermon_cmn_ownership_cmd_post(hermon_state_t *state, uint_t opcode,
2962     void *hwrsrc, uint_t size, uint_t hwrsrcindx, uint_t sleepflag)
2963 {
2964         hermon_mbox_info_t      mbox_info;
2965         hermon_cmd_post_t       cmd;
2966         uint64_t                data, in_mapaddr, out_mapaddr;
2967         uint_t                  direction, opmod;
2968         int                     status, i;
2969 
2970         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
2971 
2972         /*
2973          * Determine the direction of the ownership transfer based on the
2974          * provided opcode
2975          */
2976         if ((opcode == HW2SW_MPT) || (opcode == HW2SW_EQ) ||
2977             (opcode == HW2SW_CQ) || (opcode == HW2SW_SRQ)) {
2978                 direction = HERMON_CMD_RSRC_HW2SW;
2979 
2980         } else if ((opcode == SW2HW_MPT) || (opcode == SW2HW_EQ) ||
2981             (opcode == SW2HW_CQ) || (opcode == SW2HW_SRQ)) {
2982                 direction = HERMON_CMD_RSRC_SW2HW;
2983 
2984         } else {
2985                 return (HERMON_CMD_INVALID_STATUS);
2986         }
2987 
2988         /*
2989          * If hwrsrc is NULL then we do not allocate a mailbox.  This is used
2990          * in the case of memory deregister where the out mailbox is not
2991          * needed.  In the case of re-register, we do use the hwrsrc.
2992          *
2993          * Otherwise, If ownership transfer is going from hardware to software,
2994          * then allocate an "Out" mailbox.  This will be filled in later as a
2995          * result of the Hermon command.
2996          *
2997          * And if the ownership transfer is going from software to hardware,
2998          * then we need an "In" mailbox, and we need to fill it in and sync it
2999          * (if necessary).  Then the mailbox can be passed to the Hermon
3000          * firmware.
3001          *
3002          * For the HW2SW (dereg) case, we only use an out mbox if hwrsrc is !=
3003          * NULL.  This implies a re-reg, and the out mbox must be used.  If
3004          * hwrsrc is == NULL, then we can save some time and resources by not
3005          * using an out mbox at all.  We must set opmod to HERMON_CMD_DO_OUTMBOX
3006          * and HERMON_CMD_NO_OUTMBOX appropriately in this case.
3007          *
3008          * For the SW2HW (reg) case, no out mbox is possible.  We set opmod to
3009          * 0 anyway, but this field is not used in this case.
3010          */
3011         if (direction == HERMON_CMD_RSRC_HW2SW) {
3012                 if (hwrsrc != NULL) {
3013                         mbox_info.mbi_alloc_flags = HERMON_ALLOC_OUTMBOX;
3014                         status = hermon_mbox_alloc(state, &mbox_info,
3015                             sleepflag);
3016                         if (status != HERMON_CMD_SUCCESS) {
3017                                 return (status);
3018                         }
3019                         in_mapaddr  = 0;
3020                         out_mapaddr = mbox_info.mbi_out->mb_mapaddr;
3021                         opmod = HERMON_CMD_DO_OUTMBOX;
3022                 } else {
3023                         in_mapaddr = 0;
3024                         out_mapaddr = 0;
3025                         opmod = HERMON_CMD_NO_OUTMBOX;
3026                 }
3027         } else {  /* HERMON_CMD_RSRC_SW2HW */
3028                 mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3029                 status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3030                 if (status != HERMON_CMD_SUCCESS) {
3031                         return (status);
3032                 }
3033 
3034                 /* Copy the SW2HW ownership command into mailbox */
3035                 for (i = 0; i < (size >> 3); i++) {
3036                         data = ((uint64_t *)hwrsrc)[i];
3037                         ddi_put64(mbox_info.mbi_in->mb_acchdl,
3038                             ((uint64_t *)mbox_info.mbi_in->mb_addr + i),
3039                             data);
3040                 }
3041 
3042                 /* Sync the mailbox for the device to read */
3043                 hermon_mbox_sync(mbox_info.mbi_in, 0, size,
3044                     DDI_DMA_SYNC_FORDEV);
3045 
3046                 in_mapaddr  = mbox_info.mbi_in->mb_mapaddr;
3047                 out_mapaddr = 0;
3048                 opmod = 0;
3049         }
3050 
3051         /* Setup and post the Hermon ownership command */
3052         cmd.cp_inparm   = in_mapaddr;
3053         cmd.cp_outparm  = out_mapaddr;
3054         cmd.cp_inmod    = hwrsrcindx;
3055         cmd.cp_opcode   = (uint16_t)opcode;
3056         cmd.cp_opmod    = (uint16_t)opmod;
3057         cmd.cp_flags    = sleepflag;
3058         status = hermon_cmd_post(state, &cmd);
3059         if (status != HERMON_CMD_SUCCESS) {
3060                 goto cmn_ownership_fail;
3061         }
3062 
3063         /*
3064          * As mentioned above, for HW2SW ownership transfers we need to
3065          * sync (if necessary) and copy out the resulting data from the
3066          * "Out" mailbox" (assuming the above command was successful).
3067          */
3068         if (direction == HERMON_CMD_RSRC_HW2SW && hwrsrc != NULL) {
3069 
3070                 /* Sync the mailbox to read the results */
3071                 hermon_mbox_sync(mbox_info.mbi_out, 0, size,
3072                     DDI_DMA_SYNC_FORCPU);
3073 
3074                 /* Copy HW2SW ownership command results into "hwrsrc" */
3075                 for (i = 0; i < (size >> 3); i++) {
3076                         data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
3077                             ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
3078                         ((uint64_t *)hwrsrc)[i] = data;
3079                 }
3080         }
3081 
3082 cmn_ownership_fail:
3083         if (hwrsrc != NULL) {
3084                 /* Free the mailbox */
3085                 hermon_mbox_free(state, &mbox_info);
3086         }
3087         return (status);
3088 }
3089 
3090 
3091 /*
3092  * hermon_conf_special_qp_cmd_post()
3093  *    Context: Can be called from interrupt or base context.
3094  */
3095 /*ARGSUSED*/
3096 int
3097 hermon_conf_special_qp_cmd_post(hermon_state_t *state, uint_t qpindx,
3098     uint_t qptype, uint_t sleepflag, uint_t opmod)
3099 {
3100         hermon_cmd_post_t       cmd;
3101         int                     status;
3102 
3103         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3104 
3105         /* Setup and post Hermon "CONF_SPECIAL_QP" command */
3106         cmd.cp_inparm   = 0;
3107         cmd.cp_outparm  = 0;
3108         cmd.cp_inmod    = qpindx & 0x00FFFFF8;      /* mask off low 3 bits */
3109         cmd.cp_opcode   = CONF_SPECIAL_QP;
3110         cmd.cp_opmod    = (uint16_t)opmod;
3111         cmd.cp_flags    = sleepflag;
3112         status = hermon_cmd_post(state, &cmd);
3113 
3114         return (status);
3115 }
3116 
3117 
3118 /*
3119  * hermon_get_heart_beat_rq_cmd_post()
3120  *    Context: Can be called only from kernel or interrupt context
3121  */
3122 int
3123 hermon_get_heart_beat_rq_cmd_post(hermon_state_t *state, uint_t qpindx,
3124     uint64_t *outparm)
3125 {
3126         hermon_cmd_post_t       cmd;
3127         int                     status;
3128 
3129         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3130 
3131         /* Setup and post the Hermon "HEART_BEAT_RQ" command */
3132         cmd.cp_inparm   = 0;
3133         cmd.cp_outparm  = 0;
3134         cmd.cp_inmod    = qpindx;
3135         cmd.cp_opcode   = HEART_BEAT_RQ;
3136         cmd.cp_opmod    = 0;
3137         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3138         status = hermon_cmd_post(state, &cmd);
3139 
3140         /*
3141          * Return immediate out param through argument pointer.
3142          */
3143         *outparm = cmd.cp_outparm;
3144         return (status);
3145 }
3146 
3147 
3148 /*
3149  * hermon_mgid_hash_cmd_post()
3150  *    Context: Can be called from interrupt or base context.
3151  */
3152 int
3153 hermon_mgid_hash_cmd_post(hermon_state_t *state, uint64_t mgid_h,
3154     uint64_t mgid_l, uint64_t *mgid_hash, uint_t sleepflag)
3155 {
3156         hermon_mbox_info_t      mbox_info;
3157         hermon_cmd_post_t       cmd;
3158         int                     status;
3159 
3160         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3161 
3162         /* Get an "In" mailbox for the command */
3163         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3164         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3165         if (status != HERMON_CMD_SUCCESS) {
3166                 return (status);
3167         }
3168 
3169         /* Copy the Hermon "MGID_HASH" command into mailbox */
3170         ddi_put64(mbox_info.mbi_in->mb_acchdl,
3171             ((uint64_t *)mbox_info.mbi_in->mb_addr + 0), mgid_h);
3172         ddi_put64(mbox_info.mbi_in->mb_acchdl,
3173             ((uint64_t *)mbox_info.mbi_in->mb_addr + 1), mgid_l);
3174 
3175         /* Sync the mailbox for the device to read */
3176         hermon_mbox_sync(mbox_info.mbi_in, 0, HERMON_CMD_MGIDHASH_SZ,
3177             DDI_DMA_SYNC_FORDEV);
3178 
3179         /* Setup and post the Hermon "MGID_HASH" command */
3180         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
3181         cmd.cp_outparm  = 0;
3182         cmd.cp_inmod    = 0;
3183         cmd.cp_opcode   = MGID_HASH;
3184         cmd.cp_opmod    = 0;
3185         cmd.cp_flags    = sleepflag;
3186         status = hermon_cmd_post(state, &cmd);
3187 
3188         /* MGID hash value is returned in command "outparam" */
3189         *mgid_hash = cmd.cp_outparm;
3190 
3191         /* Free the mailbox */
3192         hermon_mbox_free(state, &mbox_info);
3193         return (status);
3194 }
3195 
3196 
3197 /*
3198  * hermon_read_mgm_cmd_post()
3199  *    Context: Can be called from interrupt or base context.
3200  *
3201  * Note: It is assumed that the "mcg" parameter is actually a pointer to a
3202  *    "hermon_hw_mcg_t" struct and some number of "hermon_hw_mcg_qp_list_t"
3203  *    structs.  Combined size should be equal to result of HERMON_MCGMEM_SZ()
3204  *    macro.
3205  */
3206 int
3207 hermon_read_mgm_cmd_post(hermon_state_t *state, hermon_hw_mcg_t *mcg,
3208     uint_t mcgindx, uint_t sleepflag)
3209 {
3210         hermon_mbox_info_t      mbox_info;
3211         hermon_cmd_post_t       cmd;
3212         uint64_t                data;
3213         uint32_t                data32;
3214         uint_t                  size, hdrsz, qplistsz;
3215         int                     status, i;
3216 
3217         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3218 
3219         /* Get an "Out" mailbox for the results */
3220         mbox_info.mbi_alloc_flags = HERMON_ALLOC_OUTMBOX;
3221         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3222         if (status != HERMON_CMD_SUCCESS) {
3223                 return (status);
3224         }
3225 
3226         /* Setup and post Hermon "READ_MGM" command */
3227         cmd.cp_inparm   = 0;
3228         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
3229         cmd.cp_inmod    = mcgindx;
3230         cmd.cp_opcode   = READ_MGM;
3231         cmd.cp_opmod    = 0;
3232         cmd.cp_flags    = sleepflag;
3233         status = hermon_cmd_post(state, &cmd);
3234         if (status != HERMON_CMD_SUCCESS) {
3235                 goto read_mgm_fail;
3236         }
3237 
3238         /* Sync the mailbox to read the results */
3239         size = HERMON_MCGMEM_SZ(state);
3240         hermon_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
3241 
3242         /* Copy the READ_MGM command results into "mcg" */
3243         hdrsz = sizeof (hermon_hw_mcg_t);
3244         for (i = 0; i < (hdrsz >> 3); i++) {
3245                 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
3246                     ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
3247                 ((uint64_t *)mcg)[i] = data;
3248         }
3249         qplistsz = size - hdrsz;
3250         for (i = 0; i < (qplistsz >> 2); i++) {
3251                 data32 = ddi_get32(mbox_info.mbi_out->mb_acchdl,
3252                     ((uint32_t *)mbox_info.mbi_out->mb_addr + i + 8));
3253                 ((uint32_t *)mcg)[i + 8] = data32;
3254         }
3255 
3256 read_mgm_fail:
3257         /* Free the mailbox */
3258         hermon_mbox_free(state, &mbox_info);
3259         return (status);
3260 }
3261 
3262 
3263 /*
3264  * hermon_write_mgm_cmd_post()
3265  *    Context: Can be called from interrupt or base context.
3266  *
3267  * Note: It is assumed that the "mcg" parameter is actually a pointer to a
3268  *    "hermon_hw_mcg_t" struct and some number of "hermon_hw_mcg_qp_list_t"
3269  *    structs.  Combined size should be equal to result of HERMON_MCGMEM_SZ()
3270  *    macro.
3271  */
3272 int
3273 hermon_write_mgm_cmd_post(hermon_state_t *state, hermon_hw_mcg_t *mcg,
3274     uint_t mcgindx, uint_t sleepflag)
3275 {
3276         hermon_mbox_info_t      mbox_info;
3277         hermon_cmd_post_t       cmd;
3278         uint64_t                data;
3279         uint_t                  size, hdrsz, qplistsz;
3280         int                     status, i;
3281 
3282         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3283 
3284         /* Get an "In" mailbox for the command */
3285         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3286         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3287         if (status != HERMON_CMD_SUCCESS) {
3288                 return (status);
3289         }
3290 
3291         /* Copy the Hermon "WRITE_MGM" command into mailbox */
3292         size  = HERMON_MCGMEM_SZ(state);
3293         hdrsz = sizeof (hermon_hw_mcg_t);
3294         for (i = 0; i < (hdrsz >> 3); i++) {
3295                 data = ((uint64_t *)mcg)[i];
3296                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
3297                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
3298         }
3299         qplistsz = size - hdrsz;
3300         for (i = 0; i < (qplistsz >> 2); i++) {
3301                 data = ((uint32_t *)mcg)[i + 8];
3302                 ddi_put32(mbox_info.mbi_in->mb_acchdl,
3303                     ((uint32_t *)mbox_info.mbi_in->mb_addr + i + 8), data);
3304         }
3305 
3306         /* Sync the mailbox for the device to read */
3307         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
3308 
3309         /* Setup and post Hermon "WRITE_MGM" command */
3310         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
3311         cmd.cp_outparm  = 0;
3312         cmd.cp_inmod    = mcgindx;
3313         cmd.cp_opcode   = WRITE_MGM;
3314         cmd.cp_opmod    = 0;
3315         cmd.cp_flags    = sleepflag;
3316         status = hermon_cmd_post(state, &cmd);
3317 
3318         /* Free the mailbox */
3319         hermon_mbox_free(state, &mbox_info);
3320         return (status);
3321 }
3322 
3323 /*
3324  * hermon_resize_srq_cmd_post()
3325  *    Context: Can be called from interrupt or base context.
3326  */
3327 
3328 int hermon_resize_srq_cmd_post(hermon_state_t *state, hermon_hw_srqc_t *srq,
3329     uint_t srqnum, uint_t sleepflag)
3330 {
3331         hermon_mbox_info_t      mbox_info;
3332         hermon_cmd_post_t       cmd;
3333         uint64_t                data;
3334         uint_t                  size;
3335         int                     status, i;
3336 
3337         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3338 
3339         /* Get an "In" mailbox for the command */
3340         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3341         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3342         if (status != HERMON_CMD_SUCCESS) {
3343                 return (status);
3344         }
3345 
3346         /* Copy the Hermon "RESIZE_SRQ" command into mailbox */
3347         size = sizeof (hermon_hw_srqc_t);
3348         for (i = 0; i < (size >> 3); i++) {
3349                 data = ((uint64_t *)(void *)srq)[i];
3350                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
3351                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
3352         }
3353 
3354         /* Sync the mailbox for the device to read */
3355         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
3356 
3357         /* Setup and post Hermon "RESIZE_SRQ" command */
3358         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
3359         cmd.cp_outparm  = 0;
3360         cmd.cp_inmod    = srqnum;
3361         cmd.cp_opcode   = RESIZE_SRQ;
3362         cmd.cp_opmod    = 0;
3363         cmd.cp_flags    = sleepflag;
3364         status = hermon_cmd_post(state, &cmd);
3365 
3366         /* Free the mailbox */
3367         hermon_mbox_free(state, &mbox_info);
3368         return (status);
3369 }
3370 /*
3371  * hermon_modify_mpt_cmd_post()
3372  *    Context: Can be called from interrupt or base context.
3373  */
3374 int
3375 hermon_modify_mpt_cmd_post(hermon_state_t *state, hermon_hw_dmpt_t *mpt,
3376     uint_t mptindx, uint_t flags, uint_t sleepflag)
3377 {
3378         hermon_mbox_info_t      mbox_info;
3379         hermon_cmd_post_t       cmd;
3380         uint64_t                data;
3381         uint_t                  size;
3382         int                     status, i;
3383 
3384         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3385 
3386         /* Get an "In" mailbox for the command */
3387         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3388         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3389         if (status != HERMON_CMD_SUCCESS) {
3390                 return (status);
3391         }
3392 
3393         /* Copy the Hermon "MODIFY_MPT" command into mailbox */
3394         size = sizeof (hermon_hw_dmpt_t);
3395         for (i = 0; i < (size >> 3); i++) {
3396                 data = ((uint64_t *)mpt)[i];
3397                 ddi_put64(mbox_info.mbi_in->mb_acchdl,
3398                     ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
3399         }
3400 
3401         /* Sync the mailbox for the device to read */
3402         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
3403 
3404         /* Setup and post Hermon "MODIFY_MPT" command */
3405         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
3406         cmd.cp_outparm  = 0;
3407         cmd.cp_inmod    = mptindx;
3408         cmd.cp_opcode   = MODIFY_MPT;
3409         cmd.cp_opmod    = (uint16_t)flags;
3410         cmd.cp_flags    = sleepflag;
3411         status = hermon_cmd_post(state, &cmd);
3412 
3413         /* Free the mailbox */
3414         hermon_mbox_free(state, &mbox_info);
3415         return (status);
3416 }
3417 
3418 
3419 /*
3420  * hermon_config_fc_cmd_post()
3421  *      Context: Can be called from user or kernel context.
3422  *      This can do either a basic config passing in
3423  *      *hermon_hw_config_fc_basic_s, or config the N_Port table.
3424  *      passing in pointer to an array of 32-bit id's
3425  *      Note that either one needs to be cast to void *
3426  */
3427 int
3428 hermon_config_fc_cmd_post(hermon_state_t *state, void *cfginfo, int enable,
3429     int selector, int n_ports, int portnum, uint_t sleepflag)
3430 {
3431         hermon_mbox_info_t      mbox_info;
3432         hermon_cmd_post_t       cmd;
3433         uint64_t                data;
3434         uint32_t                portid;
3435         uint_t                  size;
3436         int                     status, i;
3437 
3438         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3439 
3440         /* Get an "In" mailbox for the command */
3441         mbox_info.mbi_alloc_flags = HERMON_ALLOC_INMBOX;
3442         status = hermon_mbox_alloc(state, &mbox_info, sleepflag);
3443         if (status != HERMON_CMD_SUCCESS) {
3444                 return (status);
3445         }
3446 
3447         /* Copy the appropriate info into mailbox */
3448         if (selector == HERMON_HW_FC_CONF_BASIC) {      /* basic info */
3449                 size = sizeof (hermon_hw_config_fc_basic_t);
3450                 for (i = 0; i < (size >> 3); i++) {
3451                         data = ((uint64_t *)cfginfo)[i];
3452                         ddi_put64(mbox_info.mbi_in->mb_acchdl,
3453                             ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
3454                 }
3455         } else {                                        /* NPort config */
3456                 ASSERT(selector == HERMON_HW_FC_CONF_NPORT);
3457                 size = n_ports * sizeof (uint32_t);
3458                 /*
3459                  * n_ports must == number queried from card
3460                  *
3461                  * passed in is an array but for little endian needs to
3462                  * be rearranged in the mbox
3463                  */
3464                 for (i = 0; i < (size >> 3); i++) {
3465                         portid = ((uint32_t *)cfginfo)[i * 2];
3466                         data = (uint64_t)portid << 32;
3467                         if (i * 2 < n_ports) {
3468                                 portid = ((uint32_t *)cfginfo)[i * 2 + 1];
3469                                 data |= portid;
3470                         }
3471                         ddi_put64(mbox_info.mbi_in->mb_acchdl,
3472                             ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
3473                 }
3474         }
3475 
3476         /* Sync the mailbox for the device to read */
3477         hermon_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);
3478 
3479         /* Setup and post Hermon "CONFIG_FC" command */
3480         cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
3481         cmd.cp_outparm  = 0;
3482         cmd.cp_inmod    = (uint32_t)(selector | portnum);
3483         cmd.cp_opcode   = CONFIG_FC;
3484         cmd.cp_opmod    = (uint16_t)enable;
3485         cmd.cp_flags    = sleepflag;
3486         status = hermon_cmd_post(state, &cmd);
3487 
3488         /* Free the mailbox */
3489         hermon_mbox_free(state, &mbox_info);
3490         return (status);
3491 }
3492 
3493 /*
3494  * hermon_sense_port_post() - used to send protocol running on a port
3495  *      Context: Can be called from interrupt or base context
3496  */
3497 
3498 int
3499 hermon_sense_port_post(hermon_state_t *state, uint_t portnum,
3500     uint32_t *protocol)
3501 {
3502         hermon_cmd_post_t       cmd;
3503         int                     status;
3504 
3505         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3506 
3507         /* Setup and post Hermon "CMD_NOP" command */
3508         cmd.cp_inparm   = 0;
3509         cmd.cp_outparm  = 0;
3510         cmd.cp_inmod    = (uint32_t)portnum;
3511         cmd.cp_opcode   = SENSE_PORT;
3512         cmd.cp_opmod    = 0;
3513         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3514         status = hermon_cmd_post(state, &cmd);
3515         if (status == HERMON_CMD_SUCCESS) *protocol = (uint32_t)cmd.cp_outparm;
3516         return (status);
3517 }
3518 
3519 
3520 /*
3521  * CONFIG_INT_MOD - used to configure INTERRUPT moderation
3522  *      if command fails, *health is invalid/undefined
3523  */
3524 int
3525 hermon_config_int_mod(hermon_state_t *state, uint_t min_delay, uint_t vector)
3526 {
3527         hermon_cmd_post_t       cmd;
3528         int                     status;
3529         uint64_t                inparm = 0;
3530 
3531         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3532 
3533         /* Setup and post Hermon "CONFIG_INT_MOD" command */
3534         inparm = (((uint64_t)min_delay & 0xFFFF) << 48) ||
3535             (((uint64_t)vector & 0xFFFF) << 32);
3536 
3537         cmd.cp_inparm   = inparm;
3538         cmd.cp_outparm  = 0;
3539         cmd.cp_inmod    = 0;
3540         cmd.cp_opcode   = CONFIG_INT_MOD;
3541         cmd.cp_opmod    = 0;
3542         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3543         status = hermon_cmd_post(state, &cmd);
3544         return (status);
3545 }
3546 
3547 
3548 int
3549 hermon_nop_post(hermon_state_t *state, uint_t interval, uint_t sleep)
3550 {
3551         hermon_cmd_post_t       cmd;
3552         int                     status;
3553 
3554         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3555 
3556         /* Setup and post Hermon "CMD_NOP" command */
3557         cmd.cp_inparm   = 0;
3558         cmd.cp_outparm  = 0;
3559         cmd.cp_inmod    = interval;
3560         cmd.cp_opcode   = CMD_NOP;
3561         cmd.cp_opmod    = 0;
3562         cmd.cp_flags    = HERMON_CMD_SLEEP_NOSPIN;
3563         if (sleep) cmd.cp_flags = HERMON_CMD_NOSLEEP_SPIN;
3564         status = hermon_cmd_post(state, &cmd);
3565         return (status);
3566 }
3567 
3568 int
3569 hermon_hw_health_check(hermon_state_t *state, int *health)
3570 {
3571         hermon_cmd_post_t       cmd;
3572         int                     status;
3573 
3574         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3575 
3576         /* Setup and post Hermon "CMD_NOP" command */
3577         cmd.cp_inparm   = 0;
3578         cmd.cp_outparm  = 0;
3579         cmd.cp_inmod    = 0;
3580         cmd.cp_opcode   = HW_HEALTH_CHECK;
3581         cmd.cp_opmod    = 0;
3582         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3583         status = hermon_cmd_post(state, &cmd);
3584         *health = (int)cmd.cp_outparm;
3585         return (status);
3586 }
3587 
3588 int
3589 hermon_setdebug_post(hermon_state_t *state)
3590 {
3591         hermon_cmd_post_t       cmd;
3592         int                     status;
3593 
3594         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3595 
3596         /* Setup and post Hermon "CMD_NOP" command */
3597         cmd.cp_inparm   = 0xFFFFFFFFFFFFFFFF;
3598         cmd.cp_outparm  = 0;
3599         cmd.cp_inmod    = 0;
3600         cmd.cp_opcode   = SET_DEBUG_MSG;
3601         cmd.cp_opmod    = 0;
3602         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3603         status = hermon_cmd_post(state, &cmd);
3604         return (status);
3605 }
3606 
3607 
3608 int
3609 hermon_read_mtt_cmd_post(hermon_state_t *state, uint64_t mtt_addr,
3610         hermon_hw_mtt_t *mtt)
3611 {
3612 
3613         hermon_cmd_post_t       cmd;
3614         hermon_mbox_info_t      mbox_info;
3615         int                     i, status;
3616         uint_t                  size;
3617         uint64_t                data;
3618 
3619         bzero((void *)&cmd, sizeof (hermon_cmd_post_t));
3620 
3621         /* Get an "Out" mailbox for the command */
3622         mbox_info.mbi_alloc_flags = HERMON_ALLOC_OUTMBOX;
3623         status = hermon_mbox_alloc(state, &mbox_info, HERMON_CMD_SLEEP_NOSPIN);
3624         if (status != HERMON_CMD_SUCCESS) {
3625                 return (status);
3626         }
3627 
3628         /* Setup and post the "READ_MTT" command */
3629         cmd.cp_inparm   = mtt_addr;
3630         cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
3631         cmd.cp_inmod    = 1;
3632         cmd.cp_opcode   = READ_MTT;
3633         cmd.cp_opmod    = 0;
3634         cmd.cp_flags    = HERMON_CMD_NOSLEEP_SPIN;
3635         status = hermon_cmd_post(state, &cmd);
3636         if (status != HERMON_CMD_SUCCESS) {
3637                 return (status);
3638         }
3639 
3640         /* Sync the mailbox to read the results */
3641         size = sizeof (hermon_hw_mtt_t);
3642         hermon_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);
3643 
3644         /* Copy mtt read out */
3645         for (i = 0; i < (size >> 3); i++) {
3646                 data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
3647                     ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
3648                 ((uint64_t *)(void *)mtt)[i] = data;
3649         }
3650 
3651         /* Free the mailbox */
3652         hermon_mbox_free(state, &mbox_info);
3653         return (status);
3654 }