root/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas_impl.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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
 * Copyright 2014 OmniTI Computer Consulting, Inc. All rights reserved.
 * Copyright (c) 2014, Tegile Systems Inc. All rights reserved.
 * Copyright (c) 2019, Joyent, Inc.
 * Copyright 2023 Racktop Systems, Inc.
 */

/*
 * Copyright (c) 2000 to 2010, LSI Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms of all code within
 * this file that is exclusively owned by LSI, with or without
 * modification, is permitted provided that, in addition to the CDDL 1.0
 * License requirements, the following conditions are met:
 *
 *    Neither the name of the author nor the names of its contributors may be
 *    used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/*
 * mptsas_impl - This file contains all the basic functions for communicating
 * to MPT based hardware.
 */

#if defined(lint) || defined(DEBUG)
#define MPTSAS_DEBUG
#endif

/*
 * standard header files
 */
#include <sys/note.h>
#include <sys/scsi/scsi.h>
#include <sys/pci.h>

#pragma pack(1)
#include <sys/scsi/adapters/mpi/mpi2_type.h>
#include <sys/scsi/adapters/mpi/mpi2.h>
#include <sys/scsi/adapters/mpi/mpi2_cnfg.h>
#include <sys/scsi/adapters/mpi/mpi2_init.h>
#include <sys/scsi/adapters/mpi/mpi2_ioc.h>
#include <sys/scsi/adapters/mpi/mpi2_sas.h>
#include <sys/scsi/adapters/mpi/mpi2_tool.h>
#pragma pack()

/*
 * private header files.
 */
#include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
#include <sys/scsi/adapters/mpt_sas/mptsas_smhba.h>

/*
 * FMA header files.
 */
#include <sys/fm/io/ddi.h>

/*
 *  prototypes
 */
static void mptsas_ioc_event_cmdq_add(mptsas_t *mpt, m_event_struct_t *cmd);
static void mptsas_ioc_event_cmdq_delete(mptsas_t *mpt, m_event_struct_t *cmd);
static m_event_struct_t *mptsas_ioc_event_find_by_cmd(mptsas_t *mpt,
    struct mptsas_cmd *cmd);

/*
 * Use to read Host interface registers.  May read up to 3 times for
 * certain IOC's due to chip bug where zero can be erroneously returned
 * when under high i/o load.
 */
uint32_t
mptsas_hirrd(mptsas_t *mpt, uint32_t *regaddr)
{
        int i, ntries;
        uint32_t ret;

        ntries = (mpt->m_is_sea_ioc == 1) ? 3 : 1;
        for (i = 0; i < ntries; i++) {
                if ((ret = ddi_get32(mpt->m_datap, regaddr)) != 0)
                        break;
        }
        return (ret);
}


/*
 * add ioc evnet cmd into the queue
 */
static void
mptsas_ioc_event_cmdq_add(mptsas_t *mpt, m_event_struct_t *cmd)
{
        if ((cmd->m_event_linkp = mpt->m_ioc_event_cmdq) == NULL) {
                mpt->m_ioc_event_cmdtail = &cmd->m_event_linkp;
                mpt->m_ioc_event_cmdq = cmd;
        } else {
                cmd->m_event_linkp = NULL;
                *(mpt->m_ioc_event_cmdtail) = cmd;
                mpt->m_ioc_event_cmdtail = &cmd->m_event_linkp;
        }
}

/*
 * remove specified cmd from the ioc event queue
 */
static void
mptsas_ioc_event_cmdq_delete(mptsas_t *mpt, m_event_struct_t *cmd)
{
        m_event_struct_t        *prev = mpt->m_ioc_event_cmdq;
        if (prev == cmd) {
                if ((mpt->m_ioc_event_cmdq = cmd->m_event_linkp) == NULL) {
                        mpt->m_ioc_event_cmdtail = &mpt->m_ioc_event_cmdq;
                }
                cmd->m_event_linkp = NULL;
                return;
        }
        while (prev != NULL) {
                if (prev->m_event_linkp == cmd) {
                        prev->m_event_linkp = cmd->m_event_linkp;
                        if (cmd->m_event_linkp == NULL) {
                                mpt->m_ioc_event_cmdtail = &prev->m_event_linkp;
                        }

                        cmd->m_event_linkp = NULL;
                        return;
                }
                prev = prev->m_event_linkp;
        }
}

static m_event_struct_t *
mptsas_ioc_event_find_by_cmd(mptsas_t *mpt, struct mptsas_cmd *cmd)
{
        m_event_struct_t        *ioc_cmd = NULL;

        ioc_cmd = mpt->m_ioc_event_cmdq;
        while (ioc_cmd != NULL) {
                if (&(ioc_cmd->m_event_cmd) == cmd) {
                        return (ioc_cmd);
                }
                ioc_cmd = ioc_cmd->m_event_linkp;
        }
        ioc_cmd = NULL;
        return (ioc_cmd);
}

void
mptsas_destroy_ioc_event_cmd(mptsas_t *mpt)
{
        m_event_struct_t        *ioc_cmd = NULL;
        m_event_struct_t        *ioc_cmd_tmp = NULL;
        ioc_cmd = mpt->m_ioc_event_cmdq;

        /*
         * because the IOC event queue is resource of per instance for driver,
         * it's not only ACK event commands used it, but also some others used
         * it. We need destroy all ACK event commands when IOC reset, but can't
         * disturb others.So we use filter to clear the ACK event cmd in ioc
         * event queue, and other requests should be reserved, and they would
         * be free by its owner.
         */
        while (ioc_cmd != NULL) {
                if (ioc_cmd->m_event_cmd.cmd_flags & CFLAG_CMDACK) {
                        NDBG20(("destroy!! remove Ack Flag ioc_cmd\n"));
                        if ((mpt->m_ioc_event_cmdq =
                            ioc_cmd->m_event_linkp) == NULL)
                                mpt->m_ioc_event_cmdtail =
                                    &mpt->m_ioc_event_cmdq;
                        ioc_cmd_tmp = ioc_cmd;
                        ioc_cmd = ioc_cmd->m_event_linkp;
                        kmem_free(ioc_cmd_tmp, M_EVENT_STRUCT_SIZE);
                } else {
                        /*
                         * it's not ack cmd, so continue to check next one
                         */

                        NDBG20(("destroy!! it's not Ack Flag, continue\n"));
                        ioc_cmd = ioc_cmd->m_event_linkp;
                }

        }
}

void
mptsas_start_config_page_access(mptsas_t *mpt, mptsas_cmd_t *cmd)
{
        pMpi2ConfigRequest_t    request;
        pMpi2SGESimple64_t      sge;
        struct scsi_pkt         *pkt = cmd->cmd_pkt;
        mptsas_config_request_t *config = pkt->pkt_ha_private;
        uint8_t                 direction;
        uint32_t                length, flagslength;
        uint64_t                request_desc;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Point to the correct message and clear it as well as the global
         * config page memory.
         */
        request = (pMpi2ConfigRequest_t)(mpt->m_req_frame +
            (mpt->m_req_frame_size * cmd->cmd_slot));
        bzero(request, mpt->m_req_frame_size);

        /*
         * Form the request message.
         */
        ddi_put8(mpt->m_acc_req_frame_hdl, &request->Function,
            MPI2_FUNCTION_CONFIG);
        ddi_put8(mpt->m_acc_req_frame_hdl, &request->Action, config->action);
        direction = MPI2_SGE_FLAGS_IOC_TO_HOST;
        length = 0;
        sge = (pMpi2SGESimple64_t)&request->PageBufferSGE;
        if (config->action == MPI2_CONFIG_ACTION_PAGE_HEADER) {
                if (config->page_type > MPI2_CONFIG_PAGETYPE_MASK) {
                        ddi_put8(mpt->m_acc_req_frame_hdl,
                            &request->Header.PageType,
                            MPI2_CONFIG_PAGETYPE_EXTENDED);
                        ddi_put8(mpt->m_acc_req_frame_hdl,
                            &request->ExtPageType, config->page_type);
                } else {
                        ddi_put8(mpt->m_acc_req_frame_hdl,
                            &request->Header.PageType, config->page_type);
                }
        } else {
                ddi_put8(mpt->m_acc_req_frame_hdl, &request->ExtPageType,
                    config->ext_page_type);
                ddi_put16(mpt->m_acc_req_frame_hdl, &request->ExtPageLength,
                    config->ext_page_length);
                ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageType,
                    config->page_type);
                ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageLength,
                    config->page_length);
                ddi_put8(mpt->m_acc_req_frame_hdl,
                    &request->Header.PageVersion, config->page_version);
                if ((config->page_type & MPI2_CONFIG_PAGETYPE_MASK) ==
                    MPI2_CONFIG_PAGETYPE_EXTENDED) {
                        length = config->ext_page_length * 4;
                } else {
                        length = config->page_length * 4;
                }

                if (config->action == MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
                        direction = MPI2_SGE_FLAGS_HOST_TO_IOC;
                }
                ddi_put32(mpt->m_acc_req_frame_hdl, &sge->Address.Low,
                    (uint32_t)cmd->cmd_dma_addr);
                ddi_put32(mpt->m_acc_req_frame_hdl, &sge->Address.High,
                    (uint32_t)(cmd->cmd_dma_addr >> 32));
        }
        ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageNumber,
            config->page_number);
        ddi_put32(mpt->m_acc_req_frame_hdl, &request->PageAddress,
            config->page_address);
        flagslength = ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT |
            MPI2_SGE_FLAGS_END_OF_BUFFER |
            MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
            MPI2_SGE_FLAGS_SYSTEM_ADDRESS |
            MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
            direction |
            MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
        flagslength |= length;
        ddi_put32(mpt->m_acc_req_frame_hdl, &sge->FlagsLength, flagslength);

        (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0,
            DDI_DMA_SYNC_FORDEV);
        request_desc = (cmd->cmd_slot << 16) +
            MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
        cmd->cmd_rfm = 0;
        MPTSAS_START_CMD(mpt, request_desc);
        if ((mptsas_check_dma_handle(mpt->m_dma_req_frame_hdl) !=
            DDI_SUCCESS) ||
            (mptsas_check_acc_handle(mpt->m_acc_req_frame_hdl) !=
            DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
        }
}

