root/usr/src/uts/common/io/ib/adapters/tavor/tavor_cmd.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * tavor_cmd.c
 *    Tavor Firmware Command Routines
 *
 *    Implements all the routines necessary for allocating, posting, and
 *    freeing commands for the Tavor firmware.  These routines manage a
 *    preallocated list of command mailboxes and provide interfaces to post
 *    each of the several dozen commands to the Tavor firmware.
 */

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>

#include <sys/ib/adapters/tavor/tavor.h>

static int tavor_impl_mbox_alloc(tavor_state_t *state, tavor_mboxlist_t *mblist,
    tavor_mbox_t **mb, uint_t mbox_wait);
static void tavor_impl_mbox_free(tavor_mboxlist_t *mblist, tavor_mbox_t **mb);
static int tavor_impl_mboxlist_init(tavor_state_t *state,
    tavor_mboxlist_t *mblist, uint_t num_mbox, tavor_rsrc_type_t type);
static void tavor_impl_mboxlist_fini(tavor_state_t *state,
    tavor_mboxlist_t *mblist);
static int tavor_outstanding_cmd_alloc(tavor_state_t *state,
    tavor_cmd_t **cmd_ptr, uint_t cmd_wait);
static void tavor_outstanding_cmd_free(tavor_state_t *state,
    tavor_cmd_t **cmd_ptr);
static int tavor_write_hcr(tavor_state_t *state, tavor_cmd_post_t *cmdpost,
    uint16_t token);
static void tavor_mbox_sync(tavor_mbox_t *mbox, uint_t offset,
    uint_t length, uint_t flag);

/*
 * tavor_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 *    The "cp_flags" field in cmdpost
 *    is used to determine whether to wait for an available
 *    outstanding command (if necessary) or to return error.
 */
int
tavor_cmd_post(tavor_state_t *state, tavor_cmd_post_t *cmdpost)
{
        tavor_cmd_t     *cmdptr;
        int             status;
        uint16_t        token;

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdpost))

        /* Determine if we are going to spin until completion */
        if (cmdpost->cp_flags == TAVOR_CMD_NOSLEEP_SPIN) {

                /* Write the command to the HCR */
                status = tavor_write_hcr(state, cmdpost, 0);
                if (status != TAVOR_CMD_SUCCESS) {
                        return (status);
                }

                return (TAVOR_CMD_SUCCESS);

        } else {  /* "TAVOR_CMD_SLEEP_NOSPIN" */

                ASSERT(TAVOR_SLEEPFLAG_FOR_CONTEXT() != TAVOR_NOSLEEP);

                /* NOTE: Expect threads to be waiting in here */
                status = tavor_outstanding_cmd_alloc(state, &cmdptr,
                    cmdpost->cp_flags);
                if (status != TAVOR_CMD_SUCCESS) {
                        return (status);
                }
                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdptr))

                /*
                 * Set status to "TAVOR_CMD_INVALID_STATUS".  It is
                 * appropriate to do this here without the "cmd_comp_lock"
                 * because this register is overloaded.  Later it will be
                 * used to indicate - through a change from this invalid
                 * value to some other value - that the condition variable
                 * has been signaled.  Once it has, status will then contain
                 * the _real_ completion status
                 */
                cmdptr->cmd_status = TAVOR_CMD_INVALID_STATUS;

                /* Write the command to the HCR */
                token = (uint16_t)cmdptr->cmd_indx;
                status = tavor_write_hcr(state, cmdpost, token);
                if (status != TAVOR_CMD_SUCCESS) {
                        tavor_outstanding_cmd_free(state, &cmdptr);
                        return (status);
                }

                /*
                 * cv_wait() on the "command_complete" condition variable.
                 * Note: We have the "__lock_lint" here to workaround warlock.
                 * Since warlock doesn't know that other parts of the Tavor
                 * may occasionally call this routine while holding their own
                 * locks, it complains about this cv_wait.  In reality,
                 * however, the rest of the driver never calls this routine
                 * with a lock held unless they pass TAVOR_CMD_NOSLEEP.
                 */
                _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*cmdptr))
                mutex_enter(&cmdptr->cmd_comp_lock);
                while (cmdptr->cmd_status == TAVOR_CMD_INVALID_STATUS) {
#ifndef __lock_lint
                        cv_wait(&cmdptr->cmd_comp_cv, &cmdptr->cmd_comp_lock);
                        /* NOTE: EXPECT SEVERAL THREADS TO BE WAITING HERE */
#endif
                }
                mutex_exit(&cmdptr->cmd_comp_lock);
                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cmdptr))

                /*
                 * Wake up after command completes (cv_signal).  Read status
                 * from the command (success, fail, etc.).  It is appropriate
                 * here (as above) to read the status field without the
                 * "cmd_comp_lock" because it is no longer being used to
                 * indicate whether the condition variable has been signaled
                 * (i.e. at this point we are certain that it already has).
                 */
                status = cmdptr->cmd_status;

                /* Save the "outparam" values into the cmdpost struct */
                cmdpost->cp_outparm = cmdptr->cmd_outparm;

                /*
                 * Add the command back to the "outstanding commands list".
                 * Signal the "cmd_list" condition variable, if necessary.
                 */
                tavor_outstanding_cmd_free(state, &cmdptr);

                if (status != TAVOR_CMD_SUCCESS) {
                        return (status);
                }

                return (TAVOR_CMD_SUCCESS);
        }
}


/*
 * tavor_mbox_alloc()
 *    Context: Can be called from interrupt or base context.
 *
 *    The "mbox_wait" parameter is used to determine whether to
 *    wait for a mailbox to become available or not.
 */
int
tavor_mbox_alloc(tavor_state_t *state, tavor_mbox_info_t *mbox_info,
    uint_t mbox_wait)
{
        int                     status;
        uint_t                  sleep_context;

        sleep_context = TAVOR_SLEEPFLAG_FOR_CONTEXT();

        /* Allocate an "In" mailbox */
        if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
                /* Determine correct mboxlist based on calling context */
                if (sleep_context == TAVOR_NOSLEEP) {
                        status = tavor_impl_mbox_alloc(state,
                            &state->ts_in_intr_mblist,
                            &mbox_info->mbi_in, mbox_wait);

                        ASSERT(status == TAVOR_CMD_SUCCESS);
                } else {
                        /* NOTE: Expect threads to be waiting in here */
                        status = tavor_impl_mbox_alloc(state,
                            &state->ts_in_mblist, &mbox_info->mbi_in,
                            mbox_wait);
                        if (status != TAVOR_CMD_SUCCESS) {
                                return (status);
                        }
                }

        }

        /* Allocate an "Out" mailbox */
        if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
                /* Determine correct mboxlist based on calling context */
                if (sleep_context == TAVOR_NOSLEEP) {
                        status = tavor_impl_mbox_alloc(state,
                            &state->ts_out_intr_mblist,
                            &mbox_info->mbi_out, mbox_wait);

                        ASSERT(status == TAVOR_CMD_SUCCESS);
                } else {
                        /* NOTE: Expect threads to be waiting in here */
                        status = tavor_impl_mbox_alloc(state,
                            &state->ts_out_mblist, &mbox_info->mbi_out,
                            mbox_wait);
                        if (status != TAVOR_CMD_SUCCESS) {
                                /* If we allocated an "In" mailbox, free it */
                                if (mbox_info->mbi_alloc_flags &
                                    TAVOR_ALLOC_INMBOX) {
                                        tavor_impl_mbox_free(
                                            &state->ts_in_mblist,
                                            &mbox_info->mbi_in);
                                }
                                return (status);
                        }
                }
        }

        /* Store appropriate context in mbox_info */
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(mbox_info->mbi_sleep_context))
        mbox_info->mbi_sleep_context = sleep_context;

        return (TAVOR_CMD_SUCCESS);
}


/*
 * tavor_mbox_free()
 *    Context: Can be called from interrupt or base context.
 */
void
tavor_mbox_free(tavor_state_t *state, tavor_mbox_info_t *mbox_info)
{
        /*
         * The mailbox has to be freed in the same context from which it was
         * allocated.  The context is stored in the mbox_info at
         * tavor_mbox_alloc() time.  We check the stored context against the
         * current context here.
         */
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(mbox_info->mbi_sleep_context))
        ASSERT(mbox_info->mbi_sleep_context == TAVOR_SLEEPFLAG_FOR_CONTEXT());

        /* Determine correct mboxlist based on calling context */
        if (mbox_info->mbi_sleep_context == TAVOR_NOSLEEP) {
                /* Free the intr "In" mailbox */
                if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
                        tavor_impl_mbox_free(&state->ts_in_intr_mblist,
                            &mbox_info->mbi_in);
                }

                /* Free the intr "Out" mailbox */
                if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
                        tavor_impl_mbox_free(&state->ts_out_intr_mblist,
                            &mbox_info->mbi_out);
                }
        } else {
                /* Free the "In" mailbox */
                if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_INMBOX) {
                        tavor_impl_mbox_free(&state->ts_in_mblist,
                            &mbox_info->mbi_in);
                }

                /* Free the "Out" mailbox */
                if (mbox_info->mbi_alloc_flags & TAVOR_ALLOC_OUTMBOX) {
                        tavor_impl_mbox_free(&state->ts_out_mblist,
                            &mbox_info->mbi_out);
                }
        }
}


/*
 * tavor_cmd_complete_handler()
 *    Context: Called only from interrupt context.
 */
int
tavor_cmd_complete_handler(tavor_state_t *state, tavor_eqhdl_t eq,
    tavor_hw_eqe_t *eqe)
{
        tavor_cmd_t             *cmdp;
        uint_t                  eqe_evttype;

        eqe_evttype = TAVOR_EQE_EVTTYPE_GET(eq, eqe);

        ASSERT(eqe_evttype == TAVOR_EVT_COMMAND_INTF_COMP ||
            eqe_evttype == TAVOR_EVT_EQ_OVERFLOW);

        if (eqe_evttype == TAVOR_EVT_EQ_OVERFLOW) {
                tavor_eq_overflow_handler(state, eq, eqe);

                return (DDI_FAILURE);
        }

        /*
         * Find the outstanding command pointer based on value returned
         * in "token"
         */
        cmdp = &state->ts_cmd_list.cml_cmd[TAVOR_EQE_CMDTOKEN_GET(eq, eqe)];

        /* Signal the waiting thread */
        mutex_enter(&cmdp->cmd_comp_lock);
        cmdp->cmd_outparm = ((uint64_t)TAVOR_EQE_CMDOUTP0_GET(eq, eqe) << 32) |
            TAVOR_EQE_CMDOUTP1_GET(eq, eqe);
        cmdp->cmd_status = TAVOR_EQE_CMDSTATUS_GET(eq, eqe);

        cv_signal(&cmdp->cmd_comp_cv);
        mutex_exit(&cmdp->cmd_comp_lock);

        return (DDI_SUCCESS);
}


/*
 * tavor_inmbox_list_init()
 *    Context: Only called from attach() path context
 */
int
tavor_inmbox_list_init(tavor_state_t *state)
{
        int             status;
        uint_t          num_inmbox;

        /* Initialize the "In" mailbox list */
        num_inmbox  =  (1 << state->ts_cfg_profile->cp_log_num_inmbox);
        status = tavor_impl_mboxlist_init(state, &state->ts_in_mblist,
            num_inmbox, TAVOR_IN_MBOX);
        if (status != DDI_SUCCESS) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}


/*
 * tavor_intr_inmbox_list_init()
 *    Context: Only called from attach() path context
 */
int
tavor_intr_inmbox_list_init(tavor_state_t *state)
{
        int             status;
        uint_t          num_inmbox;

        /* Initialize the interrupt "In" mailbox list */
        num_inmbox  =  (1 << state->ts_cfg_profile->cp_log_num_intr_inmbox);
        status = tavor_impl_mboxlist_init(state, &state->ts_in_intr_mblist,
            num_inmbox, TAVOR_INTR_IN_MBOX);
        if (status != DDI_SUCCESS) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}


/*
 * tavor_outmbox_list_init()
 *    Context: Only called from attach() path context
 */
int
tavor_outmbox_list_init(tavor_state_t *state)
{
        int             status;
        uint_t          num_outmbox;

        /* Initialize the "Out" mailbox list */
        num_outmbox  =  (1 << state->ts_cfg_profile->cp_log_num_outmbox);
        status = tavor_impl_mboxlist_init(state, &state->ts_out_mblist,
            num_outmbox, TAVOR_OUT_MBOX);
        if (status != DDI_SUCCESS) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}


/*
 * tavor_intr_outmbox_list_init()
 *    Context: Only called from attach() path context
 */
int
tavor_intr_outmbox_list_init(tavor_state_t *state)
{
        int             status;
        uint_t          num_outmbox;

        /* Initialize the interrupts "Out" mailbox list */
        num_outmbox  =  (1 << state->ts_cfg_profile->cp_log_num_intr_outmbox);
        status = tavor_impl_mboxlist_init(state, &state->ts_out_intr_mblist,
            num_outmbox, TAVOR_INTR_OUT_MBOX);
        if (status != DDI_SUCCESS) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}


/*
 * tavor_inmbox_list_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
void
tavor_inmbox_list_fini(tavor_state_t *state)
{
        /* Free up the "In" mailbox list */
        tavor_impl_mboxlist_fini(state, &state->ts_in_mblist);
}


/*
 * tavor_intr_inmbox_list_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
void
tavor_intr_inmbox_list_fini(tavor_state_t *state)
{
        /* Free up the interupts "In" mailbox list */
        tavor_impl_mboxlist_fini(state, &state->ts_in_intr_mblist);
}


/*
 * tavor_outmbox_list_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
void
tavor_outmbox_list_fini(tavor_state_t *state)
{
        /* Free up the "Out" mailbox list */
        tavor_impl_mboxlist_fini(state, &state->ts_out_mblist);
}


/*
 * tavor_intr_outmbox_list_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
void
tavor_intr_outmbox_list_fini(tavor_state_t *state)
{
        /* Free up the interrupt "Out" mailbox list */
        tavor_impl_mboxlist_fini(state, &state->ts_out_intr_mblist);
}


/*
 * tavor_impl_mbox_alloc()
 *    Context: Can be called from interrupt or base context.
 */
static int
tavor_impl_mbox_alloc(tavor_state_t *state, tavor_mboxlist_t *mblist,
    tavor_mbox_t **mb, uint_t mbox_wait)
{
        tavor_mbox_t    *mbox_ptr;
        uint_t          index, next, prev;
        uint_t          count, countmax;

        /*
         * If the mailbox list is empty, then wait (if appropriate in the
         * current context).  Otherwise, grab the next available mailbox.
         */
        if (mbox_wait == TAVOR_NOSLEEP) {
                count    = 0;
                countmax = state->ts_cfg_profile->cp_cmd_poll_max;

                mutex_enter(&mblist->mbl_lock);
                mblist->mbl_pollers++;
                while (mblist->mbl_entries_free == 0) {
                        mutex_exit(&mblist->mbl_lock);
                        /* Delay loop polling for an available mbox */
                        if (++count > countmax) {
                                return (TAVOR_CMD_INSUFF_RSRC);
                        }

                        /* Delay before polling for mailbox again */
                        drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);
                        mutex_enter(&mblist->mbl_lock);
                }
                mblist->mbl_pollers--;

        /* TAVOR_SLEEP */
        } else {
                /*
                 * Grab lock here as we prepare to cv_wait if needed.
                 */
                mutex_enter(&mblist->mbl_lock);
                while (mblist->mbl_entries_free == 0) {
                        /*
                         * Wait (on cv) for a mailbox to become free.  Note:
                         * Just as we do above in tavor_cmd_post(), we also
                         * have the "__lock_lint" here to workaround warlock.
                         * Warlock doesn't know that other parts of the Tavor
                         * may occasionally call this routine while holding
                         * their own locks, so it complains about this cv_wait.
                         * In reality, however, the rest of the driver never
                         * calls this routine with a lock held unless they pass
                         * TAVOR_CMD_NOSLEEP.
                         */
                        mblist->mbl_waiters++;
#ifndef __lock_lint
                        cv_wait(&mblist->mbl_cv, &mblist->mbl_lock);
#endif
                }
        }

        /* Grab the next available mailbox from list */
        mbox_ptr = mblist->mbl_mbox;
        index    = mblist->mbl_head_indx;
        next     = mbox_ptr[index].mb_next;
        prev     = mbox_ptr[index].mb_prev;

        /* Remove it from the mailbox list */
        mblist->mbl_mbox[next].mb_prev  = prev;
        mblist->mbl_mbox[prev].mb_next  = next;
        mblist->mbl_head_indx           = next;

        /* Update the "free" count and return the mailbox pointer */
        mblist->mbl_entries_free--;
        *mb = &mbox_ptr[index];

        mutex_exit(&mblist->mbl_lock);

        return (TAVOR_CMD_SUCCESS);
}


/*
 * tavor_impl_mbox_free()
 *    Context: Can be called from interrupt or base context.
 */