int
mptsas_access_config_page(mptsas_t *mpt, uint8_t action, uint8_t page_type,
    uint8_t page_number, uint32_t page_address, int (*callback) (mptsas_t *,
    caddr_t, ddi_acc_handle_t, uint16_t, uint32_t, va_list), ...)
{
        va_list                 ap;
        ddi_dma_attr_t          attrs;
        ddi_dma_cookie_t        cookie;
        ddi_acc_handle_t        accessp;
        size_t                  len = 0;
        mptsas_config_request_t config;
        int                     rval = DDI_SUCCESS, config_flags = 0;
        mptsas_cmd_t            *cmd;
        struct scsi_pkt         *pkt;
        pMpi2ConfigReply_t      reply;
        uint16_t                iocstatus = 0;
        uint32_t                iocloginfo;
        caddr_t                 page_memp;
        boolean_t               free_dma = B_FALSE;

        va_start(ap, callback);
        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get a command from the pool.
         */
        if ((rval = (mptsas_request_from_pool(mpt, &cmd, &pkt))) == -1) {
                mptsas_log(mpt, CE_NOTE, "command pool is full for config "
                    "page request");
                rval = DDI_FAILURE;
                goto page_done;
        }
        config_flags |= MPTSAS_REQUEST_POOL_CMD;

        bzero((caddr_t)cmd, sizeof (*cmd));
        bzero((caddr_t)pkt, scsi_pkt_size());
        bzero((caddr_t)&config, sizeof (config));

        /*
         * Save the data for this request to be used in the call to start the
         * config header request.
         */
        config.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
        config.page_type = page_type;
        config.page_number = page_number;
        config.page_address = page_address;

        /*
         * Form a blank cmd/pkt to store the acknowledgement message
         */
        pkt->pkt_ha_private     = (opaque_t)&config;
        pkt->pkt_flags          = FLAG_HEAD;
        pkt->pkt_time           = 60;
        cmd->cmd_pkt            = pkt;
        cmd->cmd_flags          = CFLAG_CMDIOC | CFLAG_CONFIG;

        /*
         * Save the config header request message in a slot.
         */
        if (mptsas_save_cmd(mpt, cmd) == TRUE) {
                cmd->cmd_flags |= CFLAG_PREPARED;
                mptsas_start_config_page_access(mpt, cmd);
        } else {
                mptsas_waitq_add(mpt, cmd);
        }

        /*
         * If this is a request for a RAID info page, or any page called during
         * the RAID info page request, poll because these config page requests
         * are nested.  Poll to avoid data corruption due to one page's data
         * overwriting the outer page request's data.  This can happen when
         * the mutex is released in cv_wait.
         */
        if ((page_type == MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG) ||
            (page_type == MPI2_CONFIG_PAGETYPE_RAID_VOLUME) ||
            (page_type == MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK)) {
                (void) mptsas_poll(mpt, cmd, pkt->pkt_time * 1000);
        } else {
                while ((cmd->cmd_flags & CFLAG_FINISHED) == 0) {
                        cv_wait(&mpt->m_config_cv, &mpt->m_mutex);
                }
        }

        /*
         * Check if the header request completed without timing out
         */
        if (cmd->cmd_flags & CFLAG_TIMEOUT) {
                config_flags |= MPTSAS_CMD_TIMEOUT;
                mptsas_log(mpt, CE_WARN, "config header request timeout");
                rval = DDI_FAILURE;
                goto page_done;
        }

        /*
         * cmd_rfm points to the reply message if a reply was given.  Check the
         * IOCStatus to make sure everything went OK with the header request.
         */
        if (cmd->cmd_rfm) {
                config_flags |= MPTSAS_ADDRESS_REPLY;
                (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0,
                    DDI_DMA_SYNC_FORCPU);
                reply = (pMpi2ConfigReply_t)(mpt->m_reply_frame + (cmd->cmd_rfm
                    - (mpt->m_reply_frame_dma_addr & 0xffffffffu)));
                config.page_type = ddi_get8(mpt->m_acc_reply_frame_hdl,
                    &reply->Header.PageType);
                config.page_number = ddi_get8(mpt->m_acc_reply_frame_hdl,
                    &reply->Header.PageNumber);
                config.page_length = ddi_get8(mpt->m_acc_reply_frame_hdl,
                    &reply->Header.PageLength);
                config.page_version = ddi_get8(mpt->m_acc_reply_frame_hdl,
                    &reply->Header.PageVersion);
                config.ext_page_type = ddi_get8(mpt->m_acc_reply_frame_hdl,
                    &reply->ExtPageType);
                config.ext_page_length = ddi_get16(mpt->m_acc_reply_frame_hdl,
                    &reply->ExtPageLength);

                iocstatus = ddi_get16(mpt->m_acc_reply_frame_hdl,
                    &reply->IOCStatus);
                iocloginfo = ddi_get32(mpt->m_acc_reply_frame_hdl,
                    &reply->IOCLogInfo);

                if (iocstatus) {
                        NDBG13(("mptsas_access_config_page header: "
                            "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
                            iocloginfo));
                        rval = DDI_FAILURE;
                        goto page_done;
                }

                if ((config.page_type & MPI2_CONFIG_PAGETYPE_MASK) ==
                    MPI2_CONFIG_PAGETYPE_EXTENDED)
                        len = (config.ext_page_length * 4);
                else
                        len = (config.page_length * 4);

        }

        if (pkt->pkt_reason == CMD_RESET) {
                mptsas_log(mpt, CE_WARN, "ioc reset abort config header "
                    "request");
                rval = DDI_FAILURE;
                goto page_done;
        }

        /*
         * Put the reply frame back on the free queue, increment the free
         * index, and write the new index to the free index register.  But only
         * if this reply is an ADDRESS reply.
         */
        if (config_flags & MPTSAS_ADDRESS_REPLY) {
                ddi_put32(mpt->m_acc_free_queue_hdl,
                    &((uint32_t *)(void *)mpt->m_free_queue)[mpt->m_free_index],
                    cmd->cmd_rfm);
                (void) ddi_dma_sync(mpt->m_dma_free_queue_hdl, 0, 0,
                    DDI_DMA_SYNC_FORDEV);
                if (++mpt->m_free_index == mpt->m_free_queue_depth) {
                        mpt->m_free_index = 0;
                }
                ddi_put32(mpt->m_datap, &mpt->m_reg->ReplyFreeHostIndex,
                    mpt->m_free_index);
                config_flags &= (~MPTSAS_ADDRESS_REPLY);
        }

        /*
         * Allocate DMA buffer here.  Store the info regarding this buffer in
         * the cmd struct so that it can be used for this specific command and
         * de-allocated after the command completes.  The size of the reply
         * will not be larger than the reply frame size.
         */
        attrs = mpt->m_msg_dma_attr;
        attrs.dma_attr_sgllen = 1;
        attrs.dma_attr_granular = (uint32_t)len;

        if (mptsas_dma_addr_create(mpt, attrs,
            &cmd->cmd_dmahandle, &accessp, &page_memp,
            len, &cookie) == FALSE) {
                rval = DDI_FAILURE;
                mptsas_log(mpt, CE_WARN,
                    "mptsas_dma_addr_create(len=0x%x) failed", (int)len);
                goto page_done;
        }
        /* NOW we can safely call mptsas_dma_addr_destroy(). */
        free_dma = B_TRUE;

        cmd->cmd_dma_addr = cookie.dmac_laddress;
        bzero(page_memp, len);

        /*
         * Save the data for this request to be used in the call to start the
         * config page read
         */
        config.action = action;
        config.page_address = page_address;

        /*
         * Re-use the cmd that was used to get the header.  Reset some of the
         * values.
         */
        bzero((caddr_t)pkt, scsi_pkt_size());
        pkt->pkt_ha_private     = (opaque_t)&config;
        pkt->pkt_flags          = FLAG_HEAD;
        pkt->pkt_time           = 60;
        cmd->cmd_flags          = CFLAG_PREPARED | CFLAG_CMDIOC | CFLAG_CONFIG;

        /*
         * Send the config page request.  cmd is re-used from header request.
         */
        mptsas_start_config_page_access(mpt, cmd);

        /*
         * If this is a request for a RAID info page, or any page called during
         * the RAID info page request, poll because these config page requests
         * are nested.  Poll to avoid data corruption due to one page's data
         * overwriting the outer page request's data.  This can happen when
         * the mutex is released in cv_wait.
         */
        if ((page_type == MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG) ||
            (page_type == MPI2_CONFIG_PAGETYPE_RAID_VOLUME) ||
            (page_type == MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK)) {
                (void) mptsas_poll(mpt, cmd, pkt->pkt_time * 1000);
        } else {
                while ((cmd->cmd_flags & CFLAG_FINISHED) == 0) {
                        cv_wait(&mpt->m_config_cv, &mpt->m_mutex);
                }
        }

        /*
         * Check if the request completed without timing out
         */
        if (cmd->cmd_flags & CFLAG_TIMEOUT) {
                config_flags |= MPTSAS_CMD_TIMEOUT;
                mptsas_log(mpt, CE_WARN, "config page request timeout");
                rval = DDI_FAILURE;
                goto page_done;
        }

        /*
         * cmd_rfm points to the reply message if a reply was given.  The reply
         * frame and the config page are returned from this function in the
         * param list.
         */
        if (cmd->cmd_rfm) {
                config_flags |= MPTSAS_ADDRESS_REPLY;
                (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0,
                    DDI_DMA_SYNC_FORCPU);
                (void) ddi_dma_sync(cmd->cmd_dmahandle, 0, 0,
                    DDI_DMA_SYNC_FORCPU);
                reply = (pMpi2ConfigReply_t)(mpt->m_reply_frame + (cmd->cmd_rfm
                    - (mpt->m_reply_frame_dma_addr & 0xffffffffu)));
                iocstatus = ddi_get16(mpt->m_acc_reply_frame_hdl,
                    &reply->IOCStatus);
                iocstatus = MPTSAS_IOCSTATUS(iocstatus);
                iocloginfo = ddi_get32(mpt->m_acc_reply_frame_hdl,
                    &reply->IOCLogInfo);
        }

        if (callback(mpt, page_memp, accessp, iocstatus, iocloginfo, ap)) {
                rval = DDI_FAILURE;
                goto page_done;
        }

        mptsas_fma_check(mpt, cmd);
        /*
         * Check the DMA/ACC handles and then free the DMA buffer.
         */
        if ((mptsas_check_dma_handle(cmd->cmd_dmahandle) != DDI_SUCCESS) ||
            (mptsas_check_acc_handle(accessp) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
        }

        if (pkt->pkt_reason == CMD_TRAN_ERR) {
                mptsas_log(mpt, CE_WARN, "config fma error");
                rval = DDI_FAILURE;
                goto page_done;
        }
        if (pkt->pkt_reason == CMD_RESET) {
                mptsas_log(mpt, CE_WARN, "ioc reset abort config request");
                rval = DDI_FAILURE;
                goto page_done;
        }

page_done:
        va_end(ap);
        /*
         * Put the reply frame back on the free queue, increment the free
         * index, and write the new index to the free index register.  But only
         * if this reply is an ADDRESS reply.
         */
        if (config_flags & MPTSAS_ADDRESS_REPLY) {
                ddi_put32(mpt->m_acc_free_queue_hdl,
                    &((uint32_t *)(void *)mpt->m_free_queue)[mpt->m_free_index],
                    cmd->cmd_rfm);
                (void) ddi_dma_sync(mpt->m_dma_free_queue_hdl, 0, 0,
                    DDI_DMA_SYNC_FORDEV);
                if (++mpt->m_free_index == mpt->m_free_queue_depth) {
                        mpt->m_free_index = 0;
                }
                ddi_put32(mpt->m_datap, &mpt->m_reg->ReplyFreeHostIndex,
                    mpt->m_free_index);
        }

        if (free_dma)
                mptsas_dma_addr_destroy(&cmd->cmd_dmahandle, &accessp);

        if (cmd && (cmd->cmd_flags & CFLAG_PREPARED)) {
                mptsas_remove_cmd(mpt, cmd);
                config_flags &= (~MPTSAS_REQUEST_POOL_CMD);
        }
        if (config_flags & MPTSAS_REQUEST_POOL_CMD)
                mptsas_return_to_pool(mpt, cmd);

        if (config_flags & MPTSAS_CMD_TIMEOUT) {
                mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET;
                if ((mptsas_restart_ioc(mpt)) == DDI_FAILURE) {
                        mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed");
                }
        }

        return (rval);
}

int
mptsas_send_config_request_msg(mptsas_t *mpt, uint8_t action, uint8_t pagetype,
    uint32_t pageaddress, uint8_t pagenumber, uint8_t pageversion,
    uint8_t pagelength, uint32_t SGEflagslength, uint64_t SGEaddress)
{
        pMpi2ConfigRequest_t    config;
        int                     send_numbytes;

        bzero(mpt->m_hshk_memp, sizeof (MPI2_CONFIG_REQUEST));
        config = (pMpi2ConfigRequest_t)mpt->m_hshk_memp;
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Function, MPI2_FUNCTION_CONFIG);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Action, action);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageNumber, pagenumber);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageType, pagetype);
        ddi_put32(mpt->m_hshk_acc_hdl, &config->PageAddress, pageaddress);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageVersion, pageversion);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageLength, pagelength);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.FlagsLength, SGEflagslength);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.u.Address64.Low, SGEaddress);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.u.Address64.High,
            SGEaddress >> 32);
        send_numbytes = sizeof (MPI2_CONFIG_REQUEST);

        /*
         * Post message via handshake
         */
        if (mptsas_send_handshake_msg(mpt, (caddr_t)config, send_numbytes,
            mpt->m_hshk_acc_hdl)) {
                return (-1);
        }
        return (0);
}

int
mptsas_send_extended_config_request_msg(mptsas_t *mpt, uint8_t action,
    uint8_t extpagetype, uint32_t pageaddress, uint8_t pagenumber,
    uint8_t pageversion, uint16_t extpagelength,
    uint32_t SGEflagslength, uint64_t SGEaddress)
{
        pMpi2ConfigRequest_t    config;
        int                     send_numbytes;

        bzero(mpt->m_hshk_memp, sizeof (MPI2_CONFIG_REQUEST));
        config = (pMpi2ConfigRequest_t)mpt->m_hshk_memp;
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Function, MPI2_FUNCTION_CONFIG);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Action, action);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageNumber, pagenumber);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageType,
            MPI2_CONFIG_PAGETYPE_EXTENDED);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->ExtPageType, extpagetype);
        ddi_put32(mpt->m_hshk_acc_hdl, &config->PageAddress, pageaddress);
        ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageVersion, pageversion);
        ddi_put16(mpt->m_hshk_acc_hdl, &config->ExtPageLength, extpagelength);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.FlagsLength, SGEflagslength);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.u.Address64.Low, SGEaddress);
        ddi_put32(mpt->m_hshk_acc_hdl,
            &config->PageBufferSGE.MpiSimple.u.Address64.High,
            SGEaddress >> 32);
        send_numbytes = sizeof (MPI2_CONFIG_REQUEST);

        /*
         * Post message via handshake
         */
        if (mptsas_send_handshake_msg(mpt, (caddr_t)config, send_numbytes,
            mpt->m_hshk_acc_hdl)) {
                return (-1);
        }
        return (0);
}