static void
tavor_impl_mbox_free(tavor_mboxlist_t *mblist, tavor_mbox_t **mb)
{
        uint_t          mbox_indx;

        mutex_enter(&mblist->mbl_lock);

        /* Pull the "index" from mailbox entry */
        mbox_indx = (*mb)->mb_indx;

        /*
         * If mailbox list is not empty, then insert the entry.  Otherwise,
         * this is the only entry.  So update the pointers appropriately.
         */
        if (mblist->mbl_entries_free++ != 0) {
                /* Update the current mailbox */
                (*mb)->mb_next = mblist->mbl_head_indx;
                (*mb)->mb_prev = mblist->mbl_tail_indx;

                /* Update head and tail mailboxes */
                mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev = mbox_indx;
                mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next = mbox_indx;

                /* Update tail index */
                mblist->mbl_tail_indx = mbox_indx;

        } else {
                /* Update the current mailbox */
                (*mb)->mb_next = mbox_indx;
                (*mb)->mb_prev = mbox_indx;

                /* Update head and tail indexes */
                mblist->mbl_tail_indx = mbox_indx;
                mblist->mbl_head_indx = mbox_indx;
        }

        /*
         * Because we can have both waiters (SLEEP treads waiting for a
         * cv_signal to continue processing) and pollers (NOSLEEP treads
         * polling for a mailbox to become available), we try to share CPU time
         * between them.  We do this by signalling the waiters only every other
         * call to mbox_free.  This gives the pollers a chance to get some CPU
         * time to do their command.  If we signalled every time, the pollers
         * would have a much harder time getting CPU time.
         *
         * If there are waiters and no pollers, then we signal always.
         *
         * Otherwise, if there are either no waiters, there may in fact be
         * pollers, so we do not signal in that case.
         */
        if (mblist->mbl_pollers > 0 && mblist->mbl_waiters > 0) {
                /* flip the signal value */
                mblist->mbl_signal = (mblist->mbl_signal + 1) % 2;
        } else if (mblist->mbl_waiters > 0) {
                mblist->mbl_signal = 1;
        } else {
                mblist->mbl_signal = 0;
        }

        /*
         * Depending on the conditions in the previous check, we signal only if
         * we are supposed to.
         */
        if (mblist->mbl_signal) {
                mblist->mbl_waiters--;
                cv_signal(&mblist->mbl_cv);
        }

        /* Clear out the mailbox entry pointer */
        *mb = NULL;

        mutex_exit(&mblist->mbl_lock);
}


/*
 * tavor_impl_mboxlist_init()
 *    Context: Only called from attach() path context
 */
static int
tavor_impl_mboxlist_init(tavor_state_t *state, tavor_mboxlist_t *mblist,
    uint_t num_mbox, tavor_rsrc_type_t type)
{
        tavor_rsrc_t            *rsrc;
        ddi_dma_cookie_t        dma_cookie;
        uint_t                  dma_cookiecnt, flag, sync;
        int                     status, i;

        /* Allocate the memory for the mailbox entries list */
        mblist->mbl_list_sz = num_mbox;
        mblist->mbl_mbox = kmem_zalloc(mblist->mbl_list_sz *
            sizeof (tavor_mbox_t), KM_SLEEP);

        /* Initialize the mailbox entries list */
        mblist->mbl_head_indx    = 0;
        mblist->mbl_tail_indx    = mblist->mbl_list_sz - 1;
        mblist->mbl_entries_free = mblist->mbl_list_sz;
        mblist->mbl_waiters      = 0;
        mblist->mbl_num_alloc    = 0;

        /* Set up the mailbox list's cv and mutex */
        mutex_init(&mblist->mbl_lock, NULL, MUTEX_DRIVER,
            DDI_INTR_PRI(state->ts_intrmsi_pri));
        cv_init(&mblist->mbl_cv, NULL, CV_DRIVER, NULL);

        /* Determine if syncs will be necessary */
        sync = TAVOR_MBOX_IS_SYNC_REQ(state, type);

        /* Determine whether to map DDI_DMA_STREAMING or DDI_DMA_CONSISTENT */
        flag = state->ts_cfg_profile->cp_streaming_consistent;

        /* Initialize the mailbox list entries */
        for (i = 0; i < mblist->mbl_list_sz; i++) {
                /* Allocate resources for the mailbox */
                status = tavor_rsrc_alloc(state, type, 1, TAVOR_SLEEP,
                    &rsrc);
                if (status != DDI_SUCCESS) {
                        /* Jump to cleanup and return error */
                        goto mboxlist_init_fail;
                }

                /* Save away the mailbox resource info */
                mblist->mbl_mbox[i].mb_rsrcptr  = rsrc;
                mblist->mbl_mbox[i].mb_addr     = rsrc->tr_addr;
                mblist->mbl_mbox[i].mb_acchdl   = rsrc->tr_acchdl;

                /*
                 * Get a PCI mapped address for each mailbox.  Note: this
                 * uses the ddi_dma_handle return from the resource
                 * allocation routine
                 */
                status = ddi_dma_addr_bind_handle(rsrc->tr_dmahdl, NULL,
                    rsrc->tr_addr, rsrc->tr_len, (DDI_DMA_RDWR | flag),
                    DDI_DMA_SLEEP, NULL, &dma_cookie, &dma_cookiecnt);
                if (status != DDI_SUCCESS) {
                        /* Jump to cleanup and return error */
                        tavor_rsrc_free(state, &rsrc);
                        goto mboxlist_init_fail;
                }

                /* Save away the mapped address for the mailbox */
                mblist->mbl_mbox[i].mb_mapaddr  = dma_cookie.dmac_laddress;

                /* Set sync flag appropriately */
                mblist->mbl_mbox[i].mb_sync     = sync;

                /* Make each entry point to the "next" and "prev" entries */
                mblist->mbl_mbox[i].mb_next     = i+1;
                mblist->mbl_mbox[i].mb_prev     = i-1;
                mblist->mbl_mbox[i].mb_indx     = i;
                mblist->mbl_num_alloc           = i + 1;
        }

        /* Make the "head" and "tail" entries point to each other */
        mblist->mbl_mbox[mblist->mbl_head_indx].mb_prev =
            mblist->mbl_tail_indx;
        mblist->mbl_mbox[mblist->mbl_tail_indx].mb_next =
            mblist->mbl_head_indx;

        return (DDI_SUCCESS);

mboxlist_init_fail:
        tavor_impl_mboxlist_fini(state, mblist);

        return (DDI_FAILURE);
}


/*
 * tavor_impl_mboxlist_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
static void
tavor_impl_mboxlist_fini(tavor_state_t *state, tavor_mboxlist_t *mblist)
{
        tavor_rsrc_t    *rsrc;
        int             i, status;

        /* Release the resources for each of the mailbox list entries */
        for (i = 0; i < mblist->mbl_num_alloc; i++) {
                rsrc = mblist->mbl_mbox[i].mb_rsrcptr;

                /*
                 * First, unbind the DMA memory for the mailbox
                 *
                 * Note: The only way ddi_dma_unbind_handle() currently
                 * can return an error is if the handle passed in is invalid.
                 * Since this should never happen, we choose to return void
                 * from this function!  If this does return an error,
                 * however, then we print a warning message to the console.
                 */
                status = ddi_dma_unbind_handle(rsrc->tr_dmahdl);
                if (status != DDI_SUCCESS) {
                        TAVOR_WARNING(state, "failed to unbind DMA mapping");
                        return;
                }

                /* Next, free the mailbox resource */
                tavor_rsrc_free(state, &rsrc);
        }

        /* Destroy the mailbox list mutex and cv */
        mutex_destroy(&mblist->mbl_lock);
        cv_destroy(&mblist->mbl_cv);

        /* Free up the memory for tracking the mailbox list */
        kmem_free(mblist->mbl_mbox, mblist->mbl_list_sz *
            sizeof (tavor_mbox_t));
}


/*
 * tavor_outstanding_cmd_alloc()
 *    Context: Can be called only from base context.
 */
static int
tavor_outstanding_cmd_alloc(tavor_state_t *state, tavor_cmd_t **cmd_ptr,
    uint_t cmd_wait)
{
        tavor_cmdlist_t *cmd_list;
        uint_t          next, prev, head;

        cmd_list = &state->ts_cmd_list;
        mutex_enter(&cmd_list->cml_lock);

        /* Ensure that outstanding commands are supported */
        ASSERT(cmd_list->cml_num_alloc != 0);

        /*
         * If the outstanding command list is empty, then wait (if
         * appropriate in the current context).  Otherwise, grab the
         * next available command.
         */
        while (cmd_list->cml_entries_free == 0) {
                /* No free commands */
                if (cmd_wait == TAVOR_NOSLEEP) {
                        mutex_exit(&cmd_list->cml_lock);
                        return (TAVOR_CMD_INSUFF_RSRC);
                }

                /*
                 * Wait (on cv) for a command to become free.  Note: Just
                 * as we do above in tavor_cmd_post(), we also have the
                 * "__lock_lint" here to workaround warlock.  Warlock doesn't
                 * know that other parts of the Tavor may occasionally call
                 * this routine while holding their own locks, so it complains
                 * about this cv_wait.  In reality, however, the rest of the
                 * driver never calls this routine with a lock held unless
                 * they pass TAVOR_CMD_NOSLEEP.
                 */
                cmd_list->cml_waiters++;
#ifndef __lock_lint
                cv_wait(&cmd_list->cml_cv, &cmd_list->cml_lock);
#endif
        }

        /* Grab the next available command from the list */
        head = cmd_list->cml_head_indx;
        *cmd_ptr = &cmd_list->cml_cmd[head];
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(**cmd_ptr))
        next = (*cmd_ptr)->cmd_next;
        prev = (*cmd_ptr)->cmd_prev;
        (*cmd_ptr)->cmd_status = TAVOR_CMD_INVALID_STATUS;

        /* Remove it from the command list */
        cmd_list->cml_cmd[next].cmd_prev = prev;
        cmd_list->cml_cmd[prev].cmd_next = next;
        cmd_list->cml_head_indx          = next;

        /* Update the "free" count and return */
        cmd_list->cml_entries_free--;

        mutex_exit(&cmd_list->cml_lock);

        return (TAVOR_CMD_SUCCESS);
}


/*
 * tavor_outstanding_cmd_free()
 *    Context: Can be called only from base context.
 */
static void
tavor_outstanding_cmd_free(tavor_state_t *state, tavor_cmd_t **cmd_ptr)
{
        tavor_cmdlist_t *cmd_list;
        uint_t          cmd_indx;

        cmd_list = &state->ts_cmd_list;
        mutex_enter(&cmd_list->cml_lock);

        /* Pull the "index" from command entry */
        cmd_indx = (*cmd_ptr)->cmd_indx;

        /*
         * If outstanding command list is not empty, then insert the entry.
         * Otherwise, this is the only entry.  So update the pointers
         * appropriately.
         */
        if (cmd_list->cml_entries_free++ != 0) {
                /* Update the current command */
                (*cmd_ptr)->cmd_next = cmd_list->cml_head_indx;
                (*cmd_ptr)->cmd_prev = cmd_list->cml_tail_indx;

                /* Update head and tail commands */
                cmd_list->cml_cmd[cmd_list->cml_head_indx].cmd_prev = cmd_indx;
                cmd_list->cml_cmd[cmd_list->cml_tail_indx].cmd_next = cmd_indx;

                /* Update tail index */
                cmd_list->cml_tail_indx = cmd_indx;

        } else {
                /* Update the current command */
                (*cmd_ptr)->cmd_next = cmd_indx;
                (*cmd_ptr)->cmd_prev = cmd_indx;

                /* Update head and tail indexes */
                cmd_list->cml_head_indx = cmd_indx;
                cmd_list->cml_tail_indx = cmd_indx;
        }

        /* If there are threads waiting, signal one of them */
        if (cmd_list->cml_waiters > 0) {
                cmd_list->cml_waiters--;
                cv_signal(&cmd_list->cml_cv);
        }

        /* Clear out the command entry pointer */
        *cmd_ptr = NULL;

        mutex_exit(&cmd_list->cml_lock);
}


/*
 * tavor_write_hcr()
 *    Context: Can be called from interrupt or base context.
 */
static int
tavor_write_hcr(tavor_state_t *state, tavor_cmd_post_t *cmdpost,
    uint16_t token)
{
        tavor_hw_hcr_t  *hcr;
        uint_t          status, count, countmax;
        uint64_t        hcrreg;

        /*
         * Grab the "HCR access" lock if the driver is not in
         * fastreboot. In fastreboot, this function is called
         * with the single thread but in high interrupt context
         * (so that this mutex lock cannot be used).
         */
#ifdef __lock_lint
        mutex_enter(&state->ts_cmd_regs.hcr_lock);
#else
        if (!TAVOR_IN_FASTREBOOT(state)) {
                mutex_enter(&state->ts_cmd_regs.hcr_lock);
        }
#endif

        hcr = state->ts_cmd_regs.hcr;

        /*
         * First, check the "go" bit to see if the previous hcr usage is
         * complete.  As long as it is set then we must continue to poll.
         */
        count    = 0;
        countmax = state->ts_cfg_profile->cp_cmd_poll_max;
        for (;;) {
                hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->cmd);

                /* If "go" bit is clear, then done */
                if ((hcrreg & TAVOR_HCR_CMD_GO_MASK) == 0) {
                        break;
                }
                /* Delay before polling the "go" bit again */
                drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);

                /*
                 * If we poll more than the maximum number of times, then
                 * return a "timeout" error.
                 */
                if (++count > countmax) {
#ifdef __lock_lint
                        mutex_exit(&state->ts_cmd_regs.hcr_lock);
#else
                        if (!TAVOR_IN_FASTREBOOT(state)) {
                                mutex_exit(&state->ts_cmd_regs.hcr_lock);
                        }
#endif
                        return (TAVOR_CMD_TIMEOUT);
                }
        }

        /* Write "inparam" as a 64-bit quantity */
        ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->in_param0,
            cmdpost->cp_inparm);

        /* Write "inmod" and 32-bits of "outparam" as 64-bit */
        hcrreg = ((uint64_t)cmdpost->cp_inmod << 32);
        hcrreg = hcrreg | (cmdpost->cp_outparm >> 32);
        ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->input_modifier,
            hcrreg);

        /* Write the other 32-bits of "outparam" and "token" as 64-bit */
        hcrreg = (cmdpost->cp_outparm << 32);
        hcrreg = hcrreg | ((uint32_t)token << TAVOR_HCR_TOKEN_SHIFT);
        ddi_put64(state->ts_reg_cmdhdl, (uint64_t *)&hcr->out_param1,
            hcrreg);

        /* Then setup the final hcrreg to hit doorbell (i.e. "go" bit) */
        hcrreg = TAVOR_HCR_CMD_GO_MASK;
        if (cmdpost->cp_flags == TAVOR_CMD_SLEEP_NOSPIN)
                hcrreg = hcrreg | TAVOR_HCR_CMD_E_MASK;
        hcrreg = hcrreg | (cmdpost->cp_opmod << TAVOR_HCR_CMD_OPMOD_SHFT);
        hcrreg = hcrreg | (cmdpost->cp_opcode);

        /* Write the doorbell to the HCR */
        ddi_put32(state->ts_reg_cmdhdl, &hcr->cmd, hcrreg);

        /*
         * In the SPIN case we read the HCR and check the "go" bit.  For the
         * NOSPIN case we do not have to poll, we simply release the HCR lock
         * and return.
         */
        if (cmdpost->cp_flags == TAVOR_CMD_NOSLEEP_SPIN) {
                count    = 0;
                countmax = state->ts_cfg_profile->cp_cmd_poll_max;

                for (;;) {
                        hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->cmd);

                        /* If "go" bit is clear, then done */
                        if ((hcrreg & TAVOR_HCR_CMD_GO_MASK) == 0) {
                                break;
                        }
                        /* Delay before polling the "go" bit again */
                        drv_usecwait(state->ts_cfg_profile->cp_cmd_poll_delay);

                        /*
                         * If we poll more than the maximum number of times,
                         * then return a "timeout" error.
                         */
                        if (++count > countmax) {
#ifdef __lock_lint
                                mutex_exit(&state-> ts_cmd_regs.hcr_lock);
#else
                                if (!TAVOR_IN_FASTREBOOT(state)) {
                                        mutex_exit(&state->
                                            ts_cmd_regs.hcr_lock);
                                }
#endif
                                return (TAVOR_CMD_TIMEOUT);
                        }
                }

                /* Pull out the "status" bits from the HCR */
                status = (hcrreg >> TAVOR_HCR_CMD_STATUS_SHFT);

                /*
                 * Read the "outparam" value.  Note: we have to read "outparam"
                 * as two separate 32-bit reads because the field in the HCR is
                 * not 64-bit aligned.
                 */
                hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->out_param0);
                cmdpost->cp_outparm = hcrreg << 32;
                hcrreg = ddi_get32(state->ts_reg_cmdhdl, &hcr->out_param1);
                cmdpost->cp_outparm |= hcrreg;

        /* NOSPIN */
        } else {
                status = TAVOR_CMD_SUCCESS;
        }

        /* Drop the "HCR access" lock */
#ifdef __lock_lint
        mutex_exit(&state->ts_cmd_regs.hcr_lock);
#else
        if (!TAVOR_IN_FASTREBOOT(state)) {
                mutex_exit(&state->ts_cmd_regs.hcr_lock);
        }
#endif

        return (status);
}


/*
 * tavor_outstanding_cmdlist_init()
 *    Context: Only called from attach() path context
 */
int
tavor_outstanding_cmdlist_init(tavor_state_t *state)
{
        uint_t          num_outstanding_cmds, head, tail;
        int             i;

        /*
         * Determine the number of the outstanding commands supported
         * by the Tavor device (obtained from the QUERY_FW command).  Note:
         * Because we handle both SLEEP and NOSLEEP cases around the tavor HCR,
         * we know that when an interrupt comes in it will be next on the
         * command register, and will at most have to wait one commands time.
         * We do not have to reserve an outstanding command here for
         * interrupts.
         */
        num_outstanding_cmds = (1 << state->ts_fw.log_max_cmd);

        /* Initialize the outstanding command list */
        state->ts_cmd_list.cml_list_sz   = num_outstanding_cmds;
        state->ts_cmd_list.cml_head_indx = 0;
        state->ts_cmd_list.cml_tail_indx = state->ts_cmd_list.cml_list_sz - 1;
        state->ts_cmd_list.cml_entries_free = state->ts_cmd_list.cml_list_sz;
        state->ts_cmd_list.cml_waiters   = 0;
        state->ts_cmd_list.cml_num_alloc = 0;

        /* Allocate the memory for the outstanding command list */
        if (num_outstanding_cmds) {
                state->ts_cmd_list.cml_cmd =
                    kmem_zalloc(state->ts_cmd_list.cml_list_sz *
                    sizeof (tavor_cmd_t), KM_SLEEP);
        }
        mutex_init(&state->ts_cmd_list.cml_lock, NULL, MUTEX_DRIVER,
            DDI_INTR_PRI(state->ts_intrmsi_pri));
        cv_init(&state->ts_cmd_list.cml_cv, NULL, CV_DRIVER, NULL);

        /* Initialize the individual outstanding command list entries */
        for (i = 0; i < state->ts_cmd_list.cml_list_sz; i++) {
                mutex_init(&state->ts_cmd_list.cml_cmd[i].cmd_comp_lock,
                    NULL, MUTEX_DRIVER, DDI_INTR_PRI(state->ts_intrmsi_pri));
                cv_init(&state->ts_cmd_list.cml_cmd[i].cmd_comp_cv, NULL,
                    CV_DRIVER, NULL);

                state->ts_cmd_list.cml_cmd[i].cmd_next  = i+1;
                state->ts_cmd_list.cml_cmd[i].cmd_prev  = i-1;
                state->ts_cmd_list.cml_cmd[i].cmd_indx  = i;
                state->ts_cmd_list.cml_num_alloc        = i + 1;
        }
        if (num_outstanding_cmds) {
                head = state->ts_cmd_list.cml_head_indx;
                tail = state->ts_cmd_list.cml_tail_indx;
                state->ts_cmd_list.cml_cmd[head].cmd_prev =
                    state->ts_cmd_list.cml_tail_indx;
                state->ts_cmd_list.cml_cmd[tail].cmd_next =
                    state->ts_cmd_list.cml_head_indx;
        }

        return (DDI_SUCCESS);
}