int
mptsas_ioc_wait_for_response(mptsas_t *mpt)
{
        int     polls = 0;

        while ((mptsas_hirrd(mpt,
            &mpt->m_reg->HostInterruptStatus) & MPI2_HIS_IOP_DOORBELL_STATUS)) {
                drv_usecwait(1000);
                if (polls++ > 60000) {
                        return (-1);
                }
        }
        return (0);
}

int
mptsas_ioc_wait_for_doorbell(mptsas_t *mpt)
{
        int     polls = 0;

        while ((mptsas_hirrd(mpt,
            &mpt->m_reg->HostInterruptStatus) & MPI2_HIM_DIM) == 0) {
                drv_usecwait(1000);
                if (polls++ > 300000) {
                        return (-1);
                }
        }
        return (0);
}

int
mptsas_send_handshake_msg(mptsas_t *mpt, caddr_t memp, int numbytes,
    ddi_acc_handle_t accessp)
{
        int     i;

        /*
         * clean pending doorbells
         */
        ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0);
        ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell,
            ((MPI2_FUNCTION_HANDSHAKE << MPI2_DOORBELL_FUNCTION_SHIFT) |
            ((numbytes / 4) << MPI2_DOORBELL_ADD_DWORDS_SHIFT)));

        if (mptsas_ioc_wait_for_doorbell(mpt)) {
                NDBG19(("mptsas_send_handshake failed.  Doorbell not ready\n"));
                return (-1);
        }

        /*
         * clean pending doorbells again
         */
        ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0);

        if (mptsas_ioc_wait_for_response(mpt)) {
                NDBG19(("mptsas_send_handshake failed.  Doorbell not "
                    "cleared\n"));
                return (-1);
        }

        /*
         * post handshake message
         */
        for (i = 0; (i < numbytes / 4); i++, memp += 4) {
                ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell,
                    ddi_get32(accessp, (uint32_t *)((void *)(memp))));
                if (mptsas_ioc_wait_for_response(mpt)) {
                        NDBG19(("mptsas_send_handshake failed posting "
                            "message\n"));
                        return (-1);
                }
        }

        if (mptsas_check_acc_handle(mpt->m_datap) != DDI_SUCCESS) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                ddi_fm_acc_err_clear(mpt->m_datap, DDI_FME_VER0);
                return (-1);
        }

        return (0);
}

int
mptsas_get_handshake_msg(mptsas_t *mpt, caddr_t memp, int numbytes,
    ddi_acc_handle_t accessp)
{
        int             i, totalbytes, bytesleft;
        uint16_t        val;

        /*
         * wait for doorbell
         */
        if (mptsas_ioc_wait_for_doorbell(mpt)) {
                NDBG19(("mptsas_get_handshake failed.  Doorbell not ready\n"));
                return (-1);
        }

        /*
         * get first 2 bytes of handshake message to determine how much
         * data we will be getting
         */
        for (i = 0; i < 2; i++, memp += 2) {
                val = (mptsas_hirrd(mpt,
                    &mpt->m_reg->Doorbell) & MPI2_DOORBELL_DATA_MASK);
                ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0);
                if (mptsas_ioc_wait_for_doorbell(mpt)) {
                        NDBG19(("mptsas_get_handshake failure getting initial"
                            " data\n"));
                        return (-1);
                }
                ddi_put16(accessp, (uint16_t *)((void *)(memp)), val);
                if (i == 1) {
                        totalbytes = (val & 0xFF) * 2;
                }
        }

        /*
         * If we are expecting less bytes than the message wants to send
         * we simply save as much as we expected and then throw out the rest
         * later
         */
        if (totalbytes > (numbytes / 2)) {
                bytesleft = ((numbytes / 2) - 2);
        } else {
                bytesleft = (totalbytes - 2);
        }

        /*
         * Get the rest of the data
         */
        for (i = 0; i < bytesleft; i++, memp += 2) {
                val = (mptsas_hirrd(mpt,
                    &mpt->m_reg->Doorbell) & MPI2_DOORBELL_DATA_MASK);
                ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0);
                if (mptsas_ioc_wait_for_doorbell(mpt)) {
                        NDBG19(("mptsas_get_handshake failure getting"
                            " main data\n"));
                        return (-1);
                }
                ddi_put16(accessp, (uint16_t *)((void *)(memp)), val);
        }

        /*
         * Sometimes the device will send more data than is expected
         * This data is not used by us but needs to be cleared from
         * ioc doorbell.  So we just read the values and throw
         * them out.
         */
        if (totalbytes > (numbytes / 2)) {
                for (i = (numbytes / 2); i < totalbytes; i++) {
                        val = (mptsas_hirrd(mpt, &mpt->m_reg->Doorbell) &
                            MPI2_DOORBELL_DATA_MASK);
                        ddi_put32(mpt->m_datap,
                            &mpt->m_reg->HostInterruptStatus, 0);
                        if (mptsas_ioc_wait_for_doorbell(mpt)) {
                                NDBG19(("mptsas_get_handshake failure getting "
                                    "extra garbage data\n"));
                                return (-1);
                        }
                }
        }

        ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0);

        if (mptsas_check_acc_handle(mpt->m_datap) != DDI_SUCCESS) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                ddi_fm_acc_err_clear(mpt->m_datap, DDI_FME_VER0);
                return (-1);
        }

        return (0);
}

int
mptsas_kick_start(mptsas_t *mpt)
{
        int             polls = 0;
        uint32_t        diag_reg, ioc_state, saved_HCB_size;

        /*
         * Start a hard reset.  Write magic number and wait 500 mSeconds.
         */
        MPTSAS_ENABLE_DRWE(mpt);
        drv_usecwait(500000);

        /*
         * Read the current Diag Reg and save the Host Controlled Boot size.
         */
        diag_reg = mptsas_hirrd(mpt, &mpt->m_reg->HostDiagnostic);
        saved_HCB_size = mptsas_hirrd(mpt, &mpt->m_reg->HCBSize);

        /*
         * Set Reset Adapter bit and wait 50 mSeconds.
         */
        diag_reg |= MPI2_DIAG_RESET_ADAPTER;
        ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg);
        drv_usecwait(50000);

        /*
         * Poll, waiting for Reset Adapter bit to clear.  300 Seconds max
         * (600000 * 500 = 300,000,000 uSeconds, 300 seconds).
         * If no more adapter (all FF's), just return failure.
         */
        for (polls = 0; polls < 600000; polls++) {
                diag_reg = mptsas_hirrd(mpt, &mpt->m_reg->HostDiagnostic);
                if (diag_reg == 0xFFFFFFFF) {
                        mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                        ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
                        return (DDI_FAILURE);
                }
                if (!(diag_reg & MPI2_DIAG_RESET_ADAPTER)) {
                        break;
                }
                drv_usecwait(500);
        }
        if (polls == 600000) {
                mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
                return (DDI_FAILURE);
        }

        /*
         * Check if adapter is in Host Boot Mode.  If so, restart adapter
         * assuming the HCB points to good FW.
         * Set BootDeviceSel to HCDW (Host Code and Data Window).
         */
        if (diag_reg & MPI2_DIAG_HCB_MODE) {
                diag_reg &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
                diag_reg |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
                ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg);

                /*
                 * Re-enable the HCDW.
                 */
                ddi_put32(mpt->m_datap, &mpt->m_reg->HCBSize,
                    (saved_HCB_size | MPI2_HCB_SIZE_HCB_ENABLE));
        }

        /*
         * Restart the adapter.
         */
        diag_reg &= ~MPI2_DIAG_HOLD_IOC_RESET;
        ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg);

        /*
         * Disable writes to the Host Diag register.
         */
        ddi_put32(mpt->m_datap, &mpt->m_reg->WriteSequence,
            MPI2_WRSEQ_FLUSH_KEY_VALUE);

        /*
         * Wait 60 seconds max for FW to come to ready state.
         */
        for (polls = 0; polls < 60000; polls++) {
                ioc_state = mptsas_hirrd(mpt, &mpt->m_reg->Doorbell);
                if (ioc_state == 0xFFFFFFFF) {
                        mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                        ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
                        return (DDI_FAILURE);
                }
                if ((ioc_state & MPI2_IOC_STATE_MASK) ==
                    MPI2_IOC_STATE_READY) {
                        break;
                }
                drv_usecwait(1000);
        }
        if (polls == 60000) {
                mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
                return (DDI_FAILURE);
        }

        /*
         * Clear the ioc ack events queue.
         */
        mptsas_destroy_ioc_event_cmd(mpt);

        return (DDI_SUCCESS);
}

int
mptsas_ioc_reset(mptsas_t *mpt, int first_time)
{
        int             polls = 0;
        uint32_t        reset_msg;
        uint32_t        ioc_state;

        ioc_state = mptsas_hirrd(mpt, &mpt->m_reg->Doorbell);
        /*
         * If chip is already in ready state then there is nothing to do.
         */
        if (ioc_state == MPI2_IOC_STATE_READY) {
                return (MPTSAS_NO_RESET);
        }
        /*
         * If the chip is already operational, we just need to send
         * it a message unit reset to put it back in the ready state
         */
        if (ioc_state & MPI2_IOC_STATE_OPERATIONAL) {
                /*
                 * If the first time, try MUR anyway, because we haven't even
                 * queried the card for m_event_replay and other capabilities.
                 * Other platforms do it this way, we can still do a hard
                 * reset if we need to, MUR takes less time than a full
                 * adapter reset, and there are reports that some HW
                 * combinations will lock up when receiving a hard reset.
                 */
                if ((first_time || mpt->m_event_replay) &&
                    (mpt->m_softstate & MPTSAS_SS_MSG_UNIT_RESET)) {
                        mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET;
                        reset_msg = MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET;
                        ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell,
                            (reset_msg << MPI2_DOORBELL_FUNCTION_SHIFT));
                        if (mptsas_ioc_wait_for_response(mpt)) {
                                NDBG19(("mptsas_ioc_reset failure sending "
                                    "message_unit_reset\n"));
                                goto hard_reset;
                        }

                        /*
                         * Wait no more than 60 seconds for chip to become
                         * ready.
                         */
                        while ((mptsas_hirrd(mpt, &mpt->m_reg->Doorbell) &
                            MPI2_IOC_STATE_READY) == 0x0) {
                                drv_usecwait(1000);
                                if (polls++ > 60000) {
                                        goto hard_reset;
                                }
                        }

                        /*
                         * Save the last reset mode done on IOC which will be
                         * helpful while resuming from suspension.
                         */
                        mpt->m_softstate |= MPTSAS_DID_MSG_UNIT_RESET;

                        /*
                         * the message unit reset would do reset operations
                         * clear reply and request queue, so we should clear
                         * ACK event cmd.
                         */
                        mptsas_destroy_ioc_event_cmd(mpt);
                        return (MPTSAS_SUCCESS_MUR);
                }
        }
hard_reset:
        mpt->m_softstate &= ~MPTSAS_DID_MSG_UNIT_RESET;
        if (mptsas_kick_start(mpt) == DDI_FAILURE) {
                mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
                return (MPTSAS_RESET_FAIL);
        }
        return (MPTSAS_SUCCESS_HARDRESET);
}


int
mptsas_request_from_pool(mptsas_t *mpt, mptsas_cmd_t **cmd,
    struct scsi_pkt **pkt)
{
        m_event_struct_t        *ioc_cmd = NULL;

        ioc_cmd = kmem_zalloc(M_EVENT_STRUCT_SIZE, KM_SLEEP);
        if (ioc_cmd == NULL) {
                return (DDI_FAILURE);
        }
        ioc_cmd->m_event_linkp = NULL;
        mptsas_ioc_event_cmdq_add(mpt, ioc_cmd);
        *cmd = &(ioc_cmd->m_event_cmd);
        *pkt = &(ioc_cmd->m_event_pkt);