/*
 * tavor_outstanding_cmdlist_fini()
 *    Context: Only called from attach() and/or detach() path contexts
 */
void
tavor_outstanding_cmdlist_fini(tavor_state_t *state)
{
        int             i;

        /* Destroy the outstanding command list entries */
        for (i = 0; i < state->ts_cmd_list.cml_num_alloc; i++) {
                mutex_destroy(&state->ts_cmd_list.cml_cmd[i].cmd_comp_lock);
                cv_destroy(&state->ts_cmd_list.cml_cmd[i].cmd_comp_cv);
        }

        /* Destroy the lock (and cv) and free up memory for list */
        mutex_destroy(&state->ts_cmd_list.cml_lock);
        cv_destroy(&state->ts_cmd_list.cml_cv);
        if (state->ts_cmd_list.cml_num_alloc) {
                kmem_free(state->ts_cmd_list.cml_cmd,
                    state->ts_cmd_list.cml_list_sz * sizeof (tavor_cmd_t));
        }

}


/*
 * tavor_mbox_sync()
 */
static void
tavor_mbox_sync(tavor_mbox_t *mbox, uint_t offset, uint_t length,
    uint_t flag)
{
        ddi_dma_handle_t        dmahdl;
        int                     status;

        /* Determine if mailbox needs to be synced or not */
        if (mbox->mb_sync == 0) {
                return;
        }

        /* Get the DMA handle from mailbox */
        dmahdl = mbox->mb_rsrcptr->tr_dmahdl;

        /* Calculate offset into mailbox */
        status = ddi_dma_sync(dmahdl, (off_t)offset, (size_t)length, flag);
        if (status != DDI_SUCCESS) {
                return;
        }
}


/*
 * tavor_sys_en_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() path context)
 */
int
tavor_sys_en_cmd_post(tavor_state_t *state, uint_t flags,
    uint64_t *errorcode, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Setup and post the Tavor "SYS_EN" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = SYS_EN;
        cmd.cp_opmod    = flags;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                /*
                 * When the SYS_EN command fails, the "outparam" field may
                 * contain more detailed information about what caused the
                 * failure.
                 */
                *errorcode = cmd.cp_outparm;
        }

        return (status);
}


/*
 * tavor_sys_dis_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and/or detach() path contexts)
 */
int
tavor_sys_dis_cmd_post(tavor_state_t *state, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Setup and post the Tavor "SYS_DIS" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = SYS_DIS;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_init_hca_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() path context)
 */
int
tavor_init_hca_cmd_post(tavor_state_t *state,
    tavor_hw_initqueryhca_t *inithca, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size;
        int                     status, i;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "INIT_HCA" command into the mailbox */
        size = sizeof (tavor_hw_initqueryhca_t);
        for (i = 0; i < (size >> 3); i++) {
                data = ((uint64_t *)inithca)[i];
                ddi_put64(mbox_info.mbi_in->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post the Tavor "INIT_HCA" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = INIT_HCA;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_close_hca_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and/or detach() path contexts)
 */
int
tavor_close_hca_cmd_post(tavor_state_t *state, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Setup and post the Tavor "CLOSE_HCA" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = CLOSE_HCA;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_init_ib_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() path context)
 */
int
tavor_init_ib_cmd_post(tavor_state_t *state, tavor_hw_initib_t *initib,
    uint_t port, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size;
        int                     status, i;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "INIT_IB" command into the mailbox */
        size = sizeof (tavor_hw_initib_t);
        for (i = 0; i < (size >> 3); i++) {
                data = ((uint64_t *)initib)[i];
                ddi_put64(mbox_info.mbi_in->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post the Tavor "INIT_IB" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = INIT_IB;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_close_ib_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and/or detach() path contexts)
 */
int
tavor_close_ib_cmd_post(tavor_state_t *state, uint_t port, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Setup and post the Tavor "CLOSE_IB" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = CLOSE_IB;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_set_ib_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_set_ib_cmd_post(tavor_state_t *state, uint32_t capmask, uint_t port,
    uint_t reset_qkey, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "SET_IB" command into mailbox */
        ddi_put32(mbox_info.mbi_in->mb_acchdl,
            ((uint32_t *)mbox_info.mbi_in->mb_addr + 0), reset_qkey);
        ddi_put32(mbox_info.mbi_in->mb_acchdl,
            ((uint32_t *)mbox_info.mbi_in->mb_addr + 1), capmask);

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, TAVOR_CMD_SETIB_SZ,
            DDI_DMA_SYNC_FORDEV);

        /* Setup and post the Tavor "SET_IB" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = SET_IB;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_mod_stat_cfg_cmd_post()
 *    Context: Can be called only from attach() path
 */
int
tavor_mod_stat_cfg_cmd_post(tavor_state_t *state)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        tavor_hw_mod_stat_cfg_t *mod;
        uint64_t                data;
        uint_t                  size;
        int                     status, i;

        /*
         * "MOD_STAT_CFG" needs an INMBOX parameter, to specify what operations
         * to do.  However, at the point in time that we call this command, the
         * DDR has not yet been initialized, and all INMBOX'es are located in
         * DDR.  Because we want to call MOD_STAT_CFG before QUERY_DEVLIM is
         * called, and thus call it before DDR is setup, we simply use an
         * OUTMBOX memory location here as our INMBOX parameter.
         */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, TAVOR_NOSLEEP);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /*
         * Allocate on the heap our 'mod_stat_cfg' structure.  We want to
         * ideally move all of this on to the stack in the future, but this
         * works well for now.
         */
        mod = (tavor_hw_mod_stat_cfg_t *)kmem_zalloc(
            sizeof (tavor_hw_mod_stat_cfg_t), KM_SLEEP);

        /* Setup "MOD_STAT_CFG" settings */
        mod->srq_m      = 1;
        mod->srq        = state->ts_cfg_profile->cp_srq_enable;

        if (mod->srq) {
                mod->log_max_srq = state->ts_cfg_profile->cp_log_num_srq;
        } else {
                mod->log_max_srq = 0;
        }

        /* Copy the "MOD_STAT_CFG" command into the "In" mailbox */
        size = sizeof (tavor_hw_mod_stat_cfg_t);
        for (i = 0; i < (size >> 3); i++) {
                data = ((uint64_t *)mod)[i];
                ddi_put64(mbox_info.mbi_out->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_out->mb_addr + i), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post the Tavor "MOD_STAT_CFG" command */
        cmd.cp_inparm   = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = MOD_STAT_CFG;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = TAVOR_CMD_NOSLEEP_SPIN;
        status = tavor_cmd_post(state, &cmd);

        /* Free "MOD_STAT_CFG" struct */
        kmem_free(mod, sizeof (tavor_hw_mod_stat_cfg_t));

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_mad_ifc_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_mad_ifc_cmd_post(tavor_state_t *state, uint_t port,
    uint_t sleepflag, uint32_t *mad, uint32_t *resp)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint_t                  size;
        int                     status;

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the request MAD into the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        bcopy(mad, mbox_info.mbi_in->mb_addr, size);

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_CHECK;  /* Enable MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto mad_ifc_fail;
        }

        /* Sync the mailbox to read the results */
        tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);

        /* Copy the response MAD into "resp" */
        bcopy(mbox_info.mbi_out->mb_addr, resp, size);

mad_ifc_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_getportinfo_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_getportinfo_cmd_post(tavor_state_t *state, uint_t port,
    uint_t sleepflag, sm_portinfo_t *portinfo)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build the GetPortInfo request MAD in the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PORTINFO);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], port);
        for (i = 6; i < (size >> 2); i++) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK;  /* No MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getportinfo_fail;
        }

        /* Sync the mailbox to read the results */
        size = sizeof (sm_portinfo_t);
        tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
            size, DDI_DMA_SYNC_FORCPU);

        /*
         * Copy GetPortInfo response MAD into "portinfo".  Do any endian
         * swapping that may be necessary to flip any of the "portinfo"
         * fields
         */
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*portinfo))
        bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
            TAVOR_CMD_MADDATA_OFFSET), portinfo, size);
        TAVOR_GETPORTINFO_SWAP(portinfo);
        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*portinfo))

getportinfo_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_getnodeinfo_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and detach() path contexts)
 */
int
tavor_getnodeinfo_cmd_post(tavor_state_t *state, uint_t sleepflag,
    sm_nodeinfo_t *nodeinfo)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        /* Make sure we are called with the correct flag */
        ASSERT(sleepflag == TAVOR_CMD_NOSLEEP_SPIN);

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build the GetNodeInfo request MAD into the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_NODEINFO);
        for (i = 5; i < (size >> 2); i++) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = 1;  /* Get NodeInfo from port #1 */
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK;  /* No MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getnodeinfo_fail;
        }

        /* Sync the mailbox to read the results */
        size = sizeof (sm_nodeinfo_t);
        tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
            size, DDI_DMA_SYNC_FORCPU);

        /*
         * Copy GetNodeInfo response MAD into "nodeinfo".  Do any endian
         * swapping that may be necessary to flip any of the "nodeinfo"
         * fields
         */
        bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
            TAVOR_CMD_MADDATA_OFFSET), nodeinfo, size);
        TAVOR_GETNODEINFO_SWAP(nodeinfo);

getnodeinfo_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_getnodedesc_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and detach() path contexts)
 */
int
tavor_getnodedesc_cmd_post(tavor_state_t *state, uint_t sleepflag,
    sm_nodedesc_t *nodedesc)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build the GetNodeDesc request MAD into the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_NODEDESC);
        for (i = 5; i < (size >> 2); i++) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = 1;  /* Get NodeDesc from port #1 */
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK;  /* No MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getnodedesc_fail;
        }

        /* Sync the mailbox to read the results */
        size = sizeof (sm_nodedesc_t);
        tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
            size, DDI_DMA_SYNC_FORCPU);

        /* Copy GetNodeDesc response MAD into "nodedesc" */
        bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
            TAVOR_CMD_MADDATA_OFFSET), nodedesc, size);

getnodedesc_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_getguidinfo_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_getguidinfo_cmd_post(tavor_state_t *state, uint_t port,
    uint_t guidblock, uint_t sleepflag, sm_guidinfo_t *guidinfo)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build the GetGUIDInfo request MAD into the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_GUIDINFO);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], guidblock);
        for (i = 6; i < (size >> 2); i++) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK;  /* No MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getguidinfo_fail;
        }

        /* Sync the mailbox to read the results */
        size = sizeof (sm_guidinfo_t);
        tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
            size, DDI_DMA_SYNC_FORCPU);

        /*
         * Copy GetGUIDInfo response MAD into "guidinfo".  Do any endian
         * swapping that may be necessary to flip the "guidinfo" fields
         */
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*guidinfo))
        bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
            TAVOR_CMD_MADDATA_OFFSET), guidinfo, size);
        TAVOR_GETGUIDINFO_SWAP(guidinfo);
        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*guidinfo))

getguidinfo_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_getpkeytable_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_getpkeytable_cmd_post(tavor_state_t *state, uint_t port,
    uint_t pkeyblock, uint_t sleepflag, sm_pkey_table_t *pkeytable)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build the GetPkeyTable request MAD into the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0], TAVOR_CMD_MADHDR0);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PKEYTBLE);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], pkeyblock);
        for (i = 6; i < (size >> 2); i++) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[i], 0);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Tavor "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = MAD_IFC;
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK;  /* No MKey checking */
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getpkeytable_fail;
        }

        /* Sync the mailbox to read the results */
        size = sizeof (sm_pkey_table_t);
        tavor_mbox_sync(mbox_info.mbi_out, TAVOR_CMD_MADDATA_OFFSET,
            size, DDI_DMA_SYNC_FORCPU);

        /*
         * Copy GetPKeyTable response MAD into "pkeytable".  Do any endian
         * swapping that may be necessary to flip the "pkeytable" fields
         */
        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pkeytable))
        bcopy((void *)((uintptr_t)mbox_info.mbi_out->mb_addr +
            TAVOR_CMD_MADDATA_OFFSET), pkeytable, size);
        TAVOR_GETPKEYTABLE_SWAP(pkeytable);
        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*pkeytable))