        return (DDI_SUCCESS);
}

void
mptsas_return_to_pool(mptsas_t *mpt, mptsas_cmd_t *cmd)
{
        m_event_struct_t        *ioc_cmd = NULL;

        ioc_cmd = mptsas_ioc_event_find_by_cmd(mpt, cmd);
        if (ioc_cmd == NULL) {
                return;
        }

        mptsas_ioc_event_cmdq_delete(mpt, ioc_cmd);
        kmem_free(ioc_cmd, M_EVENT_STRUCT_SIZE);
        ioc_cmd = NULL;
}

/*
 * NOTE: We should be able to queue TM requests in the controller to make this
 * a lot faster.  If resetting all targets, for example, we can load the hi
 * priority queue with its limit and the controller will reply as they are
 * completed.  This way, we don't have to poll for one reply at a time.
 * Think about enhancing this later.
 */
int
mptsas_ioc_task_management(mptsas_t *mpt, int task_type, uint16_t dev_handle,
    int lun, uint8_t *reply, uint32_t reply_size, int mode)
{
        /*
         * In order to avoid allocating variables on the stack,
         * we make use of the pre-existing mptsas_cmd_t and
         * scsi_pkt which are included in the mptsas_t which
         * is passed to this routine.
         */

        pMpi2SCSITaskManagementRequest_t        task;
        int                                     rval = FALSE;
        mptsas_cmd_t                            *cmd;
        struct scsi_pkt                         *pkt;
        mptsas_slots_t                          *slots = mpt->m_active;
        uint64_t                                request_desc, i;
        pMPI2DefaultReply_t                     reply_msg;

        /*
         * Can't start another task management routine.
         */
        if (slots->m_slot[MPTSAS_TM_SLOT(mpt)] != NULL) {
                mptsas_log(mpt, CE_WARN, "Can only start 1 task management"
                    " command at a time\n");
                return (FALSE);
        }

        cmd = &(mpt->m_event_task_mgmt.m_event_cmd);
        pkt = &(mpt->m_event_task_mgmt.m_event_pkt);

        bzero((caddr_t)cmd, sizeof (*cmd));
        bzero((caddr_t)pkt, scsi_pkt_size());

        pkt->pkt_cdbp           = (opaque_t)&cmd->cmd_cdb[0];
        pkt->pkt_scbp           = (opaque_t)&cmd->cmd_scb;
        pkt->pkt_ha_private     = (opaque_t)cmd;
        pkt->pkt_flags          = (FLAG_NOINTR | FLAG_HEAD);
        pkt->pkt_time           = 60;
        pkt->pkt_address.a_target = dev_handle;
        pkt->pkt_address.a_lun = (uchar_t)lun;
        cmd->cmd_pkt            = pkt;
        cmd->cmd_scblen         = 1;
        cmd->cmd_flags          = CFLAG_TM_CMD;
        cmd->cmd_slot           = MPTSAS_TM_SLOT(mpt);

        slots->m_slot[MPTSAS_TM_SLOT(mpt)] = cmd;

        /*
         * Store the TM message in memory location corresponding to the TM slot
         * number.
         */
        task = (pMpi2SCSITaskManagementRequest_t)(mpt->m_req_frame +
            (mpt->m_req_frame_size * cmd->cmd_slot));
        bzero(task, mpt->m_req_frame_size);

        /*
         * form message for requested task
         */
        mptsas_init_std_hdr(mpt->m_acc_req_frame_hdl, task, dev_handle, lun, 0,
            MPI2_FUNCTION_SCSI_TASK_MGMT);

        /*
         * Set the task type
         */
        ddi_put8(mpt->m_acc_req_frame_hdl, &task->TaskType, task_type);

        /*
         * Send TM request using High Priority Queue.
         */
        (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0,
            DDI_DMA_SYNC_FORDEV);
        request_desc = (cmd->cmd_slot << 16) +
            MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
        MPTSAS_START_CMD(mpt, request_desc);
        rval = mptsas_poll(mpt, cmd, MPTSAS_POLL_TIME);

        if (pkt->pkt_reason == CMD_INCOMPLETE)
                rval = FALSE;

        /*
         * If a reply frame was used and there is a reply buffer to copy the
         * reply data into, copy it.  If this fails, log a message, but don't
         * fail the TM request.
         */
        if (cmd->cmd_rfm && reply) {
                (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0,
                    DDI_DMA_SYNC_FORCPU);
                reply_msg = (pMPI2DefaultReply_t)
                    (mpt->m_reply_frame + (cmd->cmd_rfm -
                    (mpt->m_reply_frame_dma_addr & 0xffffffffu)));
                if (reply_size > sizeof (MPI2_SCSI_TASK_MANAGE_REPLY)) {
                        reply_size = sizeof (MPI2_SCSI_TASK_MANAGE_REPLY);
                }
                mutex_exit(&mpt->m_mutex);
                for (i = 0; i < reply_size; i++) {
                        if (ddi_copyout((uint8_t *)reply_msg + i, reply + i, 1,
                            mode)) {
                                mptsas_log(mpt, CE_WARN, "failed to copy out "
                                    "reply data for TM request");
                                break;
                        }
                }
                mutex_enter(&mpt->m_mutex);
        }

        /*
         * clear the TM slot before returning
         */
        slots->m_slot[MPTSAS_TM_SLOT(mpt)] = NULL;

        /*
         * If we lost our task management command
         * we need to reset the ioc
         */
        if (rval == FALSE) {
                mptsas_log(mpt, CE_WARN, "mptsas_ioc_task_management failed "
                    "try to reset ioc to recovery!");
                mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET;
                if (mptsas_restart_ioc(mpt)) {
                        mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed");
                        rval = FAILED;
                }
        }

        return (rval);
}

/*
 * Complete firmware download frame for v2.0 cards.
 */
static void
mptsas_uflash2(pMpi2FWDownloadRequest fwdownload,
    ddi_acc_handle_t acc_hdl, uint32_t size, uint8_t type,
    ddi_dma_cookie_t flsh_cookie)
{
        pMpi2FWDownloadTCSGE_t  tcsge;
        pMpi2SGESimple64_t      sge;
        uint32_t                flagslength;

        ddi_put8(acc_hdl, &fwdownload->Function,
            MPI2_FUNCTION_FW_DOWNLOAD);
        ddi_put8(acc_hdl, &fwdownload->ImageType, type);
        ddi_put8(acc_hdl, &fwdownload->MsgFlags,
            MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT);
        ddi_put32(acc_hdl, &fwdownload->TotalImageSize, size);

        tcsge = (pMpi2FWDownloadTCSGE_t)&fwdownload->SGL;
        ddi_put8(acc_hdl, &tcsge->ContextSize, 0);
        ddi_put8(acc_hdl, &tcsge->DetailsLength, 12);
        ddi_put8(acc_hdl, &tcsge->Flags, 0);
        ddi_put32(acc_hdl, &tcsge->ImageOffset, 0);
        ddi_put32(acc_hdl, &tcsge->ImageSize, size);

        sge = (pMpi2SGESimple64_t)(tcsge + 1);
        flagslength = size;
        flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT |
            MPI2_SGE_FLAGS_END_OF_BUFFER |
            MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
            MPI2_SGE_FLAGS_SYSTEM_ADDRESS |
            MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
            MPI2_SGE_FLAGS_HOST_TO_IOC |
            MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
        ddi_put32(acc_hdl, &sge->FlagsLength, flagslength);
        ddi_put32(acc_hdl, &sge->Address.Low,
            flsh_cookie.dmac_address);
        ddi_put32(acc_hdl, &sge->Address.High,
            (uint32_t)(flsh_cookie.dmac_laddress >> 32));
}

/*
 * Complete firmware download frame for v2.5 cards.
 */
static void
mptsas_uflash25(pMpi25FWDownloadRequest fwdownload,
    ddi_acc_handle_t acc_hdl, uint32_t size, uint8_t type,
    ddi_dma_cookie_t flsh_cookie)
{
        pMpi2IeeeSgeSimple64_t  sge;
        uint8_t                 flags;

        ddi_put8(acc_hdl, &fwdownload->Function,
            MPI2_FUNCTION_FW_DOWNLOAD);
        ddi_put8(acc_hdl, &fwdownload->ImageType, type);
        ddi_put8(acc_hdl, &fwdownload->MsgFlags,
            MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT);
        ddi_put32(acc_hdl, &fwdownload->TotalImageSize, size);

        ddi_put32(acc_hdl, &fwdownload->ImageOffset, 0);
        ddi_put32(acc_hdl, &fwdownload->ImageSize, size);

        sge = (pMpi2IeeeSgeSimple64_t)&fwdownload->SGL;
        flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
            MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR |
            MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
        ddi_put8(acc_hdl, &sge->Flags, flags);
        ddi_put32(acc_hdl, &sge->Length, size);
        ddi_put32(acc_hdl, &sge->Address.Low,
            flsh_cookie.dmac_address);
        ddi_put32(acc_hdl, &sge->Address.High,
            (uint32_t)(flsh_cookie.dmac_laddress >> 32));
}

static int mptsas_enable_mpi25_flashupdate = 0;

int
mptsas_update_flash(mptsas_t *mpt, caddr_t ptrbuffer, uint32_t size,
    uint8_t type, int mode)
{

        /*
         * In order to avoid allocating variables on the stack,
         * we make use of the pre-existing mptsas_cmd_t and
         * scsi_pkt which are included in the mptsas_t which
         * is passed to this routine.
         */

        ddi_dma_attr_t          flsh_dma_attrs;
        ddi_dma_cookie_t        flsh_cookie;
        ddi_dma_handle_t        flsh_dma_handle;
        ddi_acc_handle_t        flsh_accessp;
        caddr_t                 memp, flsh_memp;
        mptsas_cmd_t            *cmd;
        struct scsi_pkt         *pkt;
        int                     i;
        int                     rvalue = 0;
        uint64_t                request_desc;

        if (mpt->m_MPI25 && !mptsas_enable_mpi25_flashupdate) {
                /*
                 * The code is there but not tested yet.
                 * User has to know there are risks here.
                 */
                mptsas_log(mpt, CE_WARN, "mptsas_update_flash(): "
                    "Updating firmware through MPI 2.5 has not been "
                    "tested yet!\n"
                    "To enable set mptsas_enable_mpi25_flashupdate to 1\n");
                return (-1);
        } /* Otherwise, you pay your money and you take your chances. */

        if ((rvalue = (mptsas_request_from_pool(mpt, &cmd, &pkt))) == -1) {
                mptsas_log(mpt, CE_WARN, "mptsas_update_flash(): allocation "
                    "failed. event ack command pool is full\n");
                return (rvalue);
        }

        bzero((caddr_t)cmd, sizeof (*cmd));
        bzero((caddr_t)pkt, scsi_pkt_size());
        cmd->ioc_cmd_slot = (uint32_t)rvalue;

        /*
         * dynamically create a customized dma attribute structure
         * that describes the flash file.
         */
        flsh_dma_attrs = mpt->m_msg_dma_attr;
        flsh_dma_attrs.dma_attr_sgllen = 1;

        if (mptsas_dma_addr_create(mpt, flsh_dma_attrs, &flsh_dma_handle,
            &flsh_accessp, &flsh_memp, size, &flsh_cookie) == FALSE) {
                mptsas_log(mpt, CE_WARN,
                    "(unable to allocate dma resource.");
                mptsas_return_to_pool(mpt, cmd);
                return (-1);
        }

        bzero(flsh_memp, size);

        for (i = 0; i < size; i++) {
                (void) ddi_copyin(ptrbuffer + i, flsh_memp + i, 1, mode);
        }
        (void) ddi_dma_sync(flsh_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV);

        /*
         * form a cmd/pkt to store the fw download message
         */
        pkt->pkt_cdbp           = (opaque_t)&cmd->cmd_cdb[0];
        pkt->pkt_scbp           = (opaque_t)&cmd->cmd_scb;
        pkt->pkt_ha_private     = (opaque_t)cmd;
        pkt->pkt_flags          = FLAG_HEAD;
        pkt->pkt_time           = 60;
        cmd->cmd_pkt            = pkt;
        cmd->cmd_scblen         = 1;
        cmd->cmd_flags          = CFLAG_CMDIOC | CFLAG_FW_CMD;

        /*
         * Save the command in a slot
         */
        if (mptsas_save_cmd(mpt, cmd) == FALSE) {
                mptsas_dma_addr_destroy(&flsh_dma_handle, &flsh_accessp);
                mptsas_return_to_pool(mpt, cmd);
                return (-1);
        }

        /*
         * Fill in fw download message
         */
        ASSERT(cmd->cmd_slot != 0);
        memp = mpt->m_req_frame + (mpt->m_req_frame_size * cmd->cmd_slot);
        bzero(memp, mpt->m_req_frame_size);

        if (mpt->m_MPI25)
                mptsas_uflash25((pMpi25FWDownloadRequest)memp,
                    mpt->m_acc_req_frame_hdl, size, type, flsh_cookie);
        else
                mptsas_uflash2((pMpi2FWDownloadRequest)memp,
                    mpt->m_acc_req_frame_hdl, size, type, flsh_cookie);

        /*
         * Start command
         */
        (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0,
            DDI_DMA_SYNC_FORDEV);
        request_desc = (cmd->cmd_slot << 16) +
            MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
        cmd->cmd_rfm = 0;
        MPTSAS_START_CMD(mpt, request_desc);

        rvalue = 0;
        (void) cv_reltimedwait(&mpt->m_fw_cv, &mpt->m_mutex,
            drv_usectohz(60 * MICROSEC), TR_CLOCK_TICK);
        if (!(cmd->cmd_flags & CFLAG_FINISHED)) {
                mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET;
                if ((mptsas_restart_ioc(mpt)) == DDI_FAILURE) {
                        mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed");
                }
                rvalue = -1;
        }
        mptsas_remove_cmd(mpt, cmd);
        mptsas_dma_addr_destroy(&flsh_dma_handle, &flsh_accessp);

        return (rvalue);
}