getpkeytable_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_write_mtt_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_write_mtt_cmd_post(tavor_state_t *state, tavor_mbox_info_t *mbox_info,
    uint_t num_mtt, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        uint_t                  size;
        int                     status;

        /*
         * The WRITE_MTT command is unlike the other commands we use, in that
         * we have intentionally separated the mailbox allocation step from
         * the rest of the command posting steps.  At this point (when this
         * function is called) the "In" mailbox already contains all the MTT
         * entries to be copied into the Tavor tables (starting at offset
         * 0x10) _and_ the 64-bit address of the destination for the first
         * MTT entry in the MTT table.
         */

        /* Sync the mailbox for the device to read */
        size = (num_mtt << TAVOR_MTT_SIZE_SHIFT) + TAVOR_CMD_WRITEMTT_RSVD_SZ;
        tavor_mbox_sync(mbox_info->mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post Tavor "WRITE_MTT" command */
        cmd.cp_inparm   = mbox_info->mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = num_mtt;
        cmd.cp_opcode   = WRITE_MTT;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_sync_tpt_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_sync_tpt_cmd_post(tavor_state_t *state, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Setup and post the Tavor "SYNC_TPT" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = SYNC_TPT;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}

/*
 * tavor_map_eq_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *    (Currently called only from attach() and/or detach() path contexts)
 */
int
tavor_map_eq_cmd_post(tavor_state_t *state, uint_t map, uint_t eqcindx,
    uint64_t eqmapmask, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Setup and post Tavor "MAP_EQ" command */
        cmd.cp_inparm   = eqmapmask;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = eqcindx;
        if (map != TAVOR_CMD_MAP_EQ_EVT_MAP) {
                cmd.cp_inmod |= TAVOR_CMD_UNMAP_EQ_MASK;
        }
        cmd.cp_opcode   = MAP_EQ;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_resize_cq_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_resize_cq_cmd_post(tavor_state_t *state, tavor_hw_cqc_t *cqc,
    uint_t cqcindx, uint32_t *prod_indx, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size;
        int                     status, i;

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "RESIZE_CQ" command into mailbox */
        size = sizeof (tavor_hw_cqc_t);
        for (i = 0; i < (size >> 3); i++) {
                data = ((uint64_t *)cqc)[i];
                ddi_put64(mbox_info.mbi_in->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post Tavor "RESIZE_CQ" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = cqcindx;
        cmd.cp_opcode   = RESIZE_CQ;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /*
         * New "producer index" is returned in the upper 32 bits of
         * command "outparam"
         */
        *prod_indx = (cmd.cp_outparm >> 32);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_cmn_qp_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 *    This is the common function for posting all the various types of
 *    QP state transition related Tavor commands.  Since some of the
 *    commands differ from the others in the number (and type) of arguments
 *    that each require, this routine does checks based on opcode type
 *    (explained in more detail below).
 *
 * Note: This common function should be used only with the following
 *    opcodes: RTS2SQD_QP, TOERR_QP, TORST_QP, RST2INIT_QP, INIT2INIT_QP,
 *    INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP, SQD2RTS_QP, and SQERR2RTS_QP.
 */
int
tavor_cmn_qp_cmd_post(tavor_state_t *state, uint_t opcode,
    tavor_hw_qpc_t *qp, uint_t qpindx, uint32_t opmask,
    uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data, in_mapaddr, out_mapaddr;
        uint_t                  size, flags, opmod;
        int                     status, i;

        /*
         * Use the specified opcode type to set the appropriate parameters.
         * Specifically, we need to set in_mapaddr, out_mapaddr, flags, and
         * opmod (as necessary).  Setting these parameters may also require
         * us to allocate an "In" or "Out" mailbox depending on the command
         * type.
         */
        if (opcode == RTS2SQD_QP) {
                /*
                 * Note: For RTS-to-SendQueueDrain state transitions we
                 * always want to request the event generation from the
                 * hardware.  Though we may not notify the consumer of the
                 * drained event, the decision to forward (or not) is made
                 * later in the SQD event handler.
                 */
                flags = TAVOR_CMD_REQ_SQD_EVENT;

                /*
                 * The RTS2SQD_QP command uses no "In" or "Out" mailboxes (and
                 * has no special opcode modifiers).
                 */
                in_mapaddr  = 0;
                out_mapaddr = 0;
                opmod = 0;

        } else if (opcode == TOERR_QP) {
                /*
                 * The TOERR_QP command uses no "In" or "Out" mailboxes, has no
                 * special opcode modifiers, and takes no special flags.
                 */
                in_mapaddr  = 0;
                out_mapaddr = 0;
                opmod = 0;
                flags = 0;

        } else if (opcode == TORST_QP) {
                /*
                 * The TORST_QP command could take an "Out" mailbox, but we do
                 * not require it here.  It also does not takes any special
                 * flags.  It does however, take a TAVOR_CMD_DIRECT_TO_RESET
                 * opcode modifier, which indicates that the transition to
                 * reset should happen without first moving the QP through the
                 * Error state (and, hence, without generating any unnecessary
                 * "flushed-in-error" completions).
                 */
                in_mapaddr  = 0;
                out_mapaddr = 0;
                opmod = TAVOR_CMD_DIRECT_TO_RESET | TAVOR_CMD_NO_OUTMBOX;
                flags = 0;

        } else {
                /*
                 * All the other QP state transition commands (RST2INIT_QP,
                 * INIT2INIT_QP, INIT2RTR_QP, RTR2RTS_QP, RTS2RTS_QP,
                 * SQD2RTS_QP, and SQERR2RTS_QP) require an "In" mailbox.
                 * None of these require any special flags or opcode modifiers.
                 */
                mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
                status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
                if (status != TAVOR_CMD_SUCCESS) {
                        return (status);
                }
                in_mapaddr  = mbox_info.mbi_in->mb_mapaddr;
                out_mapaddr = 0;
                flags = 0;
                opmod = 0;

                /* Copy the Tavor command into the "In" mailbox */
                size = sizeof (tavor_hw_qpc_t);
                for (i = 0; i < (size >> 3); i++) {
                        data = ((uint64_t *)qp)[i];
                        ddi_put64(mbox_info.mbi_in->mb_acchdl,
                            ((uint64_t *)mbox_info.mbi_in->mb_addr + i + 1),
                            data);
                }
                ddi_put32(mbox_info.mbi_in->mb_acchdl,
                    ((uint32_t *)mbox_info.mbi_in->mb_addr), opmask);

                /*
                 * Sync the mailbox for the device to read.  We have to add
                 * eight bytes here to account for "opt_param_mask" and
                 * proper alignment.
                 */
                tavor_mbox_sync(mbox_info.mbi_in, 0, size + 8,
                    DDI_DMA_SYNC_FORDEV);
        }

        /* Setup and post Tavor QP state transition command */
        cmd.cp_inparm   = in_mapaddr;
        cmd.cp_outparm  = out_mapaddr;
        cmd.cp_inmod    = qpindx | flags;
        cmd.cp_opcode   = opcode;
        cmd.cp_opmod    = opmod;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /*
         * If we allocated a mailbox (either an "In" or an "Out") above,
         * then free it now before returning.
         */
        if ((opcode != RTS2SQD_QP) && (opcode != TOERR_QP) &&
            (opcode != TORST_QP)) {
                /* Free the mailbox */
                tavor_mbox_free(state, &mbox_info);
        }

        return (status);
}


/*
 * tavor_cmn_query_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 *    This is the common function for posting all the various types of
 *    Tavor query commands.  All Tavor query commands require an "Out"
 *    mailbox to be allocated for the resulting queried data.
 *
 * Note: This common function should be used only with the following
 *    opcodes: QUERY_DEV_LIM, QUERY_FW, QUERY_DDR, QUERY_ADAPTER,
 *     QUERY_HCA, QUERY_MPT, QUERY_EQ, QUERY_CQ, and QUERY_QP.
 */
int
tavor_cmn_query_cmd_post(tavor_state_t *state, uint_t opcode,
    uint_t queryindx, void *query, uint_t size, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  offset;
        int                     status, i;

        /* Get an "Out" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Setup and post the Tavor query command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = queryindx;
        cmd.cp_opcode   = opcode;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto cmn_query_fail;
        }

        /* Sync the mailbox to read the results */
        tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);

        /*
         * QUERY_QP is handled somewhat differently than the other query
         * commands.  For QUERY_QP, the actual queried data is offset into
         * the mailbox (by one 64-bit word).
         */
        offset = (opcode == QUERY_QP) ? 1 : 0;

        /* Copy query command results into "query" */
        for (i = 0; i < (size >> 3); i++) {
                data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_out->mb_addr + i + offset));
                ((uint64_t *)query)[i] = data;
        }

cmn_query_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_cmn_ownership_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 *    This is the common function for posting all the various types of
 *    Tavor HW/SW resource ownership commands.  Since some of the commands
 *    differ from the others in the direction of ownership change (i.e.
 *    from HW ownership to SW, or vice versa), they differ in the type of
 *    mailbox and specific handling that each requires.  This routine does
 *    certain checks based on opcode type to determine the direction of
 *    the transition and to correctly handle the request.
 *
 * Note: This common function should be used only with the following
 *    opcodes: HW2SW_MPT, HW2SW_EQ, HW2SW_CQ, SW2HW_MPT, SW2HW_EQ, and
 *    SW2HW_CQ
 */
int
tavor_cmn_ownership_cmd_post(tavor_state_t *state, uint_t opcode,
    void *hwrsrc, uint_t size, uint_t hwrsrcindx, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data, in_mapaddr, out_mapaddr;
        uint_t                  direction, opmod;
        int                     status, i;

        /*
         * Determine the direction of the ownership transfer based on the
         * provided opcode
         */
        if ((opcode == HW2SW_MPT) || (opcode == HW2SW_EQ) ||
            (opcode == HW2SW_CQ) || (opcode == HW2SW_SRQ)) {
                direction = TAVOR_CMD_RSRC_HW2SW;

        } else if ((opcode == SW2HW_MPT) || (opcode == SW2HW_EQ) ||
            (opcode == SW2HW_CQ) || (opcode == SW2HW_SRQ)) {
                direction = TAVOR_CMD_RSRC_SW2HW;

        } else {
                return (TAVOR_CMD_INVALID_STATUS);
        }

        /*
         * If hwrsrc is NULL then we do not allocate a mailbox.  This is used
         * in the case of memory deregister where the out mailbox is not
         * needed.  In the case of re-register, we do use the hwrsrc.
         *
         * Otherwise, If ownership transfer is going from hardware to software,
         * then allocate an "Out" mailbox.  This will be filled in later as a
         * result of the Tavor command.
         *
         * And if the ownership transfer is going from software to hardware,
         * then we need an "In" mailbox, and we need to fill it in and sync it
         * (if necessary).  Then the mailbox can be passed to the Tavor
         * firmware.
         *
         * For the HW2SW (dereg) case, we only use an out mbox if hwrsrc is !=
         * NULL.  This implies a re-reg, and the out mbox must be used.  If
         * hwrsrc is == NULL, then we can save some time and resources by not
         * using an out mbox at all.  We must set opmod to TAVOR_CMD_DO_OUTMBOX
         * and TAVOR_CMD_NO_OUTMBOX appropriately in this case.
         *
         * For the SW2HW (reg) case, no out mbox is possible.  We set opmod to
         * 0 anyway, but this field is not used in this case.
         */
        if (direction == TAVOR_CMD_RSRC_HW2SW) {
                if (hwrsrc != NULL) {
                        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
                        status = tavor_mbox_alloc(state, &mbox_info,
                            sleepflag);
                        if (status != TAVOR_CMD_SUCCESS) {
                                return (status);
                        }
                        in_mapaddr  = 0;
                        out_mapaddr = mbox_info.mbi_out->mb_mapaddr;
                        opmod = TAVOR_CMD_DO_OUTMBOX;
                } else {
                        in_mapaddr = 0;
                        out_mapaddr = 0;
                        opmod = TAVOR_CMD_NO_OUTMBOX;
                }
        } else {  /* TAVOR_CMD_RSRC_SW2HW */
                mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
                status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
                if (status != TAVOR_CMD_SUCCESS) {
                        return (status);
                }

                /* Copy the SW2HW ownership command into mailbox */
                for (i = 0; i < (size >> 3); i++) {
                        data = ((uint64_t *)hwrsrc)[i];
                        ddi_put64(mbox_info.mbi_in->mb_acchdl,
                            ((uint64_t *)mbox_info.mbi_in->mb_addr + i),
                            data);
                }

                /* Sync the mailbox for the device to read */
                tavor_mbox_sync(mbox_info.mbi_in, 0, size,
                    DDI_DMA_SYNC_FORDEV);

                in_mapaddr  = mbox_info.mbi_in->mb_mapaddr;
                out_mapaddr = 0;
                opmod = 0;
        }


        /* Setup and post the Tavor ownership command */
        cmd.cp_inparm   = in_mapaddr;
        cmd.cp_outparm  = out_mapaddr;
        cmd.cp_inmod    = hwrsrcindx;
        cmd.cp_opcode   = opcode;
        cmd.cp_opmod    = opmod;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto cmn_ownership_fail;
        }

        /*
         * As mentioned above, for HW2SW ownership transfers we need to
         * sync (if necessary) and copy out the resulting data from the
         * "Out" mailbox" (assuming the above command was successful).
         */
        if (direction == TAVOR_CMD_RSRC_HW2SW && hwrsrc != NULL) {
                /* Sync the mailbox to read the results */
                tavor_mbox_sync(mbox_info.mbi_out, 0, size,
                    DDI_DMA_SYNC_FORCPU);

                /* Copy HW2SW ownership command results into "hwrsrc" */
                for (i = 0; i < (size >> 3); i++) {
                        data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
                            ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
                        ((uint64_t *)hwrsrc)[i] = data;
                }
        }

cmn_ownership_fail:
        if (hwrsrc != NULL) {
                /* Free the mailbox */
                tavor_mbox_free(state, &mbox_info);
        }

        return (status);
}


/*
 * tavor_conf_special_qp_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_conf_special_qp_cmd_post(tavor_state_t *state, uint_t qpindx,
    uint_t qptype, uint_t sleepflag)
{
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Setup and post Tavor "CONF_SPECIAL_QP" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = qpindx;
        cmd.cp_opcode   = CONF_SPECIAL_QP;
        cmd.cp_opmod    = qptype;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        return (status);
}


/*
 * tavor_mgid_hash_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_mgid_hash_cmd_post(tavor_state_t *state, uint64_t mgid_h,
    uint64_t mgid_l, uint64_t *mgid_hash, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        int                     status;

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "MGID_HASH" command into mailbox */
        ddi_put64(mbox_info.mbi_in->mb_acchdl,
            ((uint64_t *)mbox_info.mbi_in->mb_addr + 0), mgid_h);
        ddi_put64(mbox_info.mbi_in->mb_acchdl,
            ((uint64_t *)mbox_info.mbi_in->mb_addr + 1), mgid_l);

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, TAVOR_CMD_MGIDHASH_SZ,
            DDI_DMA_SYNC_FORDEV);

        /* Setup and post the Tavor "MGID_HASH" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = 0;
        cmd.cp_opcode   = MGID_HASH;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* MGID hash value is returned in command "outparam" */
        *mgid_hash = cmd.cp_outparm;

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_read_mgm_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 * Note: It is assumed that the "mcg" parameter is actually a pointer to a
 *    "tavor_hw_mcg_t" struct and some number of "tavor_hw_mcg_qp_list_t"
 *    structs.  Combined size should be equal to result of TAVOR_MCGMEM_SZ()
 *    macro.
 */
int
tavor_read_mgm_cmd_post(tavor_state_t *state, tavor_hw_mcg_t *mcg,
    uint_t mcgindx, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size, hdrsz, qplistsz;
        int                     status, i;

        /* Get an "Out" mailbox for the results */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Setup and post Tavor "READ_MGM" command */
        cmd.cp_inparm   = 0;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = mcgindx;
        cmd.cp_opcode   = READ_MGM;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto read_mgm_fail;
        }

        /* Sync the mailbox to read the results */
        size = TAVOR_MCGMEM_SZ(state);
        tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);

        /* Copy the READ_MGM command results into "mcg" */
        hdrsz = sizeof (tavor_hw_mcg_t);
        for (i = 0; i < (hdrsz >> 3); i++) {
                data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_out->mb_addr + i));
                ((uint64_t *)mcg)[i] = data;
        }
        qplistsz = size - hdrsz;
        for (i = 0; i < (qplistsz >> 2); i++) {
                data = ddi_get32(mbox_info.mbi_out->mb_acchdl,
                    ((uint32_t *)mbox_info.mbi_out->mb_addr + i + 8));
                ((uint32_t *)mcg)[i + 8] = data;
        }

read_mgm_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}


/*
 * tavor_write_mgm_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 * Note: It is assumed that the "mcg" parameter is actually a pointer to a
 *    "tavor_hw_mcg_t" struct and some number of "tavor_hw_mcg_qp_list_t"
 *    structs.  Combined size should be equal to result of TAVOR_MCGMEM_SZ()
 *    macro.
 */
int
tavor_write_mgm_cmd_post(tavor_state_t *state, tavor_hw_mcg_t *mcg,
    uint_t mcgindx, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size, hdrsz, qplistsz;
        int                     status, i;

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "WRITE_MGM" command into mailbox */
        size  = TAVOR_MCGMEM_SZ(state);
        hdrsz = sizeof (tavor_hw_mcg_t);
        for (i = 0; i < (hdrsz >> 3); i++) {
                data = ((uint64_t *)mcg)[i];
                ddi_put64(mbox_info.mbi_in->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
        }
        qplistsz = size - hdrsz;
        for (i = 0; i < (qplistsz >> 2); i++) {
                data = ((uint32_t *)mcg)[i + 8];
                ddi_put32(mbox_info.mbi_in->mb_acchdl,
                    ((uint32_t *)mbox_info.mbi_in->mb_addr + i + 8), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post Tavor "WRITE_MGM" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = mcgindx;
        cmd.cp_opcode   = WRITE_MGM;
        cmd.cp_opmod    = 0;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);

}


/*
 * tavor_modify_mpt_cmd_post()
 *    Context: Can be called from interrupt or base context.
 */
int
tavor_modify_mpt_cmd_post(tavor_state_t *state, tavor_hw_mpt_t *mpt,
    uint_t mptindx, uint_t flags, uint_t sleepflag)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint_t                  size;
        int                     status, i;

        /* Get an "In" mailbox for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Copy the Tavor "MODIFY_MPT" command into mailbox */
        size = sizeof (tavor_hw_mpt_t);
        for (i = 0; i < (size >> 3); i++) {
                data = ((uint64_t *)mpt)[i];
                ddi_put64(mbox_info.mbi_in->mb_acchdl,
                    ((uint64_t *)mbox_info.mbi_in->mb_addr + i), data);
        }

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup and post Tavor "MODIFY_MPT" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = 0;
        cmd.cp_inmod    = mptindx;
        cmd.cp_opcode   = MODIFY_MPT;
        cmd.cp_opmod    = flags;
        cmd.cp_flags    = sleepflag;
        status = tavor_cmd_post(state, &cmd);

        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);

        return (status);
}

/*
 * tavor_getpefcntr_cmd_post()
 *    Context: Can be called from interrupt or base context.
 *
 * If reset is zero, read the performance counters of the specified port and
 * copy them into perfinfo.
 * If reset is non-zero reset the performance counters of the specified port.
 */
int
tavor_getperfcntr_cmd_post(tavor_state_t *state, uint_t port,
    uint_t sleepflag, tavor_hw_sm_perfcntr_t *perfinfo, int reset)
{
        tavor_mbox_info_t       mbox_info;
        tavor_cmd_post_t        cmd;
        uint64_t                data;
        uint32_t                *mbox;
        uint_t                  size;
        int                     status, i;

        bzero((void *)&cmd, sizeof (tavor_cmd_post_t));

        /* Get "In" and "Out" mailboxes for the command */
        mbox_info.mbi_alloc_flags = TAVOR_ALLOC_INMBOX | TAVOR_ALLOC_OUTMBOX;
        status = tavor_mbox_alloc(state, &mbox_info, sleepflag);
        if (status != TAVOR_CMD_SUCCESS) {
                return (status);
        }

        /* Build request MAD in the "In" mailbox */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        mbox = (uint32_t *)mbox_info.mbi_in->mb_addr;

        if (reset) {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
                    TAVOR_CMD_PERF_SET);
        } else {
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[0],
                    TAVOR_CMD_PERF_GET);
        }
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[1], TAVOR_CMD_MADHDR1);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[2], TAVOR_CMD_MADHDR2);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[3], TAVOR_CMD_MADHDR3);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[4], TAVOR_CMD_PERFCNTRS);
        ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[5], TAVOR_CMD_PERFATTR);

        if (reset) {
                /* reset counters for XmitData, RcvData, XmitPkts, RcvPkts */
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16],
                    ((port << 16) | 0xf000));

                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[22], 0);
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[23], 0);
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[24], 0);
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[25], 0);
        } else
                ddi_put32(mbox_info.mbi_in->mb_acchdl, &mbox[16], (port << 16));

        /* Sync the mailbox for the device to read */
        tavor_mbox_sync(mbox_info.mbi_in, 0, size, DDI_DMA_SYNC_FORDEV);

        /* Setup the Hermon "MAD_IFC" command */
        cmd.cp_inparm   = mbox_info.mbi_in->mb_mapaddr;
        cmd.cp_outparm  = mbox_info.mbi_out->mb_mapaddr;
        cmd.cp_inmod    = port;
        cmd.cp_opcode   = MAD_IFC;
        /* No MKey and BKey checking */
        cmd.cp_opmod    = TAVOR_CMD_MKEY_DONTCHECK | TAVOR_CMD_BKEY_DONTCHECK;
        cmd.cp_flags    = TAVOR_CMD_NOSLEEP_SPIN; /* NO SLEEP */
        status = tavor_cmd_post(state, &cmd);
        if (status != TAVOR_CMD_SUCCESS) {
                goto getperfinfo_fail;
        }

        /* Sync the mailbox to read the results */
        size = TAVOR_CMD_MAD_IFC_SIZE;
        tavor_mbox_sync(mbox_info.mbi_out, 0, size, DDI_DMA_SYNC_FORCPU);

        if (reset == 0) {
                size = sizeof (tavor_hw_sm_perfcntr_t); /* for the copy */
                /*
                 * Copy Perfcounters into "perfinfo".  We can discard the MAD
                 * header and the 8 Quadword reserved area of the PERM mgmt
                 * class MAD
                 */

                for (i = 0; i < size >> 3; i++) {
                        data = ddi_get64(mbox_info.mbi_out->mb_acchdl,
                            ((uint64_t *)mbox_info.mbi_out->mb_addr + i + 8));
                        ((uint64_t *)(void *)perfinfo)[i] = data;
                }
        }

getperfinfo_fail:
        /* Free the mailbox */
        tavor_mbox_free(state, &mbox_info);
        return (status);
}