static int
mptsas_sasdevpage_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        pMpi2SasDevicePage0_t   sasdevpage;
        int                     rval = DDI_SUCCESS, i;
        uint8_t                 *sas_addr = NULL;
        uint8_t                 tmp_sas_wwn[SAS_WWN_BYTE_SIZE];
        uint16_t                *devhdl, *bay_num, *enclosure;
        uint64_t                *sas_wwn;
        uint32_t                *dev_info;
        uint8_t                 *physport, *phynum;
        uint16_t                *pdevhdl, *io_flags;
        uint32_t                page_address;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_device_page0 "
                    "header: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        page_address = va_arg(ap, uint32_t);
        /*
         * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there
         * are no more pages.  If everything is OK up to this point but the
         * status is INVALID_PAGE, change rval to FAILURE and quit.  Also,
         * signal that device traversal is complete.
         */
        if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
                if ((page_address & MPI2_SAS_DEVICE_PGAD_FORM_MASK) ==
                    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) {
                        mpt->m_done_traverse_dev = 1;
                }
                rval = DDI_FAILURE;
                return (rval);
        }
        devhdl = va_arg(ap, uint16_t *);
        sas_wwn = va_arg(ap, uint64_t *);
        dev_info = va_arg(ap, uint32_t *);
        physport = va_arg(ap, uint8_t *);
        phynum = va_arg(ap, uint8_t *);
        pdevhdl = va_arg(ap, uint16_t *);
        bay_num = va_arg(ap, uint16_t *);
        enclosure = va_arg(ap, uint16_t *);
        io_flags = va_arg(ap, uint16_t *);

        sasdevpage = (pMpi2SasDevicePage0_t)page_memp;

        *dev_info = ddi_get32(accessp, &sasdevpage->DeviceInfo);
        *devhdl = ddi_get16(accessp, &sasdevpage->DevHandle);
        sas_addr = (uint8_t *)(&sasdevpage->SASAddress);
        for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) {
                tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i);
        }
        bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE);
        *sas_wwn = LE_64(*sas_wwn);
        *physport = ddi_get8(accessp, &sasdevpage->PhysicalPort);
        *phynum = ddi_get8(accessp, &sasdevpage->PhyNum);
        *pdevhdl = ddi_get16(accessp, &sasdevpage->ParentDevHandle);
        *bay_num = ddi_get16(accessp, &sasdevpage->Slot);
        *enclosure = ddi_get16(accessp, &sasdevpage->EnclosureHandle);
        *io_flags = ddi_get16(accessp, &sasdevpage->Flags);

        if (*io_flags & MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) {
                /*
                 * Leave a messages about FP cabability in the log.
                 */
                mptsas_log(mpt, CE_CONT,
                    "!w%016"PRIx64" FastPath Capable%s", *sas_wwn,
                    (*io_flags &
                    MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH)?
                    " and Enabled":" but Disabled");
        }

        return (rval);
}

/*
 * Request MPI configuration page SAS device page 0 to get DevHandle, device
 * info and SAS address.
 */
int
mptsas_get_sas_device_page0(mptsas_t *mpt, uint32_t page_address,
    uint16_t *dev_handle, uint64_t *sas_wwn, uint32_t *dev_info,
    uint8_t *physport, uint8_t *phynum, uint16_t *pdev_handle,
    uint16_t *bay_num, uint16_t *enclosure, uint16_t *io_flags)
{
        int rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0, page_address,
            mptsas_sasdevpage_0_cb, page_address, dev_handle, sas_wwn,
            dev_info, physport, phynum, pdev_handle,
            bay_num, enclosure, io_flags);

        return (rval);
}

static int
mptsas_sasexpdpage_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        pMpi2ExpanderPage0_t    expddevpage;
        int                     rval = DDI_SUCCESS, i;
        uint8_t                 *sas_addr = NULL;
        uint8_t                 tmp_sas_wwn[SAS_WWN_BYTE_SIZE];
        uint16_t                *devhdl;
        uint64_t                *sas_wwn;
        uint8_t                 physport;
        mptsas_phymask_t        *phymask;
        uint16_t                *pdevhdl;
        uint32_t                page_address;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page0 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        page_address = va_arg(ap, uint32_t);
        /*
         * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there
         * are no more pages.  If everything is OK up to this point but the
         * status is INVALID_PAGE, change rval to FAILURE and quit.  Also,
         * signal that device traversal is complete.
         */
        if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
                if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) ==
                    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) {
                        mpt->m_done_traverse_smp = 1;
                }
                rval = DDI_FAILURE;
                return (rval);
        }
        devhdl = va_arg(ap, uint16_t *);
        sas_wwn = va_arg(ap, uint64_t *);
        phymask = va_arg(ap, mptsas_phymask_t *);
        pdevhdl = va_arg(ap, uint16_t *);

        expddevpage = (pMpi2ExpanderPage0_t)page_memp;

        *devhdl = ddi_get16(accessp, &expddevpage->DevHandle);
        physport = ddi_get8(accessp, &expddevpage->PhysicalPort);
        *phymask = mptsas_physport_to_phymask(mpt, physport);
        *pdevhdl = ddi_get16(accessp, &expddevpage->ParentDevHandle);
        sas_addr = (uint8_t *)(&expddevpage->SASAddress);
        for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) {
                tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i);
        }
        bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE);
        *sas_wwn = LE_64(*sas_wwn);

        return (rval);
}

/*
 * Request MPI configuration page SAS device page 0 to get DevHandle, phymask
 * and SAS address.
 */
int
mptsas_get_sas_expander_page0(mptsas_t *mpt, uint32_t page_address,
    mptsas_smp_t *info)
{
        int                     rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER, 0, page_address,
            mptsas_sasexpdpage_0_cb, page_address, &info->m_devhdl,
            &info->m_addr.mta_wwn, &info->m_addr.mta_phymask, &info->m_pdevhdl);

        return (rval);
}

static int
mptsas_sasportpage_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        int     rval = DDI_SUCCESS, i;
        uint8_t *sas_addr = NULL;
        uint64_t *sas_wwn;
        uint8_t tmp_sas_wwn[SAS_WWN_BYTE_SIZE];
        uint8_t *portwidth;
        pMpi2SasPortPage0_t sasportpage;

        if (iocstatus != MPI2_IOCSTATUS_SUCCESS) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_port_page0 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        sas_wwn = va_arg(ap, uint64_t *);
        portwidth = va_arg(ap, uint8_t *);

        sasportpage = (pMpi2SasPortPage0_t)page_memp;
        sas_addr = (uint8_t *)(&sasportpage->SASAddress);
        for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) {
                tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i);
        }
        bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE);
        *sas_wwn = LE_64(*sas_wwn);
        *portwidth = ddi_get8(accessp, &sasportpage->PortWidth);
        return (rval);
}

/*
 * Request MPI configuration page SAS port page 0 to get initiator SAS address
 * and port width.
 */
int
mptsas_get_sas_port_page0(mptsas_t *mpt, uint32_t page_address,
    uint64_t *sas_wwn, uint8_t *portwidth)
{
        int rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_SAS_PORT, 0, page_address,
            mptsas_sasportpage_0_cb, sas_wwn, portwidth);

        return (rval);
}

static int
mptsas_sasiou_page_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        int rval = DDI_SUCCESS;
        pMpi2SasIOUnitPage0_t sasioupage0;
        int i, num_phys;
        uint32_t cpdi[MPTSAS_MAX_PHYS], *retrypage0, *readpage1;
        uint8_t port_flags;

        if (iocstatus != MPI2_IOCSTATUS_SUCCESS) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page0 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        readpage1 = va_arg(ap, uint32_t *);
        retrypage0 = va_arg(ap, uint32_t *);

        sasioupage0 = (pMpi2SasIOUnitPage0_t)page_memp;

        num_phys = ddi_get8(accessp, &sasioupage0->NumPhys);
        /*
         * ASSERT that the num_phys value in SAS IO Unit Page 0 is the same as
         * was initially set.  This should never change throughout the life of
         * the driver.  Note, due to cases where we've seen page zero have more
         * phys than the reported manufacturing information, we limit the number
         * of phys here to what we got from the manufacturing information.
         */
        ASSERT3U(num_phys, >=, mpt->m_num_phys);
        num_phys = mpt->m_num_phys;
        for (i = 0; i < num_phys; i++) {
                cpdi[i] = ddi_get32(accessp,
                    &sasioupage0->PhyData[i].
                    ControllerPhyDeviceInfo);
                port_flags = ddi_get8(accessp,
                    &sasioupage0->PhyData[i].PortFlags);
                mpt->m_phy_info[i].port_num =
                    ddi_get8(accessp,
                    &sasioupage0->PhyData[i].Port);
                mpt->m_phy_info[i].ctrl_devhdl =
                    ddi_get16(accessp, &sasioupage0->
                    PhyData[i].ControllerDevHandle);
                mpt->m_phy_info[i].attached_devhdl =
                    ddi_get16(accessp, &sasioupage0->
                    PhyData[i].AttachedDevHandle);
                mpt->m_phy_info[i].phy_device_type = cpdi[i];
                mpt->m_phy_info[i].port_flags = port_flags;

                if (port_flags & DISCOVERY_IN_PROGRESS) {
                        *retrypage0 = *retrypage0 + 1;
                        break;
                } else {
                        *retrypage0 = 0;
                }
                if (!(port_flags & AUTO_PORT_CONFIGURATION)) {
                        /*
                         * some PHY configuration described in
                         * SAS IO Unit Page1
                         */
                        *readpage1 = 1;
                }
        }

        return (rval);
}

static int
mptsas_sasiou_page_1_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        int rval = DDI_SUCCESS;
        pMpi2SasIOUnitPage1_t sasioupage1;
        int i, num_phys;
        uint32_t cpdi[MPTSAS_MAX_PHYS];
        uint8_t port_flags;

        if (iocstatus != MPI2_IOCSTATUS_SUCCESS) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page1 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }

        sasioupage1 = (pMpi2SasIOUnitPage1_t)page_memp;
        num_phys = ddi_get8(accessp, &sasioupage1->NumPhys);
        /*
         * ASSERT that the num_phys value in SAS IO Unit Page 0 is the same as
         * was initially set.  This should never change throughout the life of
         * the driver.  Note, due to cases where we've seen page zero have more
         * phys than the reported manufacturing information, we limit the number
         * of phys here to what we got from the manufacturing information.
         */
        ASSERT3U(num_phys, >=, mpt->m_num_phys);
        num_phys = mpt->m_num_phys;
        for (i = 0; i < num_phys; i++) {
                cpdi[i] = ddi_get32(accessp, &sasioupage1->PhyData[i].
                    ControllerPhyDeviceInfo);
                port_flags = ddi_get8(accessp,
                    &sasioupage1->PhyData[i].PortFlags);
                mpt->m_phy_info[i].port_num =
                    ddi_get8(accessp,
                    &sasioupage1->PhyData[i].Port);
                mpt->m_phy_info[i].port_flags = port_flags;
                mpt->m_phy_info[i].phy_device_type = cpdi[i];
        }
        return (rval);
}

/*
 * Read IO unit page 0 to get information for each PHY. If needed, Read IO Unit
 * page1 to update the PHY information.  This is the message passing method of
 * this function which should be called except during initialization.
 */
int
mptsas_get_sas_io_unit_page(mptsas_t *mpt)
{
        int rval = DDI_SUCCESS, state;
        uint32_t readpage1 = 0, retrypage0 = 0;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Now we cycle through the state machine.  Here's what happens:
         * 1. Read IO unit page 0 and set phy information
         * 2. See if Read IO unit page1 is needed because of port configuration
         * 3. Read IO unit page 1 and update phy information.
         */
        state = IOUC_READ_PAGE0;
        while (state != IOUC_DONE) {
                if (state == IOUC_READ_PAGE0) {
                        rval = mptsas_access_config_page(mpt,
                            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
                            MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0, 0,
                            mptsas_sasiou_page_0_cb, &readpage1,
                            &retrypage0);
                } else if (state == IOUC_READ_PAGE1) {
                        rval = mptsas_access_config_page(mpt,
                            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
                            MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 1, 0,
                            mptsas_sasiou_page_1_cb);
                }

                if (rval == DDI_SUCCESS) {
                        switch (state) {
                        case IOUC_READ_PAGE0:
                                /*
                                 * retry 30 times if discovery is in process
                                 */
                                if (retrypage0 && (retrypage0 < 30)) {
                                        drv_usecwait(1000 * 100);
                                        state = IOUC_READ_PAGE0;
                                        break;
                                } else if (retrypage0 == 30) {
                                        mptsas_log(mpt, CE_WARN,
                                            "!Discovery in progress, can't "
                                            "verify IO unit config, then "
                                            "after 30 times retry, give "
                                            "up!");
                                        state = IOUC_DONE;
                                        rval = DDI_FAILURE;
                                        break;
                                }

                                if (readpage1 == 0) {
                                        state = IOUC_DONE;
                                        rval = DDI_SUCCESS;
                                        break;
                                }

                                state = IOUC_READ_PAGE1;
                                break;

                        case IOUC_READ_PAGE1:
                                state = IOUC_DONE;
                                rval = DDI_SUCCESS;
                                break;
                        }
                } else {
                        return (rval);
                }
        }

        return (rval);
}

static int
mptsas_biospage_3_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        pMpi2BiosPage3_t        sasbiospage;
        int                     rval = DDI_SUCCESS;
        uint32_t                *bios_version;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_bios_page3 header: "
                    "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        bios_version = va_arg(ap, uint32_t *);
        sasbiospage = (pMpi2BiosPage3_t)page_memp;
        *bios_version = ddi_get32(accessp, &sasbiospage->BiosVersion);

        return (rval);
}

/*
 * Request MPI configuration page BIOS page 3 to get BIOS version.  Since all
 * other information in this page is not needed, just ignore it.
 */
int
mptsas_get_bios_page3(mptsas_t *mpt, uint32_t *bios_version)
{
        int rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_PAGETYPE_BIOS, 3,
            0, mptsas_biospage_3_cb, bios_version);

        return (rval);
}

/*
 * Read IO unit page 0 to get information for each PHY. If needed, Read IO Unit
 * page1 to update the PHY information.  This is the handshaking version of
 * this function, which should be called during initialization only.
 */
int
mptsas_get_sas_io_unit_page_hndshk(mptsas_t *mpt)
{
        ddi_dma_attr_t          recv_dma_attrs, page_dma_attrs;
        ddi_dma_cookie_t        page_cookie;
        ddi_dma_handle_t        recv_dma_handle, page_dma_handle;
        ddi_acc_handle_t        recv_accessp, page_accessp;
        pMpi2ConfigReply_t      configreply;
        pMpi2SasIOUnitPage0_t   sasioupage0;
        pMpi2SasIOUnitPage1_t   sasioupage1;
        int                     recv_numbytes;
        caddr_t                 recv_memp, page_memp;
        uint_t                  i, num_phys, start_phy = 0;
        int                     page0_size =
            sizeof (MPI2_CONFIG_PAGE_SASIOUNIT_0) +
            (sizeof (MPI2_SAS_IO_UNIT0_PHY_DATA) * (MPTSAS_MAX_PHYS - 1));
        int                     page1_size =
            sizeof (MPI2_CONFIG_PAGE_SASIOUNIT_1) +
            (sizeof (MPI2_SAS_IO_UNIT1_PHY_DATA) * (MPTSAS_MAX_PHYS - 1));
        uint32_t                flags_length;
        uint32_t                cpdi[MPTSAS_MAX_PHYS];
        uint32_t                readpage1 = 0, retrypage0 = 0;
        uint16_t                iocstatus;
        uint8_t                 port_flags, page_number, action;
        uint32_t                reply_size;
        uint_t                  state;
        int                     rval = DDI_FAILURE;
        boolean_t               free_recv = B_FALSE, free_page = B_FALSE;

        /*
         * We want to find a reply_size that's large enough for the page0 and
         * page1 sizes and resistant to increase in the number of phys.
         */
        reply_size = MAX(page0_size, page1_size);
        if (P2ROUNDUP(reply_size, 256) <= reply_size) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndsk: "
                    "cannot size reply size");
                goto cleanup;
        }

        /*
         * Initialize our "state machine".  This is a bit convoluted,
         * but it keeps us from having to do the ddi allocations numerous
         * times.
         */

        NDBG20(("mptsas_get_sas_io_unit_page_hndshk enter"));
        ASSERT(mutex_owned(&mpt->m_mutex));
        state = IOUC_READ_PAGE0;

        /*
         * dynamically create a customized dma attribute structure
         * that describes mpt's config reply page request structure.
         */
        recv_dma_attrs = mpt->m_msg_dma_attr;
        recv_dma_attrs.dma_attr_sgllen = 1;
        recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY));

        if (mptsas_dma_addr_create(mpt, recv_dma_attrs,
            &recv_dma_handle, &recv_accessp, &recv_memp,
            (sizeof (MPI2_CONFIG_REPLY)), NULL) == FALSE) {
                mptsas_log(mpt, CE_WARN,
                    "mptsas_get_sas_io_unit_page_hndshk: recv dma failed");
                goto cleanup;
        }
        /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */
        free_recv = B_TRUE;

        page_dma_attrs = mpt->m_msg_dma_attr;
        page_dma_attrs.dma_attr_sgllen = 1;
        page_dma_attrs.dma_attr_granular = reply_size;

        if (mptsas_dma_addr_create(mpt, page_dma_attrs,
            &page_dma_handle, &page_accessp, &page_memp,
            reply_size, &page_cookie) == FALSE) {
                mptsas_log(mpt, CE_WARN,
                    "mptsas_get_sas_io_unit_page_hndshk: page dma failed");
                goto cleanup;
        }
        /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */
        free_page = B_TRUE;

        /*
         * Now we cycle through the state machine.  Here's what happens:
         * 1. Read IO unit page 0 and set phy information
         * 2. See if Read IO unit page1 is needed because of port configuration
         * 3. Read IO unit page 1 and update phy information.
         */

        sasioupage0 = (pMpi2SasIOUnitPage0_t)page_memp;
        sasioupage1 = (pMpi2SasIOUnitPage1_t)page_memp;

        while (state != IOUC_DONE) {
                switch (state) {
                case IOUC_READ_PAGE0:
                        page_number = 0;
                        action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
                        flags_length = (uint32_t)page0_size;
                        flags_length |= ((uint32_t)(
                            MPI2_SGE_FLAGS_LAST_ELEMENT |
                            MPI2_SGE_FLAGS_END_OF_BUFFER |
                            MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
                            MPI2_SGE_FLAGS_SYSTEM_ADDRESS |
                            MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
                            MPI2_SGE_FLAGS_IOC_TO_HOST |
                            MPI2_SGE_FLAGS_END_OF_LIST) <<
                            MPI2_SGE_FLAGS_SHIFT);

                        break;

                case IOUC_READ_PAGE1:
                        page_number = 1;
                        action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
                        flags_length = (uint32_t)page1_size;
                        flags_length |= ((uint32_t)(
                            MPI2_SGE_FLAGS_LAST_ELEMENT |
                            MPI2_SGE_FLAGS_END_OF_BUFFER |
                            MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
                            MPI2_SGE_FLAGS_SYSTEM_ADDRESS |
                            MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
                            MPI2_SGE_FLAGS_IOC_TO_HOST |
                            MPI2_SGE_FLAGS_END_OF_LIST) <<
                            MPI2_SGE_FLAGS_SHIFT);

                        break;
                default:
                        break;
                }

                bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY));
                configreply = (pMpi2ConfigReply_t)recv_memp;
                recv_numbytes = sizeof (MPI2_CONFIG_REPLY);

                if (mptsas_send_extended_config_request_msg(mpt,
                    MPI2_CONFIG_ACTION_PAGE_HEADER,
                    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
                    0, page_number, 0, 0, 0, 0)) {
                        goto cleanup;
                }

                if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
                    recv_accessp)) {
                        goto cleanup;
                }

                iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus);
                iocstatus = MPTSAS_IOCSTATUS(iocstatus);

                if (iocstatus != MPI2_IOCSTATUS_SUCCESS) {
                        mptsas_log(mpt, CE_WARN,
                            "mptsas_get_sas_io_unit_page_hndshk: read page "
                            "header iocstatus = 0x%x", iocstatus);
                        goto cleanup;
                }

                if (action != MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
                        bzero(page_memp, reply_size);
                }

                if (mptsas_send_extended_config_request_msg(mpt, action,
                    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0, page_number,
                    ddi_get8(recv_accessp, &configreply->Header.PageVersion),
                    ddi_get16(recv_accessp, &configreply->ExtPageLength),
                    flags_length, page_cookie.dmac_laddress)) {
                        goto cleanup;
                }

                if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
                    recv_accessp)) {
                        goto cleanup;
                }

                iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus);
                iocstatus = MPTSAS_IOCSTATUS(iocstatus);

                if (iocstatus != MPI2_IOCSTATUS_SUCCESS) {
                        mptsas_log(mpt, CE_WARN,
                            "mptsas_get_sas_io_unit_page_hndshk: IO unit "
                            "config failed for action %d, iocstatus = 0x%x",
                            action, iocstatus);
                        goto cleanup;
                }

                switch (state) {
                case IOUC_READ_PAGE0:
                        if ((ddi_dma_sync(page_dma_handle, 0, 0,
                            DDI_DMA_SYNC_FORCPU)) != DDI_SUCCESS) {
                                goto cleanup;
                        }

                        num_phys = ddi_get8(page_accessp,
                            &sasioupage0->NumPhys);
                        if (num_phys > MPTSAS_MAX_PHYS) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "supported by HBA (%d) is more than max "
                                    "supported by driver (%d).  Driver will "
                                    "not attach.", num_phys,
                                    MPTSAS_MAX_PHYS);
                                rval = DDI_FAILURE;
                                goto cleanup;
                        }
                        if (num_phys > mpt->m_num_phys) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "reported by HBA SAS IO Unit Page 0 (%u) "
                                    "is greater than that reported by the "
                                    "manufacturing information (%u). Driver "
                                    "phy count limited to %u. Please contact "
                                    "the firmware vendor about this.", num_phys,
                                    mpt->m_num_phys, mpt->m_num_phys);
                                num_phys = mpt->m_num_phys;
                        } else if (num_phys < mpt->m_num_phys) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "reported by HBA SAS IO Unit Page 0 (%u) "
                                    "is less than that reported by the "
                                    "manufacturing information (%u). Driver "
                                    "will not attach. Please contact the "
                                    "firmware vendor about this.", num_phys,
                                    mpt->m_num_phys);
                                rval = DDI_FAILURE;
                                goto cleanup;
                        }
                        for (i = start_phy; i < num_phys; i++, start_phy = i) {
                                cpdi[i] = ddi_get32(page_accessp,
                                    &sasioupage0->PhyData[i].
                                    ControllerPhyDeviceInfo);
                                port_flags = ddi_get8(page_accessp,
                                    &sasioupage0->PhyData[i].PortFlags);

                                mpt->m_phy_info[i].port_num =
                                    ddi_get8(page_accessp,
                                    &sasioupage0->PhyData[i].Port);
                                mpt->m_phy_info[i].ctrl_devhdl =
                                    ddi_get16(page_accessp, &sasioupage0->
                                    PhyData[i].ControllerDevHandle);
                                mpt->m_phy_info[i].attached_devhdl =
                                    ddi_get16(page_accessp, &sasioupage0->
                                    PhyData[i].AttachedDevHandle);
                                mpt->m_phy_info[i].phy_device_type = cpdi[i];
                                mpt->m_phy_info[i].port_flags = port_flags;

                                if (port_flags & DISCOVERY_IN_PROGRESS) {
                                        retrypage0++;
                                        NDBG20(("Discovery in progress, can't "
                                            "verify IO unit config, then NO.%d"
                                            " times retry", retrypage0));
                                        break;
                                } else {
                                        retrypage0 = 0;
                                }
                                if (!(port_flags & AUTO_PORT_CONFIGURATION)) {
                                        /*
                                         * some PHY configuration described in
                                         * SAS IO Unit Page1
                                         */
                                        readpage1 = 1;
                                }
                        }

                        /*
                         * retry 30 times if discovery is in process
                         */
                        if (retrypage0 && (retrypage0 < 30)) {
                                drv_usecwait(1000 * 100);
                                state = IOUC_READ_PAGE0;
                                break;
                        } else if (retrypage0 == 30) {
                                mptsas_log(mpt, CE_WARN,
                                    "!Discovery in progress, can't "
                                    "verify IO unit config, then after"
                                    " 30 times retry, give up!");
                                state = IOUC_DONE;
                                rval = DDI_FAILURE;
                                break;
                        }

                        if (readpage1 == 0) {
                                state = IOUC_DONE;
                                rval = DDI_SUCCESS;
                                break;
                        }

                        state = IOUC_READ_PAGE1;
                        break;

                case IOUC_READ_PAGE1:
                        if ((ddi_dma_sync(page_dma_handle, 0, 0,
                            DDI_DMA_SYNC_FORCPU)) != DDI_SUCCESS) {
                                goto cleanup;
                        }

                        num_phys = ddi_get8(page_accessp,
                            &sasioupage1->NumPhys);
                        if (num_phys > MPTSAS_MAX_PHYS) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "supported by HBA (%d) is more than max "
                                    "supported by driver (%d).  Driver will "
                                    "not attach.", num_phys,
                                    MPTSAS_MAX_PHYS);
                                rval = DDI_FAILURE;
                                goto cleanup;
                        }
                        if (num_phys > mpt->m_num_phys) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "reported by HBA SAS IO Unit Page 1 (%u) "
                                    "is greater than that reported by the "
                                    "manufacturing information (%u). Limiting "
                                    "phy count to %u. Please contact the "
                                    "firmware vendor about this.", num_phys,
                                    mpt->m_num_phys, mpt->m_num_phys);
                                num_phys = mpt->m_num_phys;
                        } else if (num_phys < mpt->m_num_phys) {
                                mptsas_log(mpt, CE_WARN, "Number of phys "
                                    "reported by HBA SAS IO Unit Page 1 (%u) "
                                    "is less than that reported by the "
                                    "manufacturing information (%u). Driver "
                                    "will not attach. Please contact the "
                                    "firmware vendor about this.", num_phys,
                                    mpt->m_num_phys);
                                rval = DDI_FAILURE;
                                goto cleanup;
                        }
                        for (i = 0; i < num_phys; i++) {
                                cpdi[i] = ddi_get32(page_accessp,
                                    &sasioupage1->PhyData[i].
                                    ControllerPhyDeviceInfo);
                                port_flags = ddi_get8(page_accessp,
                                    &sasioupage1->PhyData[i].PortFlags);
                                mpt->m_phy_info[i].port_num =
                                    ddi_get8(page_accessp,
                                    &sasioupage1->PhyData[i].Port);
                                mpt->m_phy_info[i].port_flags = port_flags;
                                mpt->m_phy_info[i].phy_device_type = cpdi[i];

                        }

                        state = IOUC_DONE;
                        rval = DDI_SUCCESS;
                        break;
                }
        }
        if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) ||
            (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
                goto cleanup;
        }
        if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) ||
            (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
                goto cleanup;
        }

cleanup:
        if (free_recv)
                mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp);
        if (free_page)
                mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp);
        if (rval != DDI_SUCCESS) {
                mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE);
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST);
        }
        return (rval);
}

/*
 * mptsas_get_manufacture_page5
 *
 * This function will retrieve the base WWID from the adapter.  Since this
 * function is only called during the initialization process, use handshaking.
 */
int
mptsas_get_manufacture_page5(mptsas_t *mpt)
{
        ddi_dma_attr_t                  recv_dma_attrs, page_dma_attrs;
        ddi_dma_cookie_t                page_cookie;
        ddi_dma_handle_t                recv_dma_handle, page_dma_handle;
        ddi_acc_handle_t                recv_accessp, page_accessp;
        pMpi2ConfigReply_t              configreply;
        caddr_t                         recv_memp, page_memp;
        int                             recv_numbytes;
        pMpi2ManufacturingPage5_t       m5;
        uint32_t                        flagslength;
        int                             rval = DDI_SUCCESS;
        uint_t                          iocstatus;
        boolean_t               free_recv = B_FALSE, free_page = B_FALSE;

        MPTSAS_DISABLE_INTR(mpt);

        if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_HEADER,
            MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 5, 0, 0, 0, 0)) {
                rval = DDI_FAILURE;
                goto done;
        }

        /*
         * dynamically create a customized dma attribute structure
         * that describes the MPT's config reply page request structure.
         */
        recv_dma_attrs = mpt->m_msg_dma_attr;
        recv_dma_attrs.dma_attr_sgllen = 1;
        recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY));

        if (mptsas_dma_addr_create(mpt, recv_dma_attrs,
            &recv_dma_handle, &recv_accessp, &recv_memp,
            (sizeof (MPI2_CONFIG_REPLY)), NULL) == FALSE) {
                rval = DDI_FAILURE;
                goto done;
        }
        /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */
        free_recv = B_TRUE;

        bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY));
        configreply = (pMpi2ConfigReply_t)recv_memp;
        recv_numbytes = sizeof (MPI2_CONFIG_REPLY);

        /*
         * get config reply message
         */
        if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
            recv_accessp)) {
                rval = DDI_FAILURE;
                goto done;
        }

        if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) !=
            0) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 update: "
                    "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
                    ddi_get32(recv_accessp, &configreply->IOCLogInfo));
                goto done;
        }

        /*
         * dynamically create a customized dma attribute structure
         * that describes the MPT's config page structure.
         */
        page_dma_attrs = mpt->m_msg_dma_attr;
        page_dma_attrs.dma_attr_sgllen = 1;
        page_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_PAGE_MAN_5));

        if (mptsas_dma_addr_create(mpt, page_dma_attrs, &page_dma_handle,
            &page_accessp, &page_memp, (sizeof (MPI2_CONFIG_PAGE_MAN_5)),
            &page_cookie) == FALSE) {
                rval = DDI_FAILURE;
                goto done;
        }
        /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */
        free_page = B_TRUE;

        bzero(page_memp, sizeof (MPI2_CONFIG_PAGE_MAN_5));
        m5 = (pMpi2ManufacturingPage5_t)page_memp;
        NDBG20(("mptsas_get_manufacture_page5: paddr 0x%p",
            (void *)(uintptr_t)page_cookie.dmac_laddress));

        /*
         * Give reply address to IOC to store config page in and send
         * config request out.
         */

        flagslength = sizeof (MPI2_CONFIG_PAGE_MAN_5);
        flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT |
            MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
            MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
            MPI2_SGE_FLAGS_IOC_TO_HOST |
            MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);

        if (mptsas_send_config_request_msg(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 5,
            ddi_get8(recv_accessp, &configreply->Header.PageVersion),
            ddi_get8(recv_accessp, &configreply->Header.PageLength),
            flagslength, page_cookie.dmac_laddress)) {
                rval = DDI_FAILURE;
                goto done;
        }

        /*
         * get reply view handshake
         */
        if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
            recv_accessp)) {
                rval = DDI_FAILURE;
                goto done;
        }

        if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) !=
            0) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 config: "
                    "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
                    ddi_get32(recv_accessp, &configreply->IOCLogInfo));
                goto done;
        }

        (void) ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU);

        /*
         * Fusion-MPT stores fields in little-endian format.  This is
         * why the low-order 32 bits are stored first.
         */
        mpt->un.sasaddr.m_base_wwid_lo =
            ddi_get32(page_accessp, (uint32_t *)(void *)&m5->Phy[0].WWID);
        mpt->un.sasaddr.m_base_wwid_hi =
            ddi_get32(page_accessp, (uint32_t *)(void *)&m5->Phy[0].WWID + 1);

        if (ddi_prop_update_int64(DDI_DEV_T_NONE, mpt->m_dip,
            "base-wwid", mpt->un.m_base_wwid) != DDI_PROP_SUCCESS) {
                NDBG2(("%s%d: failed to create base-wwid property",
                    ddi_driver_name(mpt->m_dip), ddi_get_instance(mpt->m_dip)));
        }

        /*
         * Set the number of PHYs present.
         */
        mpt->m_num_phys = ddi_get8(page_accessp, (uint8_t *)&m5->NumPhys);

        if (ddi_prop_update_int(DDI_DEV_T_NONE, mpt->m_dip,
            "num-phys", mpt->m_num_phys) != DDI_PROP_SUCCESS) {
                NDBG2(("%s%d: failed to create num-phys property",
                    ddi_driver_name(mpt->m_dip), ddi_get_instance(mpt->m_dip)));
        }

        mptsas_log(mpt, CE_NOTE, "!mpt%d: Initiator WWNs: 0x%016llx-0x%016llx",
            mpt->m_instance, (unsigned long long)mpt->un.m_base_wwid,
            (unsigned long long)mpt->un.m_base_wwid + mpt->m_num_phys - 1);

        if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) ||
            (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
                goto done;
        }
        if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) ||
            (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
        }
done:
        /*
         * free up memory
         */
        if (free_recv)
                mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp);
        if (free_page)
                mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp);
        MPTSAS_ENABLE_INTR(mpt);

        return (rval);
}

static int
mptsas_sasphypage_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        pMpi2SasPhyPage0_t      sasphypage;
        int                     rval = DDI_SUCCESS;
        uint16_t                *owner_devhdl, *attached_devhdl;
        uint8_t                 *attached_phy_identify;
        uint32_t                *attached_phy_info;
        uint8_t                 *programmed_link_rate;
        uint8_t                 *hw_link_rate;
        uint8_t                 *change_count;
        uint32_t                *phy_info;
        uint8_t                 *negotiated_link_rate;
        uint32_t                page_address;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page0 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        page_address = va_arg(ap, uint32_t);
        /*
         * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there
         * are no more pages.  If everything is OK up to this point but the
         * status is INVALID_PAGE, change rval to FAILURE and quit.  Also,
         * signal that device traversal is complete.
         */
        if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
                if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) ==
                    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) {
                        mpt->m_done_traverse_smp = 1;
                }
                rval = DDI_FAILURE;
                return (rval);
        }
        owner_devhdl = va_arg(ap, uint16_t *);
        attached_devhdl = va_arg(ap, uint16_t *);
        attached_phy_identify = va_arg(ap, uint8_t *);
        attached_phy_info = va_arg(ap, uint32_t *);
        programmed_link_rate = va_arg(ap, uint8_t *);
        hw_link_rate = va_arg(ap, uint8_t *);
        change_count = va_arg(ap, uint8_t *);
        phy_info = va_arg(ap, uint32_t *);
        negotiated_link_rate = va_arg(ap, uint8_t *);

        sasphypage = (pMpi2SasPhyPage0_t)page_memp;

        *owner_devhdl =
            ddi_get16(accessp, &sasphypage->OwnerDevHandle);
        *attached_devhdl =
            ddi_get16(accessp, &sasphypage->AttachedDevHandle);
        *attached_phy_identify =
            ddi_get8(accessp, &sasphypage->AttachedPhyIdentifier);
        *attached_phy_info =
            ddi_get32(accessp, &sasphypage->AttachedPhyInfo);
        *programmed_link_rate =
            ddi_get8(accessp, &sasphypage->ProgrammedLinkRate);
        *hw_link_rate =
            ddi_get8(accessp, &sasphypage->HwLinkRate);
        *change_count =
            ddi_get8(accessp, &sasphypage->ChangeCount);
        *phy_info =
            ddi_get32(accessp, &sasphypage->PhyInfo);
        *negotiated_link_rate =
            ddi_get8(accessp, &sasphypage->NegotiatedLinkRate);

        return (rval);
}

/*
 * Request MPI configuration page SAS phy page 0 to get DevHandle, phymask
 * and SAS address.
 */
int
mptsas_get_sas_phy_page0(mptsas_t *mpt, uint32_t page_address,
    smhba_info_t *info)
{
        int                     rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_SAS_PHY, 0, page_address,
            mptsas_sasphypage_0_cb, page_address, &info->owner_devhdl,
            &info->attached_devhdl, &info->attached_phy_identify,
            &info->attached_phy_info, &info->programmed_link_rate,
            &info->hw_link_rate, &info->change_count,
            &info->phy_info, &info->negotiated_link_rate);

        return (rval);
}

static int
mptsas_sasphypage_1_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
#ifndef __lock_lint
        _NOTE(ARGUNUSED(ap))
#endif
        pMpi2SasPhyPage1_t      sasphypage;
        int                     rval = DDI_SUCCESS;

        uint32_t                *invalid_dword_count;
        uint32_t                *running_disparity_error_count;
        uint32_t                *loss_of_dword_sync_count;
        uint32_t                *phy_reset_problem_count;
        uint32_t                page_address;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page1 "
                    "config: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                rval = DDI_FAILURE;
                return (rval);
        }
        page_address = va_arg(ap, uint32_t);
        /*
         * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there
         * are no more pages.  If everything is OK up to this point but the
         * status is INVALID_PAGE, change rval to FAILURE and quit.  Also,
         * signal that device traversal is complete.
         */
        if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
                if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) ==
                    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) {
                        mpt->m_done_traverse_smp = 1;
                }
                rval = DDI_FAILURE;
                return (rval);
        }

        invalid_dword_count = va_arg(ap, uint32_t *);
        running_disparity_error_count = va_arg(ap, uint32_t *);
        loss_of_dword_sync_count = va_arg(ap, uint32_t *);
        phy_reset_problem_count = va_arg(ap, uint32_t *);

        sasphypage = (pMpi2SasPhyPage1_t)page_memp;

        *invalid_dword_count =
            ddi_get32(accessp, &sasphypage->InvalidDwordCount);
        *running_disparity_error_count =
            ddi_get32(accessp, &sasphypage->RunningDisparityErrorCount);
        *loss_of_dword_sync_count =
            ddi_get32(accessp, &sasphypage->LossDwordSynchCount);
        *phy_reset_problem_count =
            ddi_get32(accessp, &sasphypage->PhyResetProblemCount);

        return (rval);
}

/*
 * Request MPI configuration page SAS phy page 0 to get DevHandle, phymask
 * and SAS address.
 */
int
mptsas_get_sas_phy_page1(mptsas_t *mpt, uint32_t page_address,
    smhba_info_t *info)
{
        int                     rval = DDI_SUCCESS;

        ASSERT(mutex_owned(&mpt->m_mutex));

        /*
         * Get the header and config page.  reply contains the reply frame,
         * which holds status info for the request.
         */
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_SAS_PHY, 1, page_address,
            mptsas_sasphypage_1_cb, page_address,
            &info->invalid_dword_count,
            &info->running_disparity_error_count,
            &info->loss_of_dword_sync_count,
            &info->phy_reset_problem_count);

        return (rval);
}
/*
 * mptsas_get_manufacture_page0
 *
 * This function will retrieve the base
 * Chip name, Board Name,Board Trace number from the adapter.
 * Since this function is only called during the
 * initialization process, use handshaking.
 */
int
mptsas_get_manufacture_page0(mptsas_t *mpt)
{
        ddi_dma_attr_t                  recv_dma_attrs, page_dma_attrs;
        ddi_dma_cookie_t                page_cookie;
        ddi_dma_handle_t                recv_dma_handle, page_dma_handle;
        ddi_acc_handle_t                recv_accessp, page_accessp;
        pMpi2ConfigReply_t              configreply;
        caddr_t                         recv_memp, page_memp;
        int                             recv_numbytes;
        pMpi2ManufacturingPage0_t       m0;
        uint32_t                        flagslength;
        int                             rval = DDI_SUCCESS;
        uint_t                          iocstatus;
        uint8_t                         i = 0;
        boolean_t               free_recv = B_FALSE, free_page = B_FALSE;

        MPTSAS_DISABLE_INTR(mpt);

        if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_HEADER,
            MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 0, 0, 0, 0, 0)) {
                rval = DDI_FAILURE;
                goto done;
        }

        /*
         * dynamically create a customized dma attribute structure
         * that describes the MPT's config reply page request structure.
         */
        recv_dma_attrs = mpt->m_msg_dma_attr;
        recv_dma_attrs.dma_attr_sgllen = 1;
        recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY));

        if (mptsas_dma_addr_create(mpt, recv_dma_attrs, &recv_dma_handle,
            &recv_accessp, &recv_memp, (sizeof (MPI2_CONFIG_REPLY)),
            NULL) == FALSE) {
                rval = DDI_FAILURE;
                goto done;
        }
        /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */
        free_recv = B_TRUE;

        bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY));
        configreply = (pMpi2ConfigReply_t)recv_memp;
        recv_numbytes = sizeof (MPI2_CONFIG_REPLY);

        /*
         * get config reply message
         */
        if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
            recv_accessp)) {
                rval = DDI_FAILURE;
                goto done;
        }

        if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) !=
            0) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 update: "
                    "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
                    ddi_get32(recv_accessp, &configreply->IOCLogInfo));
                goto done;
        }

        /*
         * dynamically create a customized dma attribute structure
         * that describes the MPT's config page structure.
         */
        page_dma_attrs = mpt->m_msg_dma_attr;
        page_dma_attrs.dma_attr_sgllen = 1;
        page_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_PAGE_MAN_0));

        if (mptsas_dma_addr_create(mpt, page_dma_attrs, &page_dma_handle,
            &page_accessp, &page_memp, (sizeof (MPI2_CONFIG_PAGE_MAN_0)),
            &page_cookie) == FALSE) {
                rval = DDI_FAILURE;
                goto done;
        }
        /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */
        free_page = B_TRUE;

        bzero(page_memp, sizeof (MPI2_CONFIG_PAGE_MAN_0));
        m0 = (pMpi2ManufacturingPage0_t)page_memp;

        /*
         * Give reply address to IOC to store config page in and send
         * config request out.
         */

        flagslength = sizeof (MPI2_CONFIG_PAGE_MAN_0);
        flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT |
            MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
            MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
            MPI2_SGE_FLAGS_IOC_TO_HOST |
            MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);

        if (mptsas_send_config_request_msg(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 0,
            ddi_get8(recv_accessp, &configreply->Header.PageVersion),
            ddi_get8(recv_accessp, &configreply->Header.PageLength),
            flagslength, page_cookie.dmac_laddress)) {
                rval = DDI_FAILURE;
                goto done;
        }

        /*
         * get reply view handshake
         */
        if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes,
            recv_accessp)) {
                rval = DDI_FAILURE;
                goto done;
        }

        if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) !=
            0) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page0 config: "
                    "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
                    ddi_get32(recv_accessp, &configreply->IOCLogInfo));
                goto done;
        }

        (void) ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU);

        /*
         * Fusion-MPT stores fields in little-endian format.  This is
         * why the low-order 32 bits are stored first.
         */

        for (i = 0; i < 16; i++) {
                mpt->m_MANU_page0.ChipName[i] =
                    ddi_get8(page_accessp,
                    (uint8_t *)(void *)&m0->ChipName[i]);
        }

        for (i = 0; i < 8; i++) {
                mpt->m_MANU_page0.ChipRevision[i] =
                    ddi_get8(page_accessp,
                    (uint8_t *)(void *)&m0->ChipRevision[i]);
        }

        for (i = 0; i < 16; i++) {
                mpt->m_MANU_page0.BoardName[i] =
                    ddi_get8(page_accessp,
                    (uint8_t *)(void *)&m0->BoardName[i]);
        }

        for (i = 0; i < 16; i++) {
                mpt->m_MANU_page0.BoardAssembly[i] =
                    ddi_get8(page_accessp,
                    (uint8_t *)(void *)&m0->BoardAssembly[i]);
        }

        for (i = 0; i < 16; i++) {
                mpt->m_MANU_page0.BoardTracerNumber[i] =
                    ddi_get8(page_accessp,
                    (uint8_t *)(void *)&m0->BoardTracerNumber[i]);
        }

        if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) ||
            (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
                goto done;
        }
        if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) ||
            (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) {
                ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED);
                rval = DDI_FAILURE;
        }
done:
        /*
         * free up memory
         */
        if (free_recv)
                mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp);
        if (free_page)
                mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp);
        MPTSAS_ENABLE_INTR(mpt);

        return (rval);
}

static int
mptsas_enclosurepage_0_cb(mptsas_t *mpt, caddr_t page_memp,
    ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo,
    va_list ap)
{
        uint32_t                        page_address;
        pMpi2SasEnclosurePage0_t        encpage, encout;

        if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) &&
            (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) {
                mptsas_log(mpt, CE_WARN, "mptsas_get_enclsourepage0 "
                    "header: IOCStatus=0x%x, IOCLogInfo=0x%x",
                    iocstatus, iocloginfo);
                return (DDI_FAILURE);
        }

        page_address = va_arg(ap, uint32_t);
        encout = va_arg(ap, pMpi2SasEnclosurePage0_t);
        encpage = (pMpi2SasEnclosurePage0_t)page_memp;

        /*
         * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there
         * are no more pages.  If everything is OK up to this point but the
         * status is INVALID_PAGE, change rval to FAILURE and quit.  Also,
         * signal that enclosure traversal is complete.
         */
        if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
                if ((page_address & MPI2_SAS_DEVICE_PGAD_FORM_MASK) ==
                    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) {
                        mpt->m_done_traverse_enc = 1;
                }
                return (DDI_FAILURE);
        }

        encout->Header.PageVersion = ddi_get8(accessp,
            &encpage->Header.PageVersion);
        encout->Header.PageNumber = ddi_get8(accessp,
            &encpage->Header.PageNumber);
        encout->Header.PageType = ddi_get8(accessp, &encpage->Header.PageType);
        encout->Header.ExtPageLength = ddi_get16(accessp,
            &encpage->Header.ExtPageLength);
        encout->Header.ExtPageType = ddi_get8(accessp,
            &encpage->Header.ExtPageType);

        encout->EnclosureLogicalID.Low = ddi_get32(accessp,
            &encpage->EnclosureLogicalID.Low);
        encout->EnclosureLogicalID.High = ddi_get32(accessp,
            &encpage->EnclosureLogicalID.High);
        encout->Flags = ddi_get16(accessp, &encpage->Flags);
        encout->EnclosureHandle = ddi_get16(accessp, &encpage->EnclosureHandle);
        encout->NumSlots = ddi_get16(accessp, &encpage->NumSlots);
        encout->StartSlot = ddi_get16(accessp, &encpage->StartSlot);
        encout->EnclosureLevel = ddi_get8(accessp, &encpage->EnclosureLevel);
        encout->SEPDevHandle = ddi_get16(accessp, &encpage->SEPDevHandle);

        return (DDI_SUCCESS);
}

/*
 * Request information about the SES enclosures.
 */
int
mptsas_get_enclosure_page0(mptsas_t *mpt, uint32_t page_address,
    mptsas_enclosure_t *mep)
{
        int rval = DDI_SUCCESS;
        Mpi2SasEnclosurePage0_t encpage;

        ASSERT(MUTEX_HELD(&mpt->m_mutex));

        bzero(&encpage, sizeof (encpage));
        rval = mptsas_access_config_page(mpt,
            MPI2_CONFIG_ACTION_PAGE_READ_CURRENT,
            MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE, 0, page_address,
            mptsas_enclosurepage_0_cb, page_address, &encpage);

        if (rval == DDI_SUCCESS) {
                mep->me_enchdl = encpage.EnclosureHandle;
                mep->me_flags = encpage.Flags;
                mep->me_nslots = encpage.NumSlots;
                mep->me_fslot = encpage.StartSlot;
                mep->me_slotleds = NULL;
        }

        return (rval);
